blob: d27d0cdf593ddbcb25a411ccd12c36e8d7c65f06 [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 int get_maxbacktrace_level(void);
72static void do_setdebugtracelevel(char_u *arg);
73static void do_checkbacktracelevel(void);
74static void do_showbacktrace(char_u *cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +000075
Bram Moolenaarc6f9f732018-02-11 19:06:26 +010076static char_u *debug_oldval = NULL; /* old and newval for debug expressions */
77static char_u *debug_newval = NULL;
78static int debug_expr = 0; /* use debug_expr */
79
80 int
81has_watchexpr(void)
82{
83 return debug_expr;
84}
85
Bram Moolenaar071d4272004-06-13 20:20:40 +000086/*
87 * do_debug(): Debug mode.
88 * Repeatedly get Ex commands, until told to continue normal execution.
89 */
90 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +010091do_debug(char_u *cmd)
Bram Moolenaar071d4272004-06-13 20:20:40 +000092{
93 int save_msg_scroll = msg_scroll;
94 int save_State = State;
95 int save_did_emsg = did_emsg;
96 int save_cmd_silent = cmd_silent;
97 int save_msg_silent = msg_silent;
98 int save_emsg_silent = emsg_silent;
99 int save_redir_off = redir_off;
100 tasave_T typeaheadbuf;
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000101 int typeahead_saved = FALSE;
Bram Moolenaar383c6f52008-01-04 15:01:07 +0000102 int save_ignore_script = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000103 int save_ex_normal_busy;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104 int n;
105 char_u *cmdline = NULL;
106 char_u *p;
107 char *tail = NULL;
108 static int last_cmd = 0;
109#define CMD_CONT 1
110#define CMD_NEXT 2
111#define CMD_STEP 3
112#define CMD_FINISH 4
113#define CMD_QUIT 5
114#define CMD_INTERRUPT 6
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100115#define CMD_BACKTRACE 7
116#define CMD_FRAME 8
117#define CMD_UP 9
118#define CMD_DOWN 10
Bram Moolenaar071d4272004-06-13 20:20:40 +0000119
120#ifdef ALWAYS_USE_GUI
121 /* Can't do this when there is no terminal for input/output. */
122 if (!gui.in_use)
123 {
124 /* Break as soon as possible. */
125 debug_break_level = 9999;
126 return;
127 }
128#endif
129
130 /* Make sure we are in raw mode and start termcap mode. Might have side
131 * effects... */
132 settmode(TMODE_RAW);
133 starttermcap();
134
135 ++RedrawingDisabled; /* don't redisplay the window */
136 ++no_wait_return; /* don't wait for return */
137 did_emsg = FALSE; /* don't use error from debugged stuff */
138 cmd_silent = FALSE; /* display commands */
139 msg_silent = FALSE; /* display messages */
140 emsg_silent = FALSE; /* display error messages */
141 redir_off = TRUE; /* don't redirect debug commands */
142
143 State = NORMAL;
Bram Moolenaard99388b2017-10-26 14:28:32 +0200144 debug_mode = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145
146 if (!debug_did_msg)
147 MSG(_("Entering Debug mode. Type \"cont\" to continue."));
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100148 if (debug_oldval != NULL)
149 {
150 smsg((char_u *)_("Oldval = \"%s\""), debug_oldval);
151 vim_free(debug_oldval);
152 debug_oldval = NULL;
153 }
154 if (debug_newval != NULL)
155 {
156 smsg((char_u *)_("Newval = \"%s\""), debug_newval);
157 vim_free(debug_newval);
158 debug_newval = NULL;
159 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160 if (sourcing_name != NULL)
161 msg(sourcing_name);
162 if (sourcing_lnum != 0)
Bram Moolenaar555b2802005-05-19 21:08:39 +0000163 smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 else
Bram Moolenaar555b2802005-05-19 21:08:39 +0000165 smsg((char_u *)_("cmd: %s"), cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000166 /*
167 * Repeat getting a command and executing it.
168 */
169 for (;;)
170 {
171 msg_scroll = TRUE;
172 need_wait_return = FALSE;
Bram Moolenaar85b11762016-02-27 18:13:23 +0100173
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174 /* Save the current typeahead buffer and replace it with an empty one.
175 * This makes sure we get input from the user here and don't interfere
176 * with the commands being executed. Reset "ex_normal_busy" to avoid
177 * the side effects of using ":normal". Save the stuff buffer and make
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000178 * it empty. Set ignore_script to avoid reading from script input. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179 save_ex_normal_busy = ex_normal_busy;
180 ex_normal_busy = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000181 if (!debug_greedy)
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000182 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000183 save_typeahead(&typeaheadbuf);
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000184 typeahead_saved = TRUE;
185 save_ignore_script = ignore_script;
186 ignore_script = TRUE;
187 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188
Bram Moolenaardc303bc2016-05-17 17:45:38 +0200189 vim_free(cmdline);
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000190 cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000191
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000192 if (typeahead_saved)
193 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000194 restore_typeahead(&typeaheadbuf);
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000195 ignore_script = save_ignore_script;
196 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197 ex_normal_busy = save_ex_normal_busy;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198
199 cmdline_row = msg_row;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100200 msg_starthere();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000201 if (cmdline != NULL)
202 {
203 /* If this is a debug command, set "last_cmd".
204 * If not, reset "last_cmd".
205 * For a blank line use previous command. */
206 p = skipwhite(cmdline);
207 if (*p != NUL)
208 {
209 switch (*p)
210 {
211 case 'c': last_cmd = CMD_CONT;
212 tail = "ont";
213 break;
214 case 'n': last_cmd = CMD_NEXT;
215 tail = "ext";
216 break;
217 case 's': last_cmd = CMD_STEP;
218 tail = "tep";
219 break;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100220 case 'f':
221 last_cmd = 0;
222 if (p[1] == 'r')
223 {
224 last_cmd = CMD_FRAME;
225 tail = "rame";
226 }
227 else
228 {
229 last_cmd = CMD_FINISH;
230 tail = "inish";
231 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 break;
233 case 'q': last_cmd = CMD_QUIT;
234 tail = "uit";
235 break;
236 case 'i': last_cmd = CMD_INTERRUPT;
237 tail = "nterrupt";
238 break;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100239 case 'b': last_cmd = CMD_BACKTRACE;
240 if (p[1] == 't')
241 tail = "t";
242 else
243 tail = "acktrace";
244 break;
245 case 'w': last_cmd = CMD_BACKTRACE;
246 tail = "here";
247 break;
248 case 'u': last_cmd = CMD_UP;
249 tail = "p";
250 break;
251 case 'd': last_cmd = CMD_DOWN;
252 tail = "own";
253 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000254 default: last_cmd = 0;
255 }
256 if (last_cmd != 0)
257 {
258 /* Check that the tail matches. */
259 ++p;
260 while (*p != NUL && *p == *tail)
261 {
262 ++p;
263 ++tail;
264 }
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100265 if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000266 last_cmd = 0;
267 }
268 }
269
270 if (last_cmd != 0)
271 {
272 /* Execute debug command: decided where to break next and
273 * return. */
274 switch (last_cmd)
275 {
276 case CMD_CONT:
277 debug_break_level = -1;
278 break;
279 case CMD_NEXT:
280 debug_break_level = ex_nesting_level;
281 break;
282 case CMD_STEP:
283 debug_break_level = 9999;
284 break;
285 case CMD_FINISH:
286 debug_break_level = ex_nesting_level - 1;
287 break;
288 case CMD_QUIT:
289 got_int = TRUE;
290 debug_break_level = -1;
291 break;
292 case CMD_INTERRUPT:
293 got_int = TRUE;
294 debug_break_level = 9999;
295 /* Do not repeat ">interrupt" cmd, continue stepping. */
296 last_cmd = CMD_STEP;
297 break;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100298 case CMD_BACKTRACE:
299 do_showbacktrace(cmd);
300 continue;
301 case CMD_FRAME:
302 if (*p == NUL)
303 {
304 do_showbacktrace(cmd);
305 }
306 else
307 {
308 p = skipwhite(p);
309 do_setdebugtracelevel(p);
310 }
311 continue;
312 case CMD_UP:
313 debug_backtrace_level++;
314 do_checkbacktracelevel();
315 continue;
316 case CMD_DOWN:
317 debug_backtrace_level--;
318 do_checkbacktracelevel();
319 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000320 }
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100321 /* Going out reset backtrace_level */
322 debug_backtrace_level = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000323 break;
324 }
325
326 /* don't debug this command */
327 n = debug_break_level;
328 debug_break_level = -1;
329 (void)do_cmdline(cmdline, getexline, NULL,
330 DOCMD_VERBOSE|DOCMD_EXCRESET);
331 debug_break_level = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000332 }
333 lines_left = Rows - 1;
334 }
335 vim_free(cmdline);
336
337 --RedrawingDisabled;
338 --no_wait_return;
339 redraw_all_later(NOT_VALID);
340 need_wait_return = FALSE;
341 msg_scroll = save_msg_scroll;
342 lines_left = Rows - 1;
343 State = save_State;
Bram Moolenaard99388b2017-10-26 14:28:32 +0200344 debug_mode = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345 did_emsg = save_did_emsg;
346 cmd_silent = save_cmd_silent;
347 msg_silent = save_msg_silent;
348 emsg_silent = save_emsg_silent;
349 redir_off = save_redir_off;
350
351 /* Only print the message again when typing a command before coming back
352 * here. */
353 debug_did_msg = TRUE;
354}
355
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100356 static int
357get_maxbacktrace_level(void)
358{
359 char *p, *q;
Bram Moolenaardc633cf2016-04-23 14:33:19 +0200360 int maxbacktrace = 0;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100361
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100362 if (sourcing_name != NULL)
363 {
364 p = (char *)sourcing_name;
365 while ((q = strstr(p, "..")) != NULL)
366 {
367 p = q + 2;
368 maxbacktrace++;
369 }
370 }
371 return maxbacktrace;
372}
373
374 static void
375do_setdebugtracelevel(char_u *arg)
376{
377 int level;
378
379 level = atoi((char *)arg);
380 if (*arg == '+' || level < 0)
381 debug_backtrace_level += level;
382 else
383 debug_backtrace_level = level;
384
385 do_checkbacktracelevel();
386}
387
388 static void
389do_checkbacktracelevel(void)
390{
391 if (debug_backtrace_level < 0)
392 {
393 debug_backtrace_level = 0;
394 MSG(_("frame is zero"));
395 }
396 else
397 {
398 int max = get_maxbacktrace_level();
399
400 if (debug_backtrace_level > max)
401 {
402 debug_backtrace_level = max;
403 smsg((char_u *)_("frame at highest level: %d"), max);
404 }
405 }
406}
407
408 static void
409do_showbacktrace(char_u *cmd)
410{
411 char *cur;
412 char *next;
413 int i = 0;
414 int max = get_maxbacktrace_level();
415
416 if (sourcing_name != NULL)
417 {
418 cur = (char *)sourcing_name;
419 while (!got_int)
420 {
421 next = strstr(cur, "..");
422 if (next != NULL)
423 *next = NUL;
424 if (i == max - debug_backtrace_level)
425 smsg((char_u *)"->%d %s", max - i, cur);
426 else
427 smsg((char_u *)" %d %s", max - i, cur);
428 ++i;
429 if (next == NULL)
430 break;
431 *next = '.';
432 cur = next + 2;
433 }
434 }
435 if (sourcing_lnum != 0)
436 smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, cmd);
437 else
438 smsg((char_u *)_("cmd: %s"), cmd);
439}
440
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441/*
442 * ":debug".
443 */
444 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100445ex_debug(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000446{
447 int debug_break_level_save = debug_break_level;
448
449 debug_break_level = 9999;
450 do_cmdline_cmd(eap->arg);
451 debug_break_level = debug_break_level_save;
452}
453
454static char_u *debug_breakpoint_name = NULL;
455static linenr_T debug_breakpoint_lnum;
456
457/*
458 * When debugging or a breakpoint is set on a skipped command, no debug prompt
459 * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and
460 * debug_skipped_name is then set to the source name in the breakpoint case. If
461 * a skipped command decides itself that a debug prompt should be displayed, it
462 * can do so by calling dbg_check_skipped().
463 */
464static int debug_skipped;
465static char_u *debug_skipped_name;
466
467/*
468 * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
469 * at or below the break level. But only when the line is actually
470 * executed. Return TRUE and set breakpoint_name for skipped commands that
471 * decide to execute something themselves.
472 * Called from do_one_cmd() before executing a command.
473 */
474 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100475dbg_check_breakpoint(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476{
477 char_u *p;
478
479 debug_skipped = FALSE;
480 if (debug_breakpoint_name != NULL)
481 {
482 if (!eap->skip)
483 {
484 /* replace K_SNR with "<SNR>" */
485 if (debug_breakpoint_name[0] == K_SPECIAL
486 && debug_breakpoint_name[1] == KS_EXTRA
487 && debug_breakpoint_name[2] == (int)KE_SNR)
488 p = (char_u *)"<SNR>";
489 else
490 p = (char_u *)"";
Bram Moolenaar555b2802005-05-19 21:08:39 +0000491 smsg((char_u *)_("Breakpoint in \"%s%s\" line %ld"),
492 p,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493 debug_breakpoint_name + (*p == NUL ? 0 : 3),
494 (long)debug_breakpoint_lnum);
495 debug_breakpoint_name = NULL;
496 do_debug(eap->cmd);
497 }
498 else
499 {
500 debug_skipped = TRUE;
501 debug_skipped_name = debug_breakpoint_name;
502 debug_breakpoint_name = NULL;
503 }
504 }
505 else if (ex_nesting_level <= debug_break_level)
506 {
507 if (!eap->skip)
508 do_debug(eap->cmd);
509 else
510 {
511 debug_skipped = TRUE;
512 debug_skipped_name = NULL;
513 }
514 }
515}
516
517/*
518 * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was
519 * set. Return TRUE when the debug mode is entered this time.
520 */
521 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100522dbg_check_skipped(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523{
524 int prev_got_int;
525
526 if (debug_skipped)
527 {
528 /*
529 * Save the value of got_int and reset it. We don't want a previous
530 * interruption cause flushing the input buffer.
531 */
532 prev_got_int = got_int;
533 got_int = FALSE;
534 debug_breakpoint_name = debug_skipped_name;
535 /* eap->skip is TRUE */
536 eap->skip = FALSE;
537 (void)dbg_check_breakpoint(eap);
538 eap->skip = TRUE;
539 got_int |= prev_got_int;
540 return TRUE;
541 }
542 return FALSE;
543}
544
545/*
546 * The list of breakpoints: dbg_breakp.
547 * This is a grow-array of structs.
548 */
549struct debuggy
550{
551 int dbg_nr; /* breakpoint number */
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100552 int dbg_type; /* DBG_FUNC, DBG_FILE or DBG_EXPR */
553 char_u *dbg_name; /* function, expression or file name */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000554 regprog_T *dbg_prog; /* regexp program */
555 linenr_T dbg_lnum; /* line number in function or file */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000556 int dbg_forceit; /* ! used */
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100557#ifdef FEAT_EVAL
558 typval_T *dbg_val; /* last result of watchexpression */
559#endif
560 int dbg_level; /* stored nested level for expr */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561};
562
563static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL};
Bram Moolenaar05159a02005-02-26 23:04:13 +0000564#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx])
565#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566static int last_breakp = 0; /* nr of last defined breakpoint */
567
Bram Moolenaar05159a02005-02-26 23:04:13 +0000568#ifdef FEAT_PROFILE
569/* Profiling uses file and func names similar to breakpoints. */
570static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL};
571#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572#define DBG_FUNC 1
573#define DBG_FILE 2
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100574#define DBG_EXPR 3
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575
Bram Moolenaarf28dbce2016-01-29 22:03:47 +0100576static int dbg_parsearg(char_u *arg, garray_T *gap);
577static linenr_T debuggy_find(int file,char_u *fname, linenr_T after, garray_T *gap, int *fp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000578
579/*
Bram Moolenaar05159a02005-02-26 23:04:13 +0000580 * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
581 * in the entry just after the last one in dbg_breakp. Note that "dbg_name"
582 * is allocated.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 * Returns FAIL for failure.
584 */
585 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100586dbg_parsearg(
587 char_u *arg,
588 garray_T *gap) /* either &dbg_breakp or &prof_ga */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000589{
590 char_u *p = arg;
591 char_u *q;
592 struct debuggy *bp;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000593 int here = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594
Bram Moolenaar05159a02005-02-26 23:04:13 +0000595 if (ga_grow(gap, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 return FAIL;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000597 bp = &DEBUGGY(gap, gap->ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598
599 /* Find "func" or "file". */
600 if (STRNCMP(p, "func", 4) == 0)
601 bp->dbg_type = DBG_FUNC;
602 else if (STRNCMP(p, "file", 4) == 0)
603 bp->dbg_type = DBG_FILE;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000604 else if (
605#ifdef FEAT_PROFILE
606 gap != &prof_ga &&
607#endif
608 STRNCMP(p, "here", 4) == 0)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000609 {
610 if (curbuf->b_ffname == NULL)
611 {
612 EMSG(_(e_noname));
613 return FAIL;
614 }
615 bp->dbg_type = DBG_FILE;
616 here = TRUE;
617 }
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100618 else if (
619#ifdef FEAT_PROFILE
620 gap != &prof_ga &&
621#endif
622 STRNCMP(p, "expr", 4) == 0)
623 bp->dbg_type = DBG_EXPR;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000624 else
625 {
626 EMSG2(_(e_invarg2), p);
627 return FAIL;
628 }
629 p = skipwhite(p + 4);
630
631 /* Find optional line number. */
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000632 if (here)
633 bp->dbg_lnum = curwin->w_cursor.lnum;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000634 else if (
635#ifdef FEAT_PROFILE
636 gap != &prof_ga &&
637#endif
638 VIM_ISDIGIT(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000639 {
640 bp->dbg_lnum = getdigits(&p);
641 p = skipwhite(p);
642 }
643 else
644 bp->dbg_lnum = 0;
645
646 /* Find the function or file name. Don't accept a function name with (). */
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000647 if ((!here && *p == NUL)
648 || (here && *p != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000649 || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL))
650 {
651 EMSG2(_(e_invarg2), arg);
652 return FAIL;
653 }
654
655 if (bp->dbg_type == DBG_FUNC)
656 bp->dbg_name = vim_strsave(p);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000657 else if (here)
658 bp->dbg_name = vim_strsave(curbuf->b_ffname);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100659 else if (bp->dbg_type == DBG_EXPR)
660 {
661 bp->dbg_name = vim_strsave(p);
662 if (bp->dbg_name != NULL)
663 bp->dbg_val = eval_expr(bp->dbg_name, NULL);
664 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000665 else
666 {
667 /* Expand the file name in the same way as do_source(). This means
668 * doing it twice, so that $DIR/file gets expanded when $DIR is
669 * "~/dir". */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670 q = expand_env_save(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 if (q == NULL)
672 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000673 p = expand_env_save(q);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000674 vim_free(q);
675 if (p == NULL)
676 return FAIL;
Bram Moolenaar843ee412004-06-30 16:16:41 +0000677 if (*p != '*')
678 {
679 bp->dbg_name = fix_fname(p);
680 vim_free(p);
681 }
682 else
683 bp->dbg_name = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684 }
685
686 if (bp->dbg_name == NULL)
687 return FAIL;
688 return OK;
689}
690
691/*
692 * ":breakadd".
693 */
694 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100695ex_breakadd(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696{
697 struct debuggy *bp;
698 char_u *pat;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000699 garray_T *gap;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000700
Bram Moolenaar05159a02005-02-26 23:04:13 +0000701 gap = &dbg_breakp;
702#ifdef FEAT_PROFILE
703 if (eap->cmdidx == CMD_profile)
704 gap = &prof_ga;
705#endif
706
707 if (dbg_parsearg(eap->arg, gap) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708 {
Bram Moolenaar05159a02005-02-26 23:04:13 +0000709 bp = &DEBUGGY(gap, gap->ga_len);
710 bp->dbg_forceit = eap->forceit;
711
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100712 if (bp->dbg_type != DBG_EXPR)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713 {
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100714 pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE);
715 if (pat != NULL)
716 {
717 bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
718 vim_free(pat);
719 }
720 if (pat == NULL || bp->dbg_prog == NULL)
721 vim_free(bp->dbg_name);
722 else
723 {
724 if (bp->dbg_lnum == 0) /* default line number is 1 */
725 bp->dbg_lnum = 1;
726#ifdef FEAT_PROFILE
727 if (eap->cmdidx != CMD_profile)
728#endif
729 {
730 DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
731 ++debug_tick;
732 }
733 ++gap->ga_len;
734 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 else
737 {
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100738 /* DBG_EXPR */
739 DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
740 ++debug_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741 }
742 }
743}
744
745/*
746 * ":debuggreedy".
747 */
748 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100749ex_debuggreedy(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750{
751 if (eap->addr_count == 0 || eap->line2 != 0)
752 debug_greedy = TRUE;
753 else
754 debug_greedy = FALSE;
755}
756
757/*
Bram Moolenaard9fba312005-06-26 22:34:35 +0000758 * ":breakdel" and ":profdel".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759 */
760 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100761ex_breakdel(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762{
763 struct debuggy *bp, *bpi;
764 int nr;
765 int todel = -1;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000766 int del_all = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 int i;
768 linenr_T best_lnum = 0;
Bram Moolenaard9fba312005-06-26 22:34:35 +0000769 garray_T *gap;
770
771 gap = &dbg_breakp;
Bram Moolenaard9fba312005-06-26 22:34:35 +0000772 if (eap->cmdidx == CMD_profdel)
Bram Moolenaar38bdbd62012-06-20 15:48:57 +0200773 {
774#ifdef FEAT_PROFILE
Bram Moolenaard9fba312005-06-26 22:34:35 +0000775 gap = &prof_ga;
Bram Moolenaar38bdbd62012-06-20 15:48:57 +0200776#else
777 ex_ni(eap);
778 return;
Bram Moolenaard9fba312005-06-26 22:34:35 +0000779#endif
Bram Moolenaar38bdbd62012-06-20 15:48:57 +0200780 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781
782 if (vim_isdigit(*eap->arg))
783 {
784 /* ":breakdel {nr}" */
785 nr = atol((char *)eap->arg);
Bram Moolenaard9fba312005-06-26 22:34:35 +0000786 for (i = 0; i < gap->ga_len; ++i)
787 if (DEBUGGY(gap, i).dbg_nr == nr)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000788 {
789 todel = i;
790 break;
791 }
792 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000793 else if (*eap->arg == '*')
794 {
795 todel = 0;
796 del_all = TRUE;
797 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000798 else
799 {
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100800 /* ":breakdel {func|file|expr} [lnum] {name}" */
Bram Moolenaard9fba312005-06-26 22:34:35 +0000801 if (dbg_parsearg(eap->arg, gap) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000802 return;
Bram Moolenaard9fba312005-06-26 22:34:35 +0000803 bp = &DEBUGGY(gap, gap->ga_len);
804 for (i = 0; i < gap->ga_len; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000805 {
Bram Moolenaard9fba312005-06-26 22:34:35 +0000806 bpi = &DEBUGGY(gap, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000807 if (bp->dbg_type == bpi->dbg_type
808 && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
809 && (bp->dbg_lnum == bpi->dbg_lnum
810 || (bp->dbg_lnum == 0
811 && (best_lnum == 0
812 || bpi->dbg_lnum < best_lnum))))
813 {
814 todel = i;
815 best_lnum = bpi->dbg_lnum;
816 }
817 }
818 vim_free(bp->dbg_name);
819 }
820
821 if (todel < 0)
822 EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
823 else
Bram Moolenaard9fba312005-06-26 22:34:35 +0000824 {
825 while (gap->ga_len > 0)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000826 {
Bram Moolenaard9fba312005-06-26 22:34:35 +0000827 vim_free(DEBUGGY(gap, todel).dbg_name);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100828#ifdef FEAT_EVAL
829 if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
830 && DEBUGGY(gap, todel).dbg_val != NULL)
831 free_tv(DEBUGGY(gap, todel).dbg_val);
832#endif
Bram Moolenaar473de612013-06-08 18:19:48 +0200833 vim_regfree(DEBUGGY(gap, todel).dbg_prog);
Bram Moolenaard9fba312005-06-26 22:34:35 +0000834 --gap->ga_len;
835 if (todel < gap->ga_len)
836 mch_memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
837 (gap->ga_len - todel) * sizeof(struct debuggy));
838#ifdef FEAT_PROFILE
839 if (eap->cmdidx == CMD_breakdel)
840#endif
841 ++debug_tick;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000842 if (!del_all)
843 break;
844 }
Bram Moolenaard9fba312005-06-26 22:34:35 +0000845
846 /* If all breakpoints were removed clear the array. */
847 if (gap->ga_len == 0)
848 ga_clear(gap);
849 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000850}
851
852/*
853 * ":breaklist".
854 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000855 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100856ex_breaklist(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000857{
858 struct debuggy *bp;
859 int i;
860
861 if (dbg_breakp.ga_len == 0)
862 MSG(_("No breakpoints defined"));
863 else
864 for (i = 0; i < dbg_breakp.ga_len; ++i)
865 {
866 bp = &BREAKP(i);
Bram Moolenaard58ea072011-06-26 04:25:30 +0200867 if (bp->dbg_type == DBG_FILE)
868 home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100869 if (bp->dbg_type != DBG_EXPR)
870 smsg((char_u *)_("%3d %s %s line %ld"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000871 bp->dbg_nr,
872 bp->dbg_type == DBG_FUNC ? "func" : "file",
Bram Moolenaard58ea072011-06-26 04:25:30 +0200873 bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874 (long)bp->dbg_lnum);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100875 else
876 smsg((char_u *)_("%3d expr %s"),
877 bp->dbg_nr, bp->dbg_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878 }
879}
880
881/*
882 * Find a breakpoint for a function or sourced file.
883 * Returns line number at which to break; zero when no matching breakpoint.
884 */
885 linenr_T
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100886dbg_find_breakpoint(
887 int file, /* TRUE for a file, FALSE for a function */
888 char_u *fname, /* file or function name */
889 linenr_T after) /* after this line number */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000890{
Bram Moolenaar05159a02005-02-26 23:04:13 +0000891 return debuggy_find(file, fname, after, &dbg_breakp, NULL);
892}
893
894#if defined(FEAT_PROFILE) || defined(PROTO)
895/*
896 * Return TRUE if profiling is on for a function or sourced file.
897 */
898 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100899has_profiling(
900 int file, /* TRUE for a file, FALSE for a function */
901 char_u *fname, /* file or function name */
902 int *fp) /* return: forceit */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000903{
904 return (debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
905 != (linenr_T)0);
906}
907#endif
908
909/*
910 * Common code for dbg_find_breakpoint() and has_profiling().
911 */
912 static linenr_T
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100913debuggy_find(
914 int file, /* TRUE for a file, FALSE for a function */
915 char_u *fname, /* file or function name */
916 linenr_T after, /* after this line number */
917 garray_T *gap, /* either &dbg_breakp or &prof_ga */
918 int *fp) /* if not NULL: return forceit */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000919{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000920 struct debuggy *bp;
921 int i;
922 linenr_T lnum = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000923 char_u *name = fname;
924 int prev_got_int;
925
Bram Moolenaar05159a02005-02-26 23:04:13 +0000926 /* Return quickly when there are no breakpoints. */
927 if (gap->ga_len == 0)
928 return (linenr_T)0;
929
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930 /* Replace K_SNR in function name with "<SNR>". */
931 if (!file && fname[0] == K_SPECIAL)
932 {
933 name = alloc((unsigned)STRLEN(fname) + 3);
934 if (name == NULL)
935 name = fname;
936 else
937 {
938 STRCPY(name, "<SNR>");
939 STRCPY(name + 5, fname + 3);
940 }
941 }
942
Bram Moolenaar05159a02005-02-26 23:04:13 +0000943 for (i = 0; i < gap->ga_len; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000944 {
Bram Moolenaar05159a02005-02-26 23:04:13 +0000945 /* Skip entries that are not useful or are for a line that is beyond
946 * an already found breakpoint. */
947 bp = &DEBUGGY(gap, i);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100948 if (((bp->dbg_type == DBG_FILE) == file &&
949 bp->dbg_type != DBG_EXPR && (
Bram Moolenaar05159a02005-02-26 23:04:13 +0000950#ifdef FEAT_PROFILE
951 gap == &prof_ga ||
952#endif
953 (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000954 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955 /*
Bram Moolenaar05159a02005-02-26 23:04:13 +0000956 * Save the value of got_int and reset it. We don't want a
957 * previous interruption cancel matching, only hitting CTRL-C
958 * while matching should abort it.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959 */
960 prev_got_int = got_int;
961 got_int = FALSE;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100962 if (vim_regexec_prog(&bp->dbg_prog, FALSE, name, (colnr_T)0))
Bram Moolenaar05159a02005-02-26 23:04:13 +0000963 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964 lnum = bp->dbg_lnum;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000965 if (fp != NULL)
966 *fp = bp->dbg_forceit;
967 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000968 got_int |= prev_got_int;
969 }
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100970#ifdef FEAT_EVAL
971 else if (bp->dbg_type == DBG_EXPR)
972 {
973 typval_T *tv;
974 int line = FALSE;
975
976 prev_got_int = got_int;
977 got_int = FALSE;
978
979 tv = eval_expr(bp->dbg_name, NULL);
980 if (tv != NULL)
981 {
982 if (bp->dbg_val == NULL)
983 {
984 debug_oldval = typval_tostring(NULL);
985 bp->dbg_val = tv;
986 debug_newval = typval_tostring(bp->dbg_val);
987 line = TRUE;
988 }
989 else
990 {
Bram Moolenaar31988702018-02-13 12:57:42 +0100991 if (typval_compare(tv, bp->dbg_val, TYPE_EQUAL,
992 TRUE, FALSE) == OK
993 && tv->vval.v_number == FALSE)
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100994 {
Bram Moolenaar31988702018-02-13 12:57:42 +0100995 typval_T *v;
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100996
Bram Moolenaar31988702018-02-13 12:57:42 +0100997 line = TRUE;
998 debug_oldval = typval_tostring(bp->dbg_val);
999 /* Need to evaluate again, typval_compare() overwrites
1000 * "tv". */
1001 v = eval_expr(bp->dbg_name, NULL);
1002 debug_newval = typval_tostring(v);
1003 free_tv(bp->dbg_val);
1004 bp->dbg_val = v;
Bram Moolenaarc6f9f732018-02-11 19:06:26 +01001005 }
1006 free_tv(tv);
1007 }
1008 }
1009 else if (bp->dbg_val != NULL)
1010 {
1011 debug_oldval = typval_tostring(bp->dbg_val);
1012 debug_newval = typval_tostring(NULL);
1013 free_tv(bp->dbg_val);
1014 bp->dbg_val = NULL;
1015 line = TRUE;
1016 }
1017
1018 if (line)
1019 {
1020 lnum = after > 0 ? after : 1;
1021 break;
1022 }
1023
1024 got_int |= prev_got_int;
1025 }
1026#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027 }
1028 if (name != fname)
1029 vim_free(name);
1030
1031 return lnum;
1032}
1033
1034/*
1035 * Called when a breakpoint was encountered.
1036 */
1037 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001038dbg_breakpoint(char_u *name, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039{
1040 /* We need to check if this line is actually executed in do_one_cmd() */
1041 debug_breakpoint_name = name;
1042 debug_breakpoint_lnum = lnum;
1043}
Bram Moolenaar05159a02005-02-26 23:04:13 +00001044
1045
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001046# if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001047/*
1048 * Store the current time in "tm".
1049 */
1050 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001051profile_start(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001052{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001053# ifdef WIN3264
1054 QueryPerformanceCounter(tm);
1055# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001056 gettimeofday(tm, NULL);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001057# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001058}
1059
1060/*
1061 * Compute the elapsed time from "tm" till now and store in "tm".
1062 */
1063 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001064profile_end(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001065{
1066 proftime_T now;
1067
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001068# ifdef WIN3264
1069 QueryPerformanceCounter(&now);
1070 tm->QuadPart = now.QuadPart - tm->QuadPart;
1071# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001072 gettimeofday(&now, NULL);
1073 tm->tv_usec = now.tv_usec - tm->tv_usec;
1074 tm->tv_sec = now.tv_sec - tm->tv_sec;
1075 if (tm->tv_usec < 0)
1076 {
1077 tm->tv_usec += 1000000;
1078 --tm->tv_sec;
1079 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001080# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001081}
1082
1083/*
1084 * Subtract the time "tm2" from "tm".
1085 */
1086 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001087profile_sub(proftime_T *tm, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001088{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001089# ifdef WIN3264
1090 tm->QuadPart -= tm2->QuadPart;
1091# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001092 tm->tv_usec -= tm2->tv_usec;
1093 tm->tv_sec -= tm2->tv_sec;
1094 if (tm->tv_usec < 0)
1095 {
1096 tm->tv_usec += 1000000;
1097 --tm->tv_sec;
1098 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001099# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001100}
1101
1102/*
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001103 * Return a string that represents the time in "tm".
1104 * Uses a static buffer!
1105 */
1106 char *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001107profile_msg(proftime_T *tm)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001108{
1109 static char buf[50];
1110
1111# ifdef WIN3264
1112 LARGE_INTEGER fr;
1113
1114 QueryPerformanceFrequency(&fr);
1115 sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart);
1116# else
1117 sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec);
Bram Moolenaar76929292008-01-06 19:07:36 +00001118# endif
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001119 return buf;
1120}
1121
Bram Moolenaar79c2c882016-02-07 21:19:28 +01001122# if defined(FEAT_FLOAT) || defined(PROTO)
1123/*
1124 * Return a float that represents the time in "tm".
1125 */
1126 float_T
1127profile_float(proftime_T *tm)
1128{
1129# ifdef WIN3264
1130 LARGE_INTEGER fr;
1131
1132 QueryPerformanceFrequency(&fr);
1133 return (float_T)tm->QuadPart / (float_T)fr.QuadPart;
1134# else
1135 return (float_T)tm->tv_sec + (float_T)tm->tv_usec / 1000000.0;
1136# endif
1137}
1138# endif
1139
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001140/*
Bram Moolenaar76929292008-01-06 19:07:36 +00001141 * Put the time "msec" past now in "tm".
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001142 */
Bram Moolenaar76929292008-01-06 19:07:36 +00001143 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001144profile_setlimit(long msec, proftime_T *tm)
Bram Moolenaar76929292008-01-06 19:07:36 +00001145{
1146 if (msec <= 0) /* no limit */
1147 profile_zero(tm);
1148 else
1149 {
1150# ifdef WIN3264
1151 LARGE_INTEGER fr;
1152
1153 QueryPerformanceCounter(tm);
1154 QueryPerformanceFrequency(&fr);
Bram Moolenaarb3c70982008-01-18 10:40:55 +00001155 tm->QuadPart += (LONGLONG)((double)msec / 1000.0 * (double)fr.QuadPart);
Bram Moolenaar76929292008-01-06 19:07:36 +00001156# else
1157 long usec;
1158
1159 gettimeofday(tm, NULL);
1160 usec = (long)tm->tv_usec + (long)msec * 1000;
1161 tm->tv_usec = usec % 1000000L;
1162 tm->tv_sec += usec / 1000000L;
1163# endif
1164 }
1165}
1166
1167/*
1168 * Return TRUE if the current time is past "tm".
1169 */
1170 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001171profile_passed_limit(proftime_T *tm)
Bram Moolenaar76929292008-01-06 19:07:36 +00001172{
1173 proftime_T now;
1174
1175# ifdef WIN3264
1176 if (tm->QuadPart == 0) /* timer was not set */
1177 return FALSE;
1178 QueryPerformanceCounter(&now);
1179 return (now.QuadPart > tm->QuadPart);
1180# else
1181 if (tm->tv_sec == 0) /* timer was not set */
1182 return FALSE;
1183 gettimeofday(&now, NULL);
1184 return (now.tv_sec > tm->tv_sec
1185 || (now.tv_sec == tm->tv_sec && now.tv_usec > tm->tv_usec));
1186# endif
1187}
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001188
1189/*
1190 * Set the time in "tm" to zero.
1191 */
1192 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001193profile_zero(proftime_T *tm)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001194{
1195# ifdef WIN3264
1196 tm->QuadPart = 0;
1197# else
1198 tm->tv_usec = 0;
1199 tm->tv_sec = 0;
1200# endif
1201}
1202
Bram Moolenaar76929292008-01-06 19:07:36 +00001203# endif /* FEAT_PROFILE || FEAT_RELTIME */
1204
Bram Moolenaar975b5272016-03-15 23:10:59 +01001205# if defined(FEAT_TIMERS) || defined(PROTO)
1206static timer_T *first_timer = NULL;
Bram Moolenaar75537a92016-09-05 22:45:28 +02001207static long last_timer_id = 0;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001208
Bram Moolenaar56bc8e22018-05-10 18:05:56 +02001209 long
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001210proftime_time_left(proftime_T *due, proftime_T *now)
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001211{
1212# ifdef WIN3264
1213 LARGE_INTEGER fr;
1214
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001215 if (now->QuadPart > due->QuadPart)
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001216 return 0;
1217 QueryPerformanceFrequency(&fr);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001218 return (long)(((double)(due->QuadPart - now->QuadPart)
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001219 / (double)fr.QuadPart) * 1000);
1220# else
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001221 if (now->tv_sec > due->tv_sec)
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001222 return 0;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001223 return (due->tv_sec - now->tv_sec) * 1000
1224 + (due->tv_usec - now->tv_usec) / 1000;
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001225# endif
1226}
Bram Moolenaar417ccd72016-09-01 21:26:20 +02001227
Bram Moolenaar975b5272016-03-15 23:10:59 +01001228/*
1229 * Insert a timer in the list of timers.
1230 */
1231 static void
1232insert_timer(timer_T *timer)
1233{
1234 timer->tr_next = first_timer;
1235 timer->tr_prev = NULL;
1236 if (first_timer != NULL)
1237 first_timer->tr_prev = timer;
1238 first_timer = timer;
Bram Moolenaar4231da42016-06-02 14:30:04 +02001239 did_add_timer = TRUE;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001240}
1241
1242/*
1243 * Take a timer out of the list of timers.
1244 */
1245 static void
1246remove_timer(timer_T *timer)
1247{
1248 if (timer->tr_prev == NULL)
1249 first_timer = timer->tr_next;
1250 else
1251 timer->tr_prev->tr_next = timer->tr_next;
1252 if (timer->tr_next != NULL)
1253 timer->tr_next->tr_prev = timer->tr_prev;
1254}
1255
1256 static void
1257free_timer(timer_T *timer)
1258{
Bram Moolenaar75537a92016-09-05 22:45:28 +02001259 free_callback(timer->tr_callback, timer->tr_partial);
1260 vim_free(timer);
Bram Moolenaar975b5272016-03-15 23:10:59 +01001261}
1262
1263/*
1264 * Create a timer and return it. NULL if out of memory.
1265 * Caller should set the callback.
1266 */
1267 timer_T *
1268create_timer(long msec, int repeat)
1269{
1270 timer_T *timer = (timer_T *)alloc_clear(sizeof(timer_T));
Bram Moolenaaree39ef02016-09-10 19:17:42 +02001271 long prev_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001272
1273 if (timer == NULL)
1274 return NULL;
Bram Moolenaaree39ef02016-09-10 19:17:42 +02001275 if (++last_timer_id <= prev_id)
Bram Moolenaar75537a92016-09-05 22:45:28 +02001276 /* Overflow! Might cause duplicates... */
1277 last_timer_id = 0;
1278 timer->tr_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001279 insert_timer(timer);
1280 if (repeat != 0)
Bram Moolenaar975b5272016-03-15 23:10:59 +01001281 timer->tr_repeat = repeat - 1;
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001282 timer->tr_interval = msec;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001283
1284 profile_setlimit(msec, &timer->tr_due);
1285 return timer;
1286}
1287
1288/*
1289 * Invoke the callback of "timer".
1290 */
1291 static void
1292timer_callback(timer_T *timer)
1293{
1294 typval_T rettv;
1295 int dummy;
1296 typval_T argv[2];
1297
1298 argv[0].v_type = VAR_NUMBER;
Bram Moolenaar75537a92016-09-05 22:45:28 +02001299 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001300 argv[1].v_type = VAR_UNKNOWN;
1301
1302 call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001303 &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
Bram Moolenaar975b5272016-03-15 23:10:59 +01001304 timer->tr_partial, NULL);
1305 clear_tv(&rettv);
1306}
1307
1308/*
1309 * Call timers that are due.
1310 * Return the time in msec until the next timer is due.
Bram Moolenaarc4f83382017-07-07 14:50:44 +02001311 * Returns -1 if there are no pending timers.
Bram Moolenaar975b5272016-03-15 23:10:59 +01001312 */
1313 long
Bram Moolenaarcf089462016-06-12 21:18:43 +02001314check_due_timer(void)
Bram Moolenaar975b5272016-03-15 23:10:59 +01001315{
1316 timer_T *timer;
Bram Moolenaar75537a92016-09-05 22:45:28 +02001317 timer_T *timer_next;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001318 long this_due;
Bram Moolenaar597385a2016-03-16 23:24:43 +01001319 long next_due = -1;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001320 proftime_T now;
1321 int did_one = FALSE;
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001322 int need_update_screen = FALSE;
Bram Moolenaar75537a92016-09-05 22:45:28 +02001323 long current_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001324
Bram Moolenaarc577d812017-07-08 22:37:34 +02001325 /* Don't run any timers while exiting or dealing with an error. */
1326 if (exiting || aborting())
Bram Moolenaarc4f83382017-07-07 14:50:44 +02001327 return next_due;
1328
Bram Moolenaar75537a92016-09-05 22:45:28 +02001329 profile_start(&now);
1330 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
Bram Moolenaar975b5272016-03-15 23:10:59 +01001331 {
Bram Moolenaar75537a92016-09-05 22:45:28 +02001332 timer_next = timer->tr_next;
Bram Moolenaar417ccd72016-09-01 21:26:20 +02001333
Bram Moolenaar75537a92016-09-05 22:45:28 +02001334 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
1335 continue;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001336 this_due = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaar75537a92016-09-05 22:45:28 +02001337 if (this_due <= 1)
1338 {
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 Moolenaar1e8e1452017-06-24 16:03:06 +02001348
Bram Moolenaare723c422017-09-06 23:40:10 +02001349 /* Create a scope for running the timer callback, ignoring most of
1350 * the current scope, such as being inside a try/catch. */
Bram Moolenaar1e8e1452017-06-24 16:03:06 +02001351 timer_busy = timer_busy > 0 || vgetc_busy > 0;
1352 vgetc_busy = 0;
Bram Moolenaarc577d812017-07-08 22:37:34 +02001353 called_emsg = FALSE;
Bram Moolenaare723c422017-09-06 23:40:10 +02001354 did_emsg = FALSE;
1355 did_uncaught_emsg = FALSE;
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001356 must_redraw = 0;
Bram Moolenaare723c422017-09-06 23:40:10 +02001357 trylevel = 0;
1358 did_throw = FALSE;
1359 current_exception = NULL;
1360
Bram Moolenaar75537a92016-09-05 22:45:28 +02001361 timer->tr_firing = TRUE;
1362 timer_callback(timer);
1363 timer->tr_firing = FALSE;
Bram Moolenaare723c422017-09-06 23:40:10 +02001364
Bram Moolenaar75537a92016-09-05 22:45:28 +02001365 timer_next = timer->tr_next;
1366 did_one = TRUE;
Bram Moolenaar1e8e1452017-06-24 16:03:06 +02001367 timer_busy = save_timer_busy;
1368 vgetc_busy = save_vgetc_busy;
Bram Moolenaare723c422017-09-06 23:40:10 +02001369 if (did_uncaught_emsg)
Bram Moolenaarc577d812017-07-08 22:37:34 +02001370 ++timer->tr_emsg_count;
Bram Moolenaare723c422017-09-06 23:40:10 +02001371 did_emsg = save_did_emsg;
1372 called_emsg = save_called_emsg;
1373 trylevel = save_trylevel;
1374 did_throw = save_did_throw;
1375 current_exception = save_current_exception;
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001376 if (must_redraw != 0)
1377 need_update_screen = TRUE;
1378 must_redraw = must_redraw > save_must_redraw
1379 ? must_redraw : save_must_redraw;
Bram Moolenaarf5291f32017-09-14 22:55:37 +02001380 set_pressedreturn(save_ex_pressedreturn);
Bram Moolenaar75537a92016-09-05 22:45:28 +02001381
1382 /* Only fire the timer again if it repeats and stop_timer() wasn't
1383 * called while inside the callback (tr_id == -1). */
Bram Moolenaarc577d812017-07-08 22:37:34 +02001384 if (timer->tr_repeat != 0 && timer->tr_id != -1
1385 && timer->tr_emsg_count < 3)
Bram Moolenaar75537a92016-09-05 22:45:28 +02001386 {
1387 profile_setlimit(timer->tr_interval, &timer->tr_due);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001388 this_due = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaar75537a92016-09-05 22:45:28 +02001389 if (this_due < 1)
1390 this_due = 1;
1391 if (timer->tr_repeat > 0)
1392 --timer->tr_repeat;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001393 }
Bram Moolenaar75537a92016-09-05 22:45:28 +02001394 else
1395 {
1396 this_due = -1;
1397 remove_timer(timer);
1398 free_timer(timer);
1399 }
Bram Moolenaar975b5272016-03-15 23:10:59 +01001400 }
Bram Moolenaar75537a92016-09-05 22:45:28 +02001401 if (this_due > 0 && (next_due == -1 || next_due > this_due))
1402 next_due = this_due;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001403 }
1404
1405 if (did_one)
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001406 redraw_after_callback(need_update_screen);
Bram Moolenaar975b5272016-03-15 23:10:59 +01001407
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001408#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001409 if (bevalexpr_due_set)
1410 {
1411 this_due = proftime_time_left(&bevalexpr_due, &now);
1412 if (this_due <= 1)
1413 {
1414 bevalexpr_due_set = FALSE;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001415 if (balloonEval == NULL)
1416 {
1417 balloonEval = (BalloonEval *)alloc(sizeof(BalloonEval));
1418 balloonEvalForTerm = TRUE;
1419 }
1420 if (balloonEval != NULL)
1421 general_beval_cb(balloonEval, 0);
1422 }
Bram Moolenaar1c17ffa2018-04-24 15:19:04 +02001423 else if (next_due == -1 || next_due > this_due)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001424 next_due = this_due;
1425 }
1426#endif
Bram Moolenaar56bc8e22018-05-10 18:05:56 +02001427#ifdef FEAT_TERMINAL
1428 /* Some terminal windows may need their buffer updated. */
1429 next_due = term_check_timers(next_due, &now);
1430#endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001431
Bram Moolenaar75537a92016-09-05 22:45:28 +02001432 return current_id != last_timer_id ? 1 : next_due;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001433}
1434
1435/*
1436 * Find a timer by ID. Returns NULL if not found;
1437 */
1438 timer_T *
Bram Moolenaar75537a92016-09-05 22:45:28 +02001439find_timer(long id)
Bram Moolenaar975b5272016-03-15 23:10:59 +01001440{
1441 timer_T *timer;
1442
Bram Moolenaar75537a92016-09-05 22:45:28 +02001443 if (id >= 0)
1444 {
1445 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
1446 if (timer->tr_id == id)
1447 return timer;
1448 }
1449 return NULL;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001450}
1451
1452
1453/*
1454 * Stop a timer and delete it.
1455 */
1456 void
1457stop_timer(timer_T *timer)
1458{
Bram Moolenaar75537a92016-09-05 22:45:28 +02001459 if (timer->tr_firing)
1460 /* Free the timer after the callback returns. */
1461 timer->tr_id = -1;
1462 else
1463 {
1464 remove_timer(timer);
1465 free_timer(timer);
1466 }
Bram Moolenaar975b5272016-03-15 23:10:59 +01001467}
Bram Moolenaare3188e22016-05-31 21:13:04 +02001468
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001469 void
Bram Moolenaarb73598e2016-08-07 18:22:53 +02001470stop_all_timers(void)
1471{
Bram Moolenaar75537a92016-09-05 22:45:28 +02001472 timer_T *timer;
1473 timer_T *timer_next;
1474
1475 for (timer = first_timer; timer != NULL; timer = timer_next)
1476 {
1477 timer_next = timer->tr_next;
1478 stop_timer(timer);
1479 }
Bram Moolenaarb73598e2016-08-07 18:22:53 +02001480}
1481
1482 void
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001483add_timer_info(typval_T *rettv, timer_T *timer)
1484{
1485 list_T *list = rettv->vval.v_list;
1486 dict_T *dict = dict_alloc();
1487 dictitem_T *di;
1488 long remaining;
1489 proftime_T now;
1490
1491 if (dict == NULL)
1492 return;
1493 list_append_dict(list, dict);
1494
Bram Moolenaar75537a92016-09-05 22:45:28 +02001495 dict_add_nr_str(dict, "id", timer->tr_id, NULL);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001496 dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL);
1497
1498 profile_start(&now);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001499 remaining = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001500 dict_add_nr_str(dict, "remaining", (long)remaining, NULL);
1501
1502 dict_add_nr_str(dict, "repeat",
1503 (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1), NULL);
Bram Moolenaarb73598e2016-08-07 18:22:53 +02001504 dict_add_nr_str(dict, "paused", (long)(timer->tr_paused), NULL);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001505
1506 di = dictitem_alloc((char_u *)"callback");
1507 if (di != NULL)
1508 {
1509 if (dict_add(dict, di) == FAIL)
1510 vim_free(di);
1511 else if (timer->tr_partial != NULL)
1512 {
1513 di->di_tv.v_type = VAR_PARTIAL;
1514 di->di_tv.vval.v_partial = timer->tr_partial;
1515 ++timer->tr_partial->pt_refcount;
1516 }
1517 else
1518 {
1519 di->di_tv.v_type = VAR_FUNC;
1520 di->di_tv.vval.v_string = vim_strsave(timer->tr_callback);
1521 }
1522 di->di_tv.v_lock = 0;
1523 }
1524}
1525
1526 void
1527add_timer_info_all(typval_T *rettv)
1528{
1529 timer_T *timer;
1530
1531 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
Bram Moolenaar75537a92016-09-05 22:45:28 +02001532 if (timer->tr_id != -1)
1533 add_timer_info(rettv, timer);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001534}
1535
Bram Moolenaare3188e22016-05-31 21:13:04 +02001536/*
1537 * Mark references in partials of timers.
1538 */
1539 int
1540set_ref_in_timer(int copyID)
1541{
1542 int abort = FALSE;
1543 timer_T *timer;
1544 typval_T tv;
1545
1546 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
1547 {
Bram Moolenaar1e96d9b2016-07-29 22:15:09 +02001548 if (timer->tr_partial != NULL)
1549 {
1550 tv.v_type = VAR_PARTIAL;
1551 tv.vval.v_partial = timer->tr_partial;
1552 }
1553 else
1554 {
1555 tv.v_type = VAR_FUNC;
1556 tv.vval.v_string = timer->tr_callback;
1557 }
Bram Moolenaare3188e22016-05-31 21:13:04 +02001558 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
1559 }
1560 return abort;
1561}
Bram Moolenaar623e2632016-07-30 22:47:56 +02001562
1563# if defined(EXITFREE) || defined(PROTO)
1564 void
1565timer_free_all()
1566{
1567 timer_T *timer;
1568
1569 while (first_timer != NULL)
1570 {
1571 timer = first_timer;
1572 remove_timer(timer);
1573 free_timer(timer);
1574 }
1575}
1576# endif
Bram Moolenaar975b5272016-03-15 23:10:59 +01001577# endif
1578
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001579#if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_FLOAT)
1580# if defined(HAVE_MATH_H)
1581# include <math.h>
1582# endif
1583
1584/*
1585 * Divide the time "tm" by "count" and store in "tm2".
1586 */
1587 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001588profile_divide(proftime_T *tm, int count, proftime_T *tm2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001589{
1590 if (count == 0)
1591 profile_zero(tm2);
1592 else
1593 {
1594# ifdef WIN3264
1595 tm2->QuadPart = tm->QuadPart / count;
1596# else
1597 double usec = (tm->tv_sec * 1000000.0 + tm->tv_usec) / count;
1598
1599 tm2->tv_sec = floor(usec / 1000000.0);
Bram Moolenaara2e14fc2013-06-10 20:10:44 +02001600 tm2->tv_usec = vim_round(usec - (tm2->tv_sec * 1000000.0));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001601# endif
1602 }
1603}
1604#endif
1605
Bram Moolenaar76929292008-01-06 19:07:36 +00001606# if defined(FEAT_PROFILE) || defined(PROTO)
1607/*
1608 * Functions for profiling.
1609 */
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01001610static void script_do_profile(scriptitem_T *si);
1611static void script_dump_profile(FILE *fd);
Bram Moolenaar76929292008-01-06 19:07:36 +00001612static proftime_T prof_wait_time;
1613
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001614/*
Bram Moolenaar05159a02005-02-26 23:04:13 +00001615 * Add the time "tm2" to "tm".
1616 */
1617 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001618profile_add(proftime_T *tm, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001619{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001620# ifdef WIN3264
1621 tm->QuadPart += tm2->QuadPart;
1622# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001623 tm->tv_usec += tm2->tv_usec;
1624 tm->tv_sec += tm2->tv_sec;
1625 if (tm->tv_usec >= 1000000)
1626 {
1627 tm->tv_usec -= 1000000;
1628 ++tm->tv_sec;
1629 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001630# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001631}
1632
1633/*
Bram Moolenaar1056d982006-03-09 22:37:52 +00001634 * Add the "self" time from the total time and the children's time.
1635 */
1636 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001637profile_self(proftime_T *self, proftime_T *total, proftime_T *children)
Bram Moolenaar1056d982006-03-09 22:37:52 +00001638{
1639 /* Check that the result won't be negative. Can happen with recursive
1640 * calls. */
1641#ifdef WIN3264
1642 if (total->QuadPart <= children->QuadPart)
1643 return;
1644#else
1645 if (total->tv_sec < children->tv_sec
1646 || (total->tv_sec == children->tv_sec
1647 && total->tv_usec <= children->tv_usec))
1648 return;
1649#endif
1650 profile_add(self, total);
1651 profile_sub(self, children);
1652}
1653
1654/*
Bram Moolenaar05159a02005-02-26 23:04:13 +00001655 * Get the current waittime.
1656 */
1657 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001658profile_get_wait(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001659{
1660 *tm = prof_wait_time;
1661}
1662
1663/*
1664 * Subtract the passed waittime since "tm" from "tma".
1665 */
1666 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001667profile_sub_wait(proftime_T *tm, proftime_T *tma)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001668{
1669 proftime_T tm3 = prof_wait_time;
1670
1671 profile_sub(&tm3, tm);
1672 profile_sub(tma, &tm3);
1673}
1674
1675/*
1676 * Return TRUE if "tm1" and "tm2" are equal.
1677 */
1678 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001679profile_equal(proftime_T *tm1, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001680{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001681# ifdef WIN3264
1682 return (tm1->QuadPart == tm2->QuadPart);
1683# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001684 return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001685# endif
1686}
1687
1688/*
1689 * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2"
1690 */
1691 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001692profile_cmp(const proftime_T *tm1, const proftime_T *tm2)
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001693{
1694# ifdef WIN3264
1695 return (int)(tm2->QuadPart - tm1->QuadPart);
1696# else
1697 if (tm1->tv_sec == tm2->tv_sec)
1698 return tm2->tv_usec - tm1->tv_usec;
1699 return tm2->tv_sec - tm1->tv_sec;
1700# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001701}
1702
Bram Moolenaar05159a02005-02-26 23:04:13 +00001703static char_u *profile_fname = NULL;
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00001704static proftime_T pause_time;
Bram Moolenaar05159a02005-02-26 23:04:13 +00001705
1706/*
1707 * ":profile cmd args"
1708 */
1709 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001710ex_profile(exarg_T *eap)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001711{
1712 char_u *e;
1713 int len;
1714
1715 e = skiptowhite(eap->arg);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001716 len = (int)(e - eap->arg);
Bram Moolenaar05159a02005-02-26 23:04:13 +00001717 e = skipwhite(e);
1718
1719 if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL)
1720 {
1721 vim_free(profile_fname);
Bram Moolenaard94682f2015-04-13 15:37:56 +02001722 profile_fname = expand_env_save_opt(e, TRUE);
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00001723 do_profiling = PROF_YES;
Bram Moolenaar05159a02005-02-26 23:04:13 +00001724 profile_zero(&prof_wait_time);
1725 set_vim_var_nr(VV_PROFILING, 1L);
1726 }
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00001727 else if (do_profiling == PROF_NONE)
Bram Moolenaar54c1b492010-02-24 14:01:28 +01001728 EMSG(_("E750: First use \":profile start {fname}\""));
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00001729 else if (STRCMP(eap->arg, "pause") == 0)
1730 {
1731 if (do_profiling == PROF_YES)
1732 profile_start(&pause_time);
1733 do_profiling = PROF_PAUSED;
1734 }
1735 else if (STRCMP(eap->arg, "continue") == 0)
1736 {
1737 if (do_profiling == PROF_PAUSED)
1738 {
1739 profile_end(&pause_time);
1740 profile_add(&prof_wait_time, &pause_time);
1741 }
1742 do_profiling = PROF_YES;
1743 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00001744 else
1745 {
1746 /* The rest is similar to ":breakadd". */
1747 ex_breakadd(eap);
1748 }
1749}
1750
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001751/* Command line expansion for :profile. */
1752static enum
1753{
1754 PEXP_SUBCMD, /* expand :profile sub-commands */
Bram Moolenaar49789dc2011-02-25 14:46:09 +01001755 PEXP_FUNC /* expand :profile func {funcname} */
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001756} pexpand_what;
1757
1758static char *pexpand_cmds[] = {
1759 "start",
1760#define PROFCMD_START 0
1761 "pause",
1762#define PROFCMD_PAUSE 1
1763 "continue",
1764#define PROFCMD_CONTINUE 2
1765 "func",
1766#define PROFCMD_FUNC 3
1767 "file",
1768#define PROFCMD_FILE 4
1769 NULL
1770#define PROFCMD_LAST 5
1771};
1772
1773/*
1774 * Function given to ExpandGeneric() to obtain the profile command
1775 * specific expansion.
1776 */
1777 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001778get_profile_name(expand_T *xp UNUSED, int idx)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001779{
1780 switch (pexpand_what)
1781 {
1782 case PEXP_SUBCMD:
1783 return (char_u *)pexpand_cmds[idx];
1784 /* case PEXP_FUNC: TODO */
1785 default:
1786 return NULL;
1787 }
1788}
1789
1790/*
1791 * Handle command line completion for :profile command.
1792 */
1793 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001794set_context_in_profile_cmd(expand_T *xp, char_u *arg)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001795{
1796 char_u *end_subcmd;
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001797
1798 /* Default: expand subcommands. */
1799 xp->xp_context = EXPAND_PROFILE;
1800 pexpand_what = PEXP_SUBCMD;
1801 xp->xp_pattern = arg;
1802
1803 end_subcmd = skiptowhite(arg);
1804 if (*end_subcmd == NUL)
1805 return;
1806
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01001807 if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001808 {
1809 xp->xp_context = EXPAND_FILES;
1810 xp->xp_pattern = skipwhite(end_subcmd);
1811 return;
1812 }
1813
1814 /* TODO: expand function names after "func" */
1815 xp->xp_context = EXPAND_NOTHING;
1816}
1817
Bram Moolenaar05159a02005-02-26 23:04:13 +00001818/*
1819 * Dump the profiling info.
1820 */
1821 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001822profile_dump(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001823{
1824 FILE *fd;
1825
1826 if (profile_fname != NULL)
1827 {
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +00001828 fd = mch_fopen((char *)profile_fname, "w");
Bram Moolenaar05159a02005-02-26 23:04:13 +00001829 if (fd == NULL)
1830 EMSG2(_(e_notopen), profile_fname);
1831 else
1832 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00001833 script_dump_profile(fd);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001834 func_dump_profile(fd);
Bram Moolenaar05159a02005-02-26 23:04:13 +00001835 fclose(fd);
1836 }
1837 }
1838}
1839
1840/*
1841 * Start profiling script "fp".
1842 */
1843 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001844script_do_profile(scriptitem_T *si)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001845{
1846 si->sn_pr_count = 0;
1847 profile_zero(&si->sn_pr_total);
1848 profile_zero(&si->sn_pr_self);
1849
1850 ga_init2(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
1851 si->sn_prl_idx = -1;
1852 si->sn_prof_on = TRUE;
1853 si->sn_pr_nest = 0;
1854}
1855
1856/*
Bram Moolenaar67435d92017-10-19 21:04:37 +02001857 * Save time when starting to invoke another script or function.
Bram Moolenaar05159a02005-02-26 23:04:13 +00001858 */
1859 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001860script_prof_save(
1861 proftime_T *tm) /* place to store wait time */
Bram Moolenaar05159a02005-02-26 23:04:13 +00001862{
1863 scriptitem_T *si;
1864
1865 if (current_SID > 0 && current_SID <= script_items.ga_len)
1866 {
1867 si = &SCRIPT_ITEM(current_SID);
1868 if (si->sn_prof_on && si->sn_pr_nest++ == 0)
1869 profile_start(&si->sn_pr_child);
1870 }
1871 profile_get_wait(tm);
1872}
1873
1874/*
1875 * Count time spent in children after invoking another script or function.
1876 */
1877 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001878script_prof_restore(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001879{
1880 scriptitem_T *si;
1881
1882 if (current_SID > 0 && current_SID <= script_items.ga_len)
1883 {
1884 si = &SCRIPT_ITEM(current_SID);
1885 if (si->sn_prof_on && --si->sn_pr_nest == 0)
1886 {
1887 profile_end(&si->sn_pr_child);
1888 profile_sub_wait(tm, &si->sn_pr_child); /* don't count wait time */
1889 profile_add(&si->sn_pr_children, &si->sn_pr_child);
1890 profile_add(&si->sn_prl_children, &si->sn_pr_child);
1891 }
1892 }
1893}
1894
1895static proftime_T inchar_time;
1896
1897/*
1898 * Called when starting to wait for the user to type a character.
1899 */
1900 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001901prof_inchar_enter(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001902{
1903 profile_start(&inchar_time);
1904}
1905
1906/*
1907 * Called when finished waiting for the user to type a character.
1908 */
1909 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001910prof_inchar_exit(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001911{
1912 profile_end(&inchar_time);
1913 profile_add(&prof_wait_time, &inchar_time);
1914}
1915
1916/*
1917 * Dump the profiling results for all scripts in file "fd".
1918 */
1919 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001920script_dump_profile(FILE *fd)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001921{
1922 int id;
1923 scriptitem_T *si;
1924 int i;
1925 FILE *sfd;
1926 sn_prl_T *pp;
1927
1928 for (id = 1; id <= script_items.ga_len; ++id)
1929 {
1930 si = &SCRIPT_ITEM(id);
1931 if (si->sn_prof_on)
1932 {
1933 fprintf(fd, "SCRIPT %s\n", si->sn_name);
1934 if (si->sn_pr_count == 1)
1935 fprintf(fd, "Sourced 1 time\n");
1936 else
1937 fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
1938 fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total));
1939 fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self));
1940 fprintf(fd, "\n");
1941 fprintf(fd, "count total (s) self (s)\n");
1942
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +00001943 sfd = mch_fopen((char *)si->sn_name, "r");
Bram Moolenaar05159a02005-02-26 23:04:13 +00001944 if (sfd == NULL)
1945 fprintf(fd, "Cannot open file!\n");
1946 else
1947 {
Bram Moolenaar67435d92017-10-19 21:04:37 +02001948 /* Keep going till the end of file, so that trailing
1949 * continuation lines are listed. */
1950 for (i = 0; ; ++i)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001951 {
1952 if (vim_fgets(IObuff, IOSIZE, sfd))
1953 break;
Bram Moolenaarac112f02017-12-05 16:46:28 +01001954 /* When a line has been truncated, append NL, taking care
1955 * of multi-byte characters . */
1956 if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL)
1957 {
1958 int n = IOSIZE - 2;
1959# ifdef FEAT_MBYTE
1960 if (enc_utf8)
1961 {
1962 /* Move to the first byte of this char.
1963 * utf_head_off() doesn't work, because it checks
1964 * for a truncated character. */
1965 while (n > 0 && (IObuff[n] & 0xc0) == 0x80)
1966 --n;
1967 }
1968 else if (has_mbyte)
1969 n -= mb_head_off(IObuff, IObuff + n);
1970# endif
1971 IObuff[n] = NL;
1972 IObuff[n + 1] = NUL;
1973 }
Bram Moolenaar67435d92017-10-19 21:04:37 +02001974 if (i < si->sn_prl_ga.ga_len
1975 && (pp = &PRL_ITEM(si, i))->snp_count > 0)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001976 {
1977 fprintf(fd, "%5d ", pp->snp_count);
1978 if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self))
1979 fprintf(fd, " ");
1980 else
1981 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total));
1982 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self));
1983 }
1984 else
1985 fprintf(fd, " ");
1986 fprintf(fd, "%s", IObuff);
1987 }
1988 fclose(sfd);
1989 }
1990 fprintf(fd, "\n");
1991 }
1992 }
1993}
1994
1995/*
1996 * Return TRUE when a function defined in the current script should be
1997 * profiled.
1998 */
1999 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002000prof_def_func(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00002001{
Bram Moolenaar53180ce2005-07-05 21:48:14 +00002002 if (current_SID > 0)
2003 return SCRIPT_ITEM(current_SID).sn_pr_force;
2004 return FALSE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002005}
2006
2007# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008#endif
2009
2010/*
2011 * If 'autowrite' option set, try to write the file.
2012 * Careful: autocommands may make "buf" invalid!
2013 *
2014 * return FAIL for failure, OK otherwise
2015 */
2016 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002017autowrite(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002018{
Bram Moolenaar373154b2007-02-13 05:19:30 +00002019 int r;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002020 bufref_T bufref;
Bram Moolenaar373154b2007-02-13 05:19:30 +00002021
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022 if (!(p_aw || p_awa) || !p_write
2023#ifdef FEAT_QUICKFIX
Bram Moolenaar373154b2007-02-13 05:19:30 +00002024 /* never autowrite a "nofile" or "nowrite" buffer */
2025 || bt_dontwrite(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002026#endif
Bram Moolenaar373154b2007-02-13 05:19:30 +00002027 || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002028 return FAIL;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002029 set_bufref(&bufref, buf);
Bram Moolenaar373154b2007-02-13 05:19:30 +00002030 r = buf_write_all(buf, forceit);
2031
2032 /* Writing may succeed but the buffer still changed, e.g., when there is a
2033 * conversion error. We do want to return FAIL then. */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002034 if (bufref_valid(&bufref) && bufIsChanged(buf))
Bram Moolenaar373154b2007-02-13 05:19:30 +00002035 r = FAIL;
2036 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002037}
2038
2039/*
2040 * flush all buffers, except the ones that are readonly
2041 */
2042 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002043autowrite_all(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002044{
2045 buf_T *buf;
2046
2047 if (!(p_aw || p_awa) || !p_write)
2048 return;
Bram Moolenaar29323592016-07-24 22:04:11 +02002049 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002050 if (bufIsChanged(buf) && !buf->b_p_ro)
2051 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002052 bufref_T bufref;
2053
2054 set_bufref(&bufref, buf);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002055
Bram Moolenaar071d4272004-06-13 20:20:40 +00002056 (void)buf_write_all(buf, FALSE);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002057
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 /* an autocommand may have deleted the buffer */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002059 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060 buf = firstbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002061 }
2062}
2063
2064/*
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002065 * Return TRUE if buffer was changed and cannot be abandoned.
2066 * For flags use the CCGD_ values.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002067 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002069check_changed(buf_T *buf, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070{
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002071 int forceit = (flags & CCGD_FORCEIT);
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002072 bufref_T bufref;
2073
2074 set_bufref(&bufref, buf);
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002075
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 if ( !forceit
2077 && bufIsChanged(buf)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002078 && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1)
2079 && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080 {
2081#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2082 if ((p_confirm || cmdmod.confirm) && p_write)
2083 {
2084 buf_T *buf2;
2085 int count = 0;
2086
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002087 if (flags & CCGD_ALLBUF)
Bram Moolenaar29323592016-07-24 22:04:11 +02002088 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089 if (bufIsChanged(buf2)
2090 && (buf2->b_ffname != NULL
2091# ifdef FEAT_BROWSE
2092 || cmdmod.browse
2093# endif
2094 ))
2095 ++count;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002096 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002097 /* Autocommand deleted buffer, oops! It's not changed now. */
2098 return FALSE;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002099
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100 dialog_changed(buf, count > 1);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002101
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002102 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 /* Autocommand deleted buffer, oops! It's not changed now. */
2104 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002105 return bufIsChanged(buf);
2106 }
2107#endif
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002108 if (flags & CCGD_EXCMD)
Bram Moolenaarf5be7cd2017-08-17 16:55:13 +02002109 no_write_message();
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002110 else
Bram Moolenaar7a760922018-02-19 23:10:02 +01002111 no_write_message_nobang(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 return TRUE;
2113 }
2114 return FALSE;
2115}
2116
2117#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
2118
2119#if defined(FEAT_BROWSE) || defined(PROTO)
2120/*
2121 * When wanting to write a file without a file name, ask the user for a name.
2122 */
2123 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002124browse_save_fname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125{
2126 if (buf->b_fname == NULL)
2127 {
2128 char_u *fname;
2129
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002130 fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"),
2131 NULL, NULL, NULL, NULL, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002132 if (fname != NULL)
2133 {
2134 if (setfname(buf, fname, NULL, TRUE) == OK)
2135 buf->b_flags |= BF_NOTEDITED;
2136 vim_free(fname);
2137 }
2138 }
2139}
2140#endif
2141
2142/*
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02002143 * Ask the user what to do when abandoning a changed buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144 * Must check 'write' option first!
2145 */
2146 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002147dialog_changed(
2148 buf_T *buf,
2149 int checkall) /* may abandon all changed buffers */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002150{
Bram Moolenaard9462e32011-04-11 21:35:11 +02002151 char_u buff[DIALOG_MSG_SIZE];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002152 int ret;
2153 buf_T *buf2;
Bram Moolenaar8218f602012-04-25 17:32:18 +02002154 exarg_T ea;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155
Bram Moolenaar3f9a1ff2017-08-21 22:06:02 +02002156 dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002157 if (checkall)
2158 ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
2159 else
2160 ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
2161
Bram Moolenaar8218f602012-04-25 17:32:18 +02002162 /* Init ea pseudo-structure, this is needed for the check_overwrite()
2163 * function. */
2164 ea.append = ea.forceit = FALSE;
2165
Bram Moolenaar071d4272004-06-13 20:20:40 +00002166 if (ret == VIM_YES)
2167 {
2168#ifdef FEAT_BROWSE
2169 /* May get file name, when there is none */
2170 browse_save_fname(buf);
2171#endif
Bram Moolenaar8218f602012-04-25 17:32:18 +02002172 if (buf->b_fname != NULL && check_overwrite(&ea, buf,
2173 buf->b_fname, buf->b_ffname, FALSE) == OK)
2174 /* didn't hit Cancel */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002175 (void)buf_write_all(buf, FALSE);
2176 }
2177 else if (ret == VIM_NO)
2178 {
2179 unchanged(buf, TRUE);
2180 }
2181 else if (ret == VIM_ALL)
2182 {
2183 /*
2184 * Write all modified files that can be written.
2185 * Skip readonly buffers, these need to be confirmed
2186 * individually.
2187 */
Bram Moolenaar29323592016-07-24 22:04:11 +02002188 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 {
2190 if (bufIsChanged(buf2)
2191 && (buf2->b_ffname != NULL
2192#ifdef FEAT_BROWSE
2193 || cmdmod.browse
2194#endif
2195 )
2196 && !buf2->b_p_ro)
2197 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002198 bufref_T bufref;
2199
2200 set_bufref(&bufref, buf2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201#ifdef FEAT_BROWSE
2202 /* May get file name, when there is none */
2203 browse_save_fname(buf2);
2204#endif
Bram Moolenaar8218f602012-04-25 17:32:18 +02002205 if (buf2->b_fname != NULL && check_overwrite(&ea, buf2,
2206 buf2->b_fname, buf2->b_ffname, FALSE) == OK)
2207 /* didn't hit Cancel */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 (void)buf_write_all(buf2, FALSE);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002209
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210 /* an autocommand may have deleted the buffer */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002211 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002212 buf2 = firstbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213 }
2214 }
2215 }
2216 else if (ret == VIM_DISCARDALL)
2217 {
2218 /*
2219 * mark all buffers as unchanged
2220 */
Bram Moolenaar29323592016-07-24 22:04:11 +02002221 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002222 unchanged(buf2, TRUE);
2223 }
2224}
2225#endif
2226
2227/*
2228 * Return TRUE if the buffer "buf" can be abandoned, either by making it
2229 * hidden, autowriting it or unloading it.
2230 */
2231 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002232can_abandon(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002233{
Bram Moolenaareb44a682017-08-03 22:44:55 +02002234 return ( buf_hide(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002235 || !bufIsChanged(buf)
2236 || buf->b_nwindows > 1
2237 || autowrite(buf, forceit) == OK
2238 || forceit);
2239}
2240
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01002241static void add_bufnum(int *bufnrs, int *bufnump, int nr);
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002242
2243/*
2244 * Add a buffer number to "bufnrs", unless it's already there.
2245 */
2246 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002247add_bufnum(int *bufnrs, int *bufnump, int nr)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002248{
2249 int i;
2250
2251 for (i = 0; i < *bufnump; ++i)
2252 if (bufnrs[i] == nr)
2253 return;
2254 bufnrs[*bufnump] = nr;
2255 *bufnump = *bufnump + 1;
2256}
2257
Bram Moolenaar071d4272004-06-13 20:20:40 +00002258/*
2259 * Return TRUE if any buffer was changed and cannot be abandoned.
2260 * That changed buffer becomes the current buffer.
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002261 * When "unload" is TRUE the current buffer is unloaded instead of making it
Bram Moolenaar027387f2016-01-02 22:25:52 +01002262 * hidden. This is used for ":q!".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002263 */
2264 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002265check_changed_any(
2266 int hidden, /* Only check hidden buffers */
2267 int unload)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268{
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002269 int ret = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002270 buf_T *buf;
2271 int save;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002272 int i;
2273 int bufnum = 0;
2274 int bufcount = 0;
2275 int *bufnrs;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002276 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002279 /* Make a list of all buffers, with the most important ones first. */
Bram Moolenaar29323592016-07-24 22:04:11 +02002280 FOR_ALL_BUFFERS(buf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002281 ++bufcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002282
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002283 if (bufcount == 0)
2284 return FALSE;
2285
2286 bufnrs = (int *)alloc(sizeof(int) * bufcount);
2287 if (bufnrs == NULL)
2288 return FALSE;
2289
2290 /* curbuf */
2291 bufnrs[bufnum++] = curbuf->b_fnum;
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002292
2293 /* buffers in current tab */
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002294 FOR_ALL_WINDOWS(wp)
2295 if (wp->w_buffer != curbuf)
2296 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
2297
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002298 /* buffers in other tabs */
Bram Moolenaar29323592016-07-24 22:04:11 +02002299 FOR_ALL_TABPAGES(tp)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002300 if (tp != curtab)
2301 for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
2302 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002303
2304 /* any other buffer */
Bram Moolenaar29323592016-07-24 22:04:11 +02002305 FOR_ALL_BUFFERS(buf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002306 add_bufnum(bufnrs, &bufnum, buf->b_fnum);
2307
2308 for (i = 0; i < bufnum; ++i)
2309 {
2310 buf = buflist_findnr(bufnrs[i]);
2311 if (buf == NULL)
2312 continue;
2313 if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf))
2314 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002315 bufref_T bufref;
2316
2317 set_bufref(&bufref, buf);
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002318#ifdef FEAT_TERMINAL
2319 if (term_job_running(buf->b_term))
2320 {
2321 if (term_try_stop_job(buf) == FAIL)
2322 break;
2323 }
2324 else
2325#endif
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002326 /* Try auto-writing the buffer. If this fails but the buffer no
2327 * longer exists it's not changed, that's OK. */
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002328 if (check_changed(buf, (p_awa ? CCGD_AW : 0)
2329 | CCGD_MULTWIN
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002330 | CCGD_ALLBUF) && bufref_valid(&bufref))
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002331 break; /* didn't save - still changes */
2332 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002333 }
2334
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002335 if (i >= bufnum)
2336 goto theend;
2337
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002338 /* Get here if "buf" cannot be abandoned. */
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002339 ret = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002340 exiting = FALSE;
2341#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2342 /*
2343 * When ":confirm" used, don't give an error message.
2344 */
2345 if (!(p_confirm || cmdmod.confirm))
2346#endif
2347 {
2348 /* There must be a wait_return for this message, do_buffer()
2349 * may cause a redraw. But wait_return() is a no-op when vgetc()
2350 * is busy (Quit used from window menu), then make sure we don't
2351 * cause a scroll up. */
Bram Moolenaar61660ea2006-04-07 21:40:07 +00002352 if (vgetc_busy > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002353 {
2354 msg_row = cmdline_row;
2355 msg_col = 0;
2356 msg_didout = FALSE;
2357 }
Bram Moolenaareb44a682017-08-03 22:44:55 +02002358 if (
2359#ifdef FEAT_TERMINAL
2360 term_job_running(buf->b_term)
2361 ? EMSG2(_("E947: Job still running in buffer \"%s\""),
2362 buf->b_fname)
2363 :
2364#endif
2365 EMSG2(_("E162: No write since last change for buffer \"%s\""),
Bram Moolenaare1704ba2012-10-03 18:25:00 +02002366 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367 {
2368 save = no_wait_return;
2369 no_wait_return = FALSE;
2370 wait_return(FALSE);
2371 no_wait_return = save;
2372 }
2373 }
2374
Bram Moolenaar071d4272004-06-13 20:20:40 +00002375 /* Try to find a window that contains the buffer. */
2376 if (buf != curbuf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002377 FOR_ALL_TAB_WINDOWS(tp, wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378 if (wp->w_buffer == buf)
2379 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002380 bufref_T bufref;
2381
2382 set_bufref(&bufref, buf);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002383
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002384 goto_tabpage_win(tp, wp);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002385
Bram Moolenaar071d4272004-06-13 20:20:40 +00002386 /* Paranoia: did autocms wipe out the buffer with changes? */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002387 if (!bufref_valid(&bufref))
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002388 goto theend;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002389 goto buf_found;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002390 }
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002391buf_found:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002392
2393 /* Open the changed buffer in the current window. */
2394 if (buf != curbuf)
Bram Moolenaar027387f2016-01-02 22:25:52 +01002395 set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002396
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002397theend:
2398 vim_free(bufnrs);
2399 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002400}
2401
2402/*
2403 * return FAIL if there is no file name, OK if there is one
2404 * give error message for FAIL
2405 */
2406 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002407check_fname(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002408{
2409 if (curbuf->b_ffname == NULL)
2410 {
2411 EMSG(_(e_noname));
2412 return FAIL;
2413 }
2414 return OK;
2415}
2416
2417/*
2418 * flush the contents of a buffer, unless it has no file name
2419 *
2420 * return FAIL for failure, OK otherwise
2421 */
2422 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002423buf_write_all(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424{
2425 int retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002426 buf_T *old_curbuf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427
2428 retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
2429 (linenr_T)1, buf->b_ml.ml_line_count, NULL,
2430 FALSE, forceit, TRUE, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 if (curbuf != old_curbuf)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002432 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01002433 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434 MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002435 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002436 return retval;
2437}
2438
2439/*
2440 * Code to handle the argument list.
2441 */
2442
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01002443static char_u *do_one_arg(char_u *str);
2444static int do_arglist(char_u *str, int what, int after);
2445static void alist_check_arg_idx(void);
2446static int editing_arg_idx(win_T *win);
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01002447static int alist_add_list(int count, char_u **files, int after);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002448#define AL_SET 1
2449#define AL_ADD 2
2450#define AL_DEL 3
2451
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002453 * Isolate one argument, taking backticks.
2454 * Changes the argument in-place, puts a NUL after it. Backticks remain.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455 * Return a pointer to the start of the next argument.
2456 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002457 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002458do_one_arg(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002459{
2460 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461 int inbacktick;
2462
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463 inbacktick = FALSE;
2464 for (p = str; *str; ++str)
2465 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002466 /* When the backslash is used for escaping the special meaning of a
2467 * character we need to keep it until wildcard expansion. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468 if (rem_backslash(str))
2469 {
2470 *p++ = *str++;
2471 *p++ = *str;
2472 }
2473 else
2474 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002475 /* An item ends at a space not in backticks */
2476 if (!inbacktick && vim_isspace(*str))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002477 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002478 if (*str == '`')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002479 inbacktick ^= TRUE;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002480 *p++ = *str;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002481 }
2482 }
2483 str = skipwhite(str);
2484 *p = NUL;
2485
2486 return str;
2487}
2488
Bram Moolenaar86b68352004-12-27 21:59:20 +00002489/*
2490 * Separate the arguments in "str" and return a list of pointers in the
2491 * growarray "gap".
2492 */
Bram Moolenaar398ee732017-08-03 14:29:14 +02002493 static int
2494get_arglist(garray_T *gap, char_u *str, int escaped)
Bram Moolenaar86b68352004-12-27 21:59:20 +00002495{
2496 ga_init2(gap, (int)sizeof(char_u *), 20);
2497 while (*str != NUL)
2498 {
2499 if (ga_grow(gap, 1) == FAIL)
2500 {
2501 ga_clear(gap);
2502 return FAIL;
2503 }
2504 ((char_u **)gap->ga_data)[gap->ga_len++] = str;
2505
Bram Moolenaar398ee732017-08-03 14:29:14 +02002506 /* If str is escaped, don't handle backslashes or spaces */
2507 if (!escaped)
2508 return OK;
2509
Bram Moolenaar86b68352004-12-27 21:59:20 +00002510 /* Isolate one argument, change it in-place, put a NUL after it. */
2511 str = do_one_arg(str);
2512 }
2513 return OK;
2514}
2515
Bram Moolenaar7df351e2006-01-23 22:30:28 +00002516#if defined(FEAT_QUICKFIX) || defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002517/*
2518 * Parse a list of arguments (file names), expand them and return in
Bram Moolenaar8f5c6f02012-06-29 12:57:06 +02002519 * "fnames[fcountp]". When "wig" is TRUE, removes files matching 'wildignore'.
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002520 * Return FAIL or OK.
2521 */
2522 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002523get_arglist_exp(
2524 char_u *str,
2525 int *fcountp,
2526 char_u ***fnamesp,
2527 int wig)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002528{
2529 garray_T ga;
2530 int i;
2531
Bram Moolenaar398ee732017-08-03 14:29:14 +02002532 if (get_arglist(&ga, str, TRUE) == FAIL)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002533 return FAIL;
Bram Moolenaar8f5c6f02012-06-29 12:57:06 +02002534 if (wig == TRUE)
2535 i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
2536 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
2537 else
2538 i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
2539 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
2540
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002541 ga_clear(&ga);
2542 return i;
2543}
2544#endif
2545
Bram Moolenaar071d4272004-06-13 20:20:40 +00002546/*
2547 * Redefine the argument list.
2548 */
2549 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002550set_arglist(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002551{
2552 do_arglist(str, AL_SET, 0);
2553}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002554
2555/*
2556 * "what" == AL_SET: Redefine the argument list to 'str'.
2557 * "what" == AL_ADD: add files in 'str' to the argument list after "after".
2558 * "what" == AL_DEL: remove files in 'str' from the argument list.
2559 *
2560 * Return FAIL for failure, OK otherwise.
2561 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002562 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002563do_arglist(
2564 char_u *str,
Bram Moolenaarf1d25012016-03-03 12:22:53 +01002565 int what,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002566 int after UNUSED) /* 0 means before first one */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002567{
2568 garray_T new_ga;
2569 int exp_count;
2570 char_u **exp_files;
2571 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572 char_u *p;
2573 int match;
Bram Moolenaar398ee732017-08-03 14:29:14 +02002574 int arg_escaped = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575
2576 /*
Bram Moolenaar2faa29f2016-01-23 23:02:34 +01002577 * Set default argument for ":argadd" command.
2578 */
2579 if (what == AL_ADD && *str == NUL)
2580 {
2581 if (curbuf->b_ffname == NULL)
2582 return FAIL;
2583 str = curbuf->b_fname;
Bram Moolenaar398ee732017-08-03 14:29:14 +02002584 arg_escaped = FALSE;
Bram Moolenaar2faa29f2016-01-23 23:02:34 +01002585 }
2586
2587 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002588 * Collect all file name arguments in "new_ga".
2589 */
Bram Moolenaar398ee732017-08-03 14:29:14 +02002590 if (get_arglist(&new_ga, str, arg_escaped) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00002591 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593 if (what == AL_DEL)
2594 {
2595 regmatch_T regmatch;
2596 int didone;
2597
2598 /*
2599 * Delete the items: use each item as a regexp and find a match in the
2600 * argument list.
2601 */
Bram Moolenaar71afbfe2013-03-19 16:49:16 +01002602 regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002603 for (i = 0; i < new_ga.ga_len && !got_int; ++i)
2604 {
2605 p = ((char_u **)new_ga.ga_data)[i];
2606 p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
2607 if (p == NULL)
2608 break;
2609 regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
2610 if (regmatch.regprog == NULL)
2611 {
2612 vim_free(p);
2613 break;
2614 }
2615
2616 didone = FALSE;
2617 for (match = 0; match < ARGCOUNT; ++match)
2618 if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
2619 (colnr_T)0))
2620 {
2621 didone = TRUE;
2622 vim_free(ARGLIST[match].ae_fname);
2623 mch_memmove(ARGLIST + match, ARGLIST + match + 1,
2624 (ARGCOUNT - match - 1) * sizeof(aentry_T));
2625 --ALIST(curwin)->al_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626 if (curwin->w_arg_idx > match)
2627 --curwin->w_arg_idx;
2628 --match;
2629 }
2630
Bram Moolenaar473de612013-06-08 18:19:48 +02002631 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 vim_free(p);
2633 if (!didone)
2634 EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
2635 }
2636 ga_clear(&new_ga);
2637 }
2638 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002639 {
2640 i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
2641 &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
2642 ga_clear(&new_ga);
Bram Moolenaar2db5c3b2016-01-16 22:49:34 +01002643 if (i == FAIL || exp_count == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002644 {
2645 EMSG(_(e_nomatch));
2646 return FAIL;
2647 }
2648
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649 if (what == AL_ADD)
2650 {
2651 (void)alist_add_list(exp_count, exp_files, after);
2652 vim_free(exp_files);
2653 }
2654 else /* what == AL_SET */
Bram Moolenaar86b68352004-12-27 21:59:20 +00002655 alist_set(ALIST(curwin), exp_count, exp_files, FALSE, NULL, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656 }
2657
2658 alist_check_arg_idx();
2659
2660 return OK;
2661}
2662
2663/*
2664 * Check the validity of the arg_idx for each other window.
2665 */
2666 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002667alist_check_arg_idx(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002668{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002669 win_T *win;
Bram Moolenaarf740b292006-02-16 22:11:02 +00002670 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671
Bram Moolenaarf740b292006-02-16 22:11:02 +00002672 FOR_ALL_TAB_WINDOWS(tp, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 if (win->w_alist == curwin->w_alist)
2674 check_arg_idx(win);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002675}
2676
2677/*
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01002678 * Return TRUE if window "win" is editing the file at the current argument
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002679 * index.
2680 */
2681 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002682editing_arg_idx(win_T *win)
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002683{
2684 return !(win->w_arg_idx >= WARGCOUNT(win)
2685 || (win->w_buffer->b_fnum
2686 != WARGLIST(win)[win->w_arg_idx].ae_fnum
2687 && (win->w_buffer->b_ffname == NULL
2688 || !(fullpathcmp(
2689 alist_name(&WARGLIST(win)[win->w_arg_idx]),
2690 win->w_buffer->b_ffname, TRUE) & FPC_SAME))));
2691}
2692
2693/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002694 * Check if window "win" is editing the w_arg_idx file in its argument list.
2695 */
2696 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002697check_arg_idx(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002698{
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002699 if (WARGCOUNT(win) > 1 && !editing_arg_idx(win))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700 {
2701 /* We are not editing the current entry in the argument list.
2702 * Set "arg_had_last" if we are editing the last one. */
2703 win->w_arg_idx_invalid = TRUE;
2704 if (win->w_arg_idx != WARGCOUNT(win) - 1
2705 && arg_had_last == FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 && ALIST(win) == &global_alist
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 && GARGCOUNT > 0
2708 && win->w_arg_idx < GARGCOUNT
2709 && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
2710 || (win->w_buffer->b_ffname != NULL
2711 && (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
2712 win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
2713 arg_had_last = TRUE;
2714 }
2715 else
2716 {
2717 /* We are editing the current entry in the argument list.
2718 * Set "arg_had_last" if it's also the last one */
2719 win->w_arg_idx_invalid = FALSE;
2720 if (win->w_arg_idx == WARGCOUNT(win) - 1
Bram Moolenaar4033c552017-09-16 20:54:51 +02002721 && win->w_alist == &global_alist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002722 arg_had_last = TRUE;
2723 }
2724}
2725
2726/*
2727 * ":args", ":argslocal" and ":argsglobal".
2728 */
2729 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002730ex_args(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731{
2732 int i;
2733
2734 if (eap->cmdidx != CMD_args)
2735 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736 alist_unlink(ALIST(curwin));
2737 if (eap->cmdidx == CMD_argglobal)
2738 ALIST(curwin) = &global_alist;
2739 else /* eap->cmdidx == CMD_arglocal */
2740 alist_new();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 }
2742
2743 if (!ends_excmd(*eap->arg))
2744 {
2745 /*
2746 * ":args file ..": define new argument list, handle like ":next"
2747 * Also for ":argslocal file .." and ":argsglobal file ..".
2748 */
2749 ex_next(eap);
2750 }
Bram Moolenaar0c72fe42018-03-29 16:04:08 +02002751 else if (eap->cmdidx == CMD_args)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002752 {
2753 /*
2754 * ":args": list arguments.
2755 */
2756 if (ARGCOUNT > 0)
2757 {
Bram Moolenaar405dadb2018-04-20 22:48:58 +02002758 char_u **items = (char_u **)alloc(sizeof(char_u *) * ARGCOUNT);
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002759
2760 if (items != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002761 {
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002762 /* Overwrite the command, for a short list there is no
2763 * scrolling required and no wait_return(). */
2764 gotocmdline(TRUE);
2765
2766 for (i = 0; i < ARGCOUNT; ++i)
Bram Moolenaar405dadb2018-04-20 22:48:58 +02002767 items[i] = alist_name(&ARGLIST[i]);
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002768 list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
2769 vim_free(items);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 }
2771 }
2772 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002773 else if (eap->cmdidx == CMD_arglocal)
2774 {
2775 garray_T *gap = &curwin->w_alist->al_ga;
2776
2777 /*
2778 * ":argslocal": make a local copy of the global argument list.
2779 */
2780 if (ga_grow(gap, GARGCOUNT) == OK)
2781 for (i = 0; i < GARGCOUNT; ++i)
2782 if (GARGLIST[i].ae_fname != NULL)
2783 {
2784 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
2785 vim_strsave(GARGLIST[i].ae_fname);
2786 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
2787 GARGLIST[i].ae_fnum;
2788 ++gap->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002789 }
2790 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791}
2792
2793/*
2794 * ":previous", ":sprevious", ":Next" and ":sNext".
2795 */
2796 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002797ex_previous(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002798{
2799 /* If past the last one already, go to the last one. */
2800 if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
2801 do_argfile(eap, ARGCOUNT - 1);
2802 else
2803 do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
2804}
2805
2806/*
2807 * ":rewind", ":first", ":sfirst" and ":srewind".
2808 */
2809 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002810ex_rewind(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002811{
2812 do_argfile(eap, 0);
2813}
2814
2815/*
2816 * ":last" and ":slast".
2817 */
2818 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002819ex_last(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820{
2821 do_argfile(eap, ARGCOUNT - 1);
2822}
2823
2824/*
2825 * ":argument" and ":sargument".
2826 */
2827 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002828ex_argument(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002829{
2830 int i;
2831
2832 if (eap->addr_count > 0)
2833 i = eap->line2 - 1;
2834 else
2835 i = curwin->w_arg_idx;
2836 do_argfile(eap, i);
2837}
2838
2839/*
2840 * Edit file "argn" of the argument lists.
2841 */
2842 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002843do_argfile(exarg_T *eap, int argn)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844{
2845 int other;
2846 char_u *p;
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002847 int old_arg_idx = curwin->w_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002848
2849 if (argn < 0 || argn >= ARGCOUNT)
2850 {
2851 if (ARGCOUNT <= 1)
2852 EMSG(_("E163: There is only one file to edit"));
2853 else if (argn < 0)
2854 EMSG(_("E164: Cannot go before first file"));
2855 else
2856 EMSG(_("E165: Cannot go beyond last file"));
2857 }
2858 else
2859 {
2860 setpcmark();
2861#ifdef FEAT_GUI
2862 need_mouse_correct = TRUE;
2863#endif
2864
Bram Moolenaardf1bdc92006-02-23 21:32:16 +00002865 /* split window or create new tab page first */
2866 if (*eap->cmd == 's' || cmdmod.tab != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867 {
2868 if (win_split(0, 0) == FAIL)
2869 return;
Bram Moolenaar3368ea22010-09-21 16:56:35 +02002870 RESET_BINDING(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002871 }
2872 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873 {
2874 /*
2875 * if 'hidden' set, only check for changed file when re-editing
2876 * the same buffer
2877 */
2878 other = TRUE;
Bram Moolenaareb44a682017-08-03 22:44:55 +02002879 if (buf_hide(curbuf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002880 {
2881 p = fix_fname(alist_name(&ARGLIST[argn]));
2882 other = otherfile(p);
2883 vim_free(p);
2884 }
Bram Moolenaareb44a682017-08-03 22:44:55 +02002885 if ((!buf_hide(curbuf) || !other)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002886 && check_changed(curbuf, CCGD_AW
2887 | (other ? 0 : CCGD_MULTWIN)
2888 | (eap->forceit ? CCGD_FORCEIT : 0)
2889 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890 return;
2891 }
2892
2893 curwin->w_arg_idx = argn;
Bram Moolenaar4033c552017-09-16 20:54:51 +02002894 if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 arg_had_last = TRUE;
2896
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002897 /* Edit the file; always use the last known line number.
2898 * When it fails (e.g. Abort for already edited file) restore the
2899 * argument index. */
2900 if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 eap, ECMD_LAST,
Bram Moolenaareb44a682017-08-03 22:44:55 +02002902 (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
Bram Moolenaar701f7af2008-11-15 13:12:07 +00002903 + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL)
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002904 curwin->w_arg_idx = old_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 /* like Vi: set the mark where the cursor is in the file. */
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002906 else if (eap->cmdidx != CMD_argdo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002907 setmark('\'');
2908 }
2909}
2910
2911/*
2912 * ":next", and commands that behave like it.
2913 */
2914 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002915ex_next(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916{
2917 int i;
2918
2919 /*
2920 * check for changed buffer now, if this fails the argument list is not
2921 * redefined.
2922 */
Bram Moolenaareb44a682017-08-03 22:44:55 +02002923 if ( buf_hide(curbuf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002924 || eap->cmdidx == CMD_snext
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002925 || !check_changed(curbuf, CCGD_AW
2926 | (eap->forceit ? CCGD_FORCEIT : 0)
2927 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928 {
2929 if (*eap->arg != NUL) /* redefine file list */
2930 {
2931 if (do_arglist(eap->arg, AL_SET, 0) == FAIL)
2932 return;
2933 i = 0;
2934 }
2935 else
2936 i = curwin->w_arg_idx + (int)eap->line2;
2937 do_argfile(eap, i);
2938 }
2939}
2940
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941/*
2942 * ":argedit"
2943 */
2944 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002945ex_argedit(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002946{
Bram Moolenaar90305c62017-07-16 15:31:17 +02002947 int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
Bram Moolenaar46a53df2018-04-24 21:58:51 +02002948 // Whether curbuf will be reused, curbuf->b_ffname will be set.
2949 int curbuf_is_reusable = curbuf_reusable();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950
Bram Moolenaar90305c62017-07-16 15:31:17 +02002951 if (do_arglist(eap->arg, AL_ADD, i) == FAIL)
2952 return;
2953#ifdef FEAT_TITLE
2954 maketitle();
2955#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002956
Bram Moolenaar46a53df2018-04-24 21:58:51 +02002957 if (curwin->w_arg_idx == 0
2958 && (curbuf->b_ml.ml_flags & ML_EMPTY)
2959 && (curbuf->b_ffname == NULL || curbuf_is_reusable))
Bram Moolenaar90305c62017-07-16 15:31:17 +02002960 i = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002961 /* Edit the argument. */
Bram Moolenaar90305c62017-07-16 15:31:17 +02002962 if (i < ARGCOUNT)
2963 do_argfile(eap, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002964}
2965
2966/*
2967 * ":argadd"
2968 */
2969 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002970ex_argadd(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002971{
2972 do_arglist(eap->arg, AL_ADD,
2973 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
2974#ifdef FEAT_TITLE
2975 maketitle();
2976#endif
2977}
2978
2979/*
2980 * ":argdelete"
2981 */
2982 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002983ex_argdelete(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984{
2985 int i;
2986 int n;
2987
2988 if (eap->addr_count > 0)
2989 {
2990 /* ":1,4argdel": Delete all arguments in the range. */
2991 if (eap->line2 > ARGCOUNT)
2992 eap->line2 = ARGCOUNT;
2993 n = eap->line2 - eap->line1 + 1;
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002994 if (*eap->arg != NUL)
2995 /* Can't have both a range and an argument. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002996 EMSG(_(e_invarg));
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002997 else if (n <= 0)
2998 {
2999 /* Don't give an error for ":%argdel" if the list is empty. */
3000 if (eap->line1 != 1 || eap->line2 != 0)
3001 EMSG(_(e_invrange));
3002 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003003 else
3004 {
3005 for (i = eap->line1; i <= eap->line2; ++i)
3006 vim_free(ARGLIST[i - 1].ae_fname);
3007 mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
3008 (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
3009 ALIST(curwin)->al_ga.ga_len -= n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010 if (curwin->w_arg_idx >= eap->line2)
3011 curwin->w_arg_idx -= n;
3012 else if (curwin->w_arg_idx > eap->line1)
3013 curwin->w_arg_idx = eap->line1;
Bram Moolenaar72defda2016-01-17 18:04:33 +01003014 if (ARGCOUNT == 0)
3015 curwin->w_arg_idx = 0;
3016 else if (curwin->w_arg_idx >= ARGCOUNT)
3017 curwin->w_arg_idx = ARGCOUNT - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018 }
3019 }
3020 else if (*eap->arg == NUL)
3021 EMSG(_(e_argreq));
3022 else
3023 do_arglist(eap->arg, AL_DEL, 0);
3024#ifdef FEAT_TITLE
3025 maketitle();
3026#endif
3027}
3028
3029/*
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003030 * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003031 */
3032 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003033ex_listdo(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003034{
3035 int i;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003036 win_T *wp;
3037 tabpage_T *tp;
Bram Moolenaare25bb902015-02-27 20:33:37 +01003038 buf_T *buf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039 int next_fnum = 0;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003040#if defined(FEAT_SYN_HL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003041 char_u *save_ei = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003042#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003043 char_u *p_shm_save;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003044#ifdef FEAT_QUICKFIX
Bram Moolenaared84b762015-09-09 22:35:29 +02003045 int qf_size = 0;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003046 int qf_idx;
3047#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003048
Bram Moolenaar0106e3d2016-02-23 18:55:43 +01003049#ifndef FEAT_QUICKFIX
3050 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
3051 eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3052 {
3053 ex_ni(eap);
3054 return;
3055 }
3056#endif
3057
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003058#if defined(FEAT_SYN_HL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003059 if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00003060 /* Don't do syntax HL autocommands. Skipping the syntax file is a
3061 * great speed improvement. */
3062 save_ei = au_event_disable(",Syntax");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003063#endif
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02003064#ifdef FEAT_CLIPBOARD
3065 start_global_changes();
3066#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067
3068 if (eap->cmdidx == CMD_windo
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003069 || eap->cmdidx == CMD_tabdo
Bram Moolenaareb44a682017-08-03 22:44:55 +02003070 || buf_hide(curbuf)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01003071 || !check_changed(curbuf, CCGD_AW
3072 | (eap->forceit ? CCGD_FORCEIT : 0)
3073 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003074 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003075 i = 0;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003076 /* start at the eap->line1 argument/window/buffer */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003077 wp = firstwin;
3078 tp = first_tabpage;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003079 switch (eap->cmdidx)
3080 {
Bram Moolenaara162bc52015-01-07 16:54:21 +01003081 case CMD_windo:
3082 for ( ; wp != NULL && i + 1 < eap->line1; wp = wp->w_next)
3083 i++;
3084 break;
3085 case CMD_tabdo:
3086 for( ; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next)
3087 i++;
3088 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003089 case CMD_argdo:
3090 i = eap->line1 - 1;
3091 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003092 default:
3093 break;
3094 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095 /* set pcmark now */
3096 if (eap->cmdidx == CMD_bufdo)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003097 {
Bram Moolenaare25bb902015-02-27 20:33:37 +01003098 /* Advance to the first listed buffer after "eap->line1". */
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003099 for (buf = firstbuf; buf != NULL && (buf->b_fnum < eap->line1
Bram Moolenaare25bb902015-02-27 20:33:37 +01003100 || !buf->b_p_bl); buf = buf->b_next)
3101 if (buf->b_fnum > eap->line2)
3102 {
3103 buf = NULL;
3104 break;
3105 }
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003106 if (buf != NULL)
Bram Moolenaare25bb902015-02-27 20:33:37 +01003107 goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003108 }
3109#ifdef FEAT_QUICKFIX
3110 else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
3111 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3112 {
3113 qf_size = qf_get_size(eap);
3114 if (qf_size <= 0 || eap->line1 > qf_size)
3115 buf = NULL;
3116 else
3117 {
3118 ex_cc(eap);
3119
3120 buf = curbuf;
3121 i = eap->line1 - 1;
3122 if (eap->addr_count <= 0)
3123 /* default is all the quickfix/location list entries */
3124 eap->line2 = qf_size;
3125 }
3126 }
3127#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003128 else
3129 setpcmark();
3130 listcmd_busy = TRUE; /* avoids setting pcmark below */
3131
Bram Moolenaare25bb902015-02-27 20:33:37 +01003132 while (!got_int && buf != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003133 {
3134 if (eap->cmdidx == CMD_argdo)
3135 {
3136 /* go to argument "i" */
3137 if (i == ARGCOUNT)
3138 break;
3139 /* Don't call do_argfile() when already there, it will try
3140 * reloading the file. */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00003141 if (curwin->w_arg_idx != i || !editing_arg_idx(curwin))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003142 {
3143 /* Clear 'shm' to avoid that the file message overwrites
3144 * any output from the command. */
3145 p_shm_save = vim_strsave(p_shm);
3146 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003147 do_argfile(eap, i);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003148 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
3149 vim_free(p_shm_save);
3150 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003151 if (curwin->w_arg_idx != i)
3152 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003154 else if (eap->cmdidx == CMD_windo)
3155 {
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003156 /* go to window "wp" */
3157 if (!win_valid(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003158 break;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003159 win_goto(wp);
Bram Moolenaar41423242007-05-03 20:11:13 +00003160 if (curwin != wp)
3161 break; /* something must be wrong */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003162 wp = curwin->w_next;
3163 }
3164 else if (eap->cmdidx == CMD_tabdo)
3165 {
3166 /* go to window "tp" */
3167 if (!valid_tabpage(tp))
3168 break;
Bram Moolenaar49e649f2013-05-06 04:50:35 +02003169 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003170 tp = tp->tp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003172 else if (eap->cmdidx == CMD_bufdo)
3173 {
3174 /* Remember the number of the next listed buffer, in case
3175 * ":bwipe" is used or autocommands do something strange. */
3176 next_fnum = -1;
3177 for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
3178 if (buf->b_p_bl)
3179 {
3180 next_fnum = buf->b_fnum;
3181 break;
3182 }
3183 }
3184
Bram Moolenaara162bc52015-01-07 16:54:21 +01003185 ++i;
3186
Bram Moolenaar071d4272004-06-13 20:20:40 +00003187 /* execute the command */
3188 do_cmdline(eap->arg, eap->getline, eap->cookie,
3189 DOCMD_VERBOSE + DOCMD_NOWAIT);
3190
3191 if (eap->cmdidx == CMD_bufdo)
3192 {
3193 /* Done? */
Bram Moolenaara162bc52015-01-07 16:54:21 +01003194 if (next_fnum < 0 || next_fnum > eap->line2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003195 break;
3196 /* Check if the buffer still exists. */
Bram Moolenaar29323592016-07-24 22:04:11 +02003197 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198 if (buf->b_fnum == next_fnum)
3199 break;
3200 if (buf == NULL)
3201 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003202
3203 /* Go to the next buffer. Clear 'shm' to avoid that the file
3204 * message overwrites any output from the command. */
3205 p_shm_save = vim_strsave(p_shm);
3206 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003207 goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003208 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
3209 vim_free(p_shm_save);
3210
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003211 /* If autocommands took us elsewhere, quit here. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212 if (curbuf->b_fnum != next_fnum)
3213 break;
3214 }
3215
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003216#ifdef FEAT_QUICKFIX
3217 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
3218 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3219 {
3220 if (i >= qf_size || i >= eap->line2)
3221 break;
3222
3223 qf_idx = qf_get_cur_idx(eap);
3224
3225 ex_cnext(eap);
3226
3227 /* If jumping to the next quickfix entry fails, quit here */
3228 if (qf_get_cur_idx(eap) == qf_idx)
3229 break;
3230 }
3231#endif
3232
Bram Moolenaar071d4272004-06-13 20:20:40 +00003233 if (eap->cmdidx == CMD_windo)
3234 {
3235 validate_cursor(); /* cursor may have moved */
Bram Moolenaar8a3bb562018-03-04 20:14:14 +01003236
Bram Moolenaar071d4272004-06-13 20:20:40 +00003237 /* required when 'scrollbind' has been set */
3238 if (curwin->w_p_scb)
3239 do_check_scrollbind(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240 }
Bram Moolenaara162bc52015-01-07 16:54:21 +01003241
Bram Moolenaara162bc52015-01-07 16:54:21 +01003242 if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo)
3243 if (i+1 > eap->line2)
3244 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003245 if (eap->cmdidx == CMD_argdo && i >= eap->line2)
3246 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247 }
3248 listcmd_busy = FALSE;
3249 }
3250
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003251#if defined(FEAT_SYN_HL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003252 if (save_ei != NULL)
3253 {
3254 au_event_restore(save_ei);
3255 apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
3256 curbuf->b_fname, TRUE, curbuf);
3257 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258#endif
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02003259#ifdef FEAT_CLIPBOARD
3260 end_global_changes();
3261#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262}
3263
3264/*
3265 * Add files[count] to the arglist of the current window after arg "after".
3266 * The file names in files[count] must have been allocated and are taken over.
3267 * Files[] itself is not taken over.
3268 * Returns index of first added argument. Returns -1 when failed (out of mem).
3269 */
3270 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003271alist_add_list(
3272 int count,
3273 char_u **files,
3274 int after) /* where to add: 0 = before first one */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275{
3276 int i;
Bram Moolenaara24f0a52016-01-17 19:39:00 +01003277 int old_argcount = ARGCOUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278
3279 if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
3280 {
3281 if (after < 0)
3282 after = 0;
3283 if (after > ARGCOUNT)
3284 after = ARGCOUNT;
3285 if (after < ARGCOUNT)
3286 mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
3287 (ARGCOUNT - after) * sizeof(aentry_T));
3288 for (i = 0; i < count; ++i)
3289 {
3290 ARGLIST[after + i].ae_fname = files[i];
Bram Moolenaar46a53df2018-04-24 21:58:51 +02003291 ARGLIST[after + i].ae_fnum =
3292 buflist_add(files[i], BLN_LISTED | BLN_CURBUF);
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 Moolenaar071d4272004-06-13 20:20:40 +00003297 return after;
3298 }
3299
3300 for (i = 0; i < count; ++i)
3301 vim_free(files[i]);
3302 return -1;
3303}
3304
Bram Moolenaarcd43eff2018-03-29 15:55:38 +02003305#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
3306/*
3307 * Function given to ExpandGeneric() to obtain the possible arguments of the
3308 * argedit and argdelete commands.
3309 */
3310 char_u *
3311get_arglist_name(expand_T *xp UNUSED, int idx)
3312{
3313 if (idx >= ARGCOUNT)
3314 return NULL;
3315
3316 return alist_name(&ARGLIST[idx]);
3317}
3318#endif
3319
Bram Moolenaar0c72fe42018-03-29 16:04:08 +02003320
Bram Moolenaar071d4272004-06-13 20:20:40 +00003321#ifdef FEAT_EVAL
3322/*
3323 * ":compiler[!] {name}"
3324 */
3325 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003326ex_compiler(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003327{
3328 char_u *buf;
3329 char_u *old_cur_comp = NULL;
3330 char_u *p;
3331
3332 if (*eap->arg == NUL)
3333 {
3334 /* List all compiler scripts. */
3335 do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
3336 /* ) keep the indenter happy... */
3337 }
3338 else
3339 {
3340 buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
3341 if (buf != NULL)
3342 {
3343 if (eap->forceit)
3344 {
3345 /* ":compiler! {name}" sets global options */
3346 do_cmdline_cmd((char_u *)
3347 "command -nargs=* CompilerSet set <args>");
3348 }
3349 else
3350 {
3351 /* ":compiler! {name}" sets local options.
3352 * To remain backwards compatible "current_compiler" is always
3353 * used. A user's compiler plugin may set it, the distributed
3354 * plugin will then skip the settings. Afterwards set
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003355 * "b:current_compiler" and restore "current_compiler".
3356 * Explicitly prepend "g:" to make it work in a function. */
3357 old_cur_comp = get_var_value((char_u *)"g:current_compiler");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 if (old_cur_comp != NULL)
3359 old_cur_comp = vim_strsave(old_cur_comp);
3360 do_cmdline_cmd((char_u *)
3361 "command -nargs=* CompilerSet setlocal <args>");
3362 }
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003363 do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003364 do_unlet((char_u *)"b:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365
3366 sprintf((char *)buf, "compiler/%s.vim", eap->arg);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003367 if (source_runtime(buf, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 EMSG2(_("E666: compiler not supported: %s"), eap->arg);
3369 vim_free(buf);
3370
3371 do_cmdline_cmd((char_u *)":delcommand CompilerSet");
3372
3373 /* Set "b:current_compiler" from "current_compiler". */
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003374 p = get_var_value((char_u *)"g:current_compiler");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003375 if (p != NULL)
3376 set_internal_string_var((char_u *)"b:current_compiler", p);
3377
3378 /* Restore "current_compiler" for ":compiler {name}". */
3379 if (!eap->forceit)
3380 {
3381 if (old_cur_comp != NULL)
3382 {
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003383 set_internal_string_var((char_u *)"g:current_compiler",
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384 old_cur_comp);
3385 vim_free(old_cur_comp);
3386 }
3387 else
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003388 do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389 }
3390 }
3391 }
3392}
3393#endif
3394
3395/*
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003396 * ":runtime [what] {name}"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397 */
3398 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003399ex_runtime(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400{
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003401 char_u *arg = eap->arg;
3402 char_u *p = skiptowhite(arg);
3403 int len = (int)(p - arg);
3404 int flags = eap->forceit ? DIP_ALL : 0;
3405
3406 if (STRNCMP(arg, "START", len) == 0)
3407 {
3408 flags += DIP_START + DIP_NORTP;
3409 arg = skipwhite(arg + len);
3410 }
3411 else if (STRNCMP(arg, "OPT", len) == 0)
3412 {
3413 flags += DIP_OPT + DIP_NORTP;
3414 arg = skipwhite(arg + len);
3415 }
3416 else if (STRNCMP(arg, "PACK", len) == 0)
3417 {
3418 flags += DIP_START + DIP_OPT + DIP_NORTP;
3419 arg = skipwhite(arg + len);
3420 }
3421 else if (STRNCMP(arg, "ALL", len) == 0)
3422 {
3423 flags += DIP_START + DIP_OPT;
3424 arg = skipwhite(arg + len);
3425 }
3426
3427 source_runtime(arg, flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428}
3429
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003431source_callback(char_u *fname, void *cookie UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432{
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003433 (void)do_source(fname, FALSE, DOSO_NONE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003434}
3435
3436/*
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003437 * Find the file "name" in all directories in "path" and invoke
3438 * "callback(fname, cookie)".
3439 * "name" can contain wildcards.
3440 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
3441 * When "flags" has DIP_DIR: find directories instead of files.
3442 * When "flags" has DIP_ERR: give an error message if there is no match.
3443 *
3444 * return FAIL when no file could be sourced, OK otherwise.
3445 */
Bram Moolenaar6bef5302016-03-12 21:28:26 +01003446 int
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003447do_in_path(
3448 char_u *path,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003449 char_u *name,
Bram Moolenaar91715872016-03-03 17:13:03 +01003450 int flags,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003451 void (*callback)(char_u *fname, void *ck),
3452 void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453{
3454 char_u *rtp;
3455 char_u *np;
3456 char_u *buf;
3457 char_u *rtp_copy;
3458 char_u *tail;
3459 int num_files;
3460 char_u **files;
3461 int i;
3462 int did_one = FALSE;
3463#ifdef AMIGA
3464 struct Process *proc = (struct Process *)FindTask(0L);
3465 APTR save_winptr = proc->pr_WindowPtr;
3466
3467 /* Avoid a requester here for a volume that doesn't exist. */
3468 proc->pr_WindowPtr = (APTR)-1L;
3469#endif
3470
3471 /* Make a copy of 'runtimepath'. Invoking the callback may change the
3472 * value. */
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003473 rtp_copy = vim_strsave(path);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474 buf = alloc(MAXPATHL);
3475 if (buf != NULL && rtp_copy != NULL)
3476 {
Bram Moolenaarc09a6d62013-06-10 21:27:29 +02003477 if (p_verbose > 1 && name != NULL)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003478 {
3479 verbose_enter();
Bram Moolenaar555b2802005-05-19 21:08:39 +00003480 smsg((char_u *)_("Searching for \"%s\" in \"%s\""),
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003481 (char *)name, (char *)path);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003482 verbose_leave();
3483 }
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00003484
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485 /* Loop over all entries in 'runtimepath'. */
3486 rtp = rtp_copy;
Bram Moolenaar91715872016-03-03 17:13:03 +01003487 while (*rtp != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003488 {
Bram Moolenaar66459b72016-08-06 19:01:55 +02003489 size_t buflen;
3490
Bram Moolenaar071d4272004-06-13 20:20:40 +00003491 /* Copy the path from 'runtimepath' to buf[]. */
3492 copy_option_part(&rtp, buf, MAXPATHL, ",");
Bram Moolenaar66459b72016-08-06 19:01:55 +02003493 buflen = STRLEN(buf);
3494
3495 /* Skip after or non-after directories. */
3496 if (flags & (DIP_NOAFTER | DIP_AFTER))
3497 {
3498 int is_after = buflen >= 5
3499 && STRCMP(buf + buflen - 5, "after") == 0;
3500
3501 if ((is_after && (flags & DIP_NOAFTER))
3502 || (!is_after && (flags & DIP_AFTER)))
3503 continue;
3504 }
3505
Bram Moolenaarc09a6d62013-06-10 21:27:29 +02003506 if (name == NULL)
3507 {
3508 (*callback)(buf, (void *) &cookie);
3509 if (!did_one)
3510 did_one = (cookie == NULL);
3511 }
Bram Moolenaar66459b72016-08-06 19:01:55 +02003512 else if (buflen + STRLEN(name) + 2 < MAXPATHL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513 {
3514 add_pathsep(buf);
3515 tail = buf + STRLEN(buf);
3516
3517 /* Loop over all patterns in "name" */
3518 np = name;
Bram Moolenaar91715872016-03-03 17:13:03 +01003519 while (*np != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003520 {
3521 /* Append the pattern from "name" to buf[]. */
3522 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
3523 "\t ");
3524
3525 if (p_verbose > 2)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003526 {
3527 verbose_enter();
Bram Moolenaar555b2802005-05-19 21:08:39 +00003528 smsg((char_u *)_("Searching for \"%s\""), buf);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003529 verbose_leave();
3530 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003531
3532 /* Expand wildcards, invoke the callback for each match. */
3533 if (gen_expand_wildcards(1, &buf, &num_files, &files,
Bram Moolenaar91715872016-03-03 17:13:03 +01003534 (flags & DIP_DIR) ? EW_DIR : EW_FILE) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003535 {
3536 for (i = 0; i < num_files; ++i)
3537 {
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00003538 (*callback)(files[i], cookie);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003539 did_one = TRUE;
Bram Moolenaar91715872016-03-03 17:13:03 +01003540 if (!(flags & DIP_ALL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003541 break;
3542 }
3543 FreeWild(num_files, files);
3544 }
3545 }
3546 }
3547 }
3548 }
3549 vim_free(buf);
3550 vim_free(rtp_copy);
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003551 if (!did_one && name != NULL)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003552 {
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003553 char *basepath = path == p_rtp ? "runtimepath" : "packpath";
3554
3555 if (flags & DIP_ERR)
3556 EMSG3(_(e_dirnotf), basepath, name);
3557 else if (p_verbose > 0)
3558 {
3559 verbose_enter();
3560 smsg((char_u *)_("not found in '%s': \"%s\""), basepath, name);
3561 verbose_leave();
3562 }
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003563 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003564
3565#ifdef AMIGA
3566 proc->pr_WindowPtr = save_winptr;
3567#endif
3568
3569 return did_one ? OK : FAIL;
3570}
3571
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003572/*
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003573 * Find "name" in "path". When found, invoke the callback function for
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003574 * it: callback(fname, "cookie")
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003575 * When "flags" has DIP_ALL repeat for all matches, otherwise only the first
3576 * one is used.
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003577 * Returns OK when at least one match found, FAIL otherwise.
3578 *
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003579 * If "name" is NULL calls callback for each entry in "path". Cookie is
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003580 * passed by reference in this case, setting it to NULL indicates that callback
3581 * has done its job.
3582 */
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003583 static int
3584do_in_path_and_pp(
3585 char_u *path,
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003586 char_u *name,
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003587 int flags,
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003588 void (*callback)(char_u *fname, void *ck),
3589 void *cookie)
3590{
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003591 int done = FAIL;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003592 char_u *s;
3593 int len;
3594 char *start_dir = "pack/*/start/*/%s";
3595 char *opt_dir = "pack/*/opt/*/%s";
3596
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003597 if ((flags & DIP_NORTP) == 0)
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003598 done = do_in_path(path, name, flags, callback, cookie);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003599
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003600 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START))
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003601 {
Bram Moolenaar1c8b4ed2016-03-17 21:51:03 +01003602 len = (int)(STRLEN(start_dir) + STRLEN(name));
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003603 s = alloc(len);
3604 if (s == NULL)
3605 return FAIL;
3606 vim_snprintf((char *)s, len, start_dir, name);
3607 done = do_in_path(p_pp, s, flags, callback, cookie);
3608 vim_free(s);
3609 }
3610
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003611 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT))
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003612 {
Bram Moolenaar1c8b4ed2016-03-17 21:51:03 +01003613 len = (int)(STRLEN(opt_dir) + STRLEN(name));
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003614 s = alloc(len);
3615 if (s == NULL)
3616 return FAIL;
3617 vim_snprintf((char *)s, len, opt_dir, name);
3618 done = do_in_path(p_pp, s, flags, callback, cookie);
3619 vim_free(s);
3620 }
3621
3622 return done;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003623}
3624
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003625/*
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003626 * Just like do_in_path_and_pp(), using 'runtimepath' for "path".
3627 */
3628 int
3629do_in_runtimepath(
3630 char_u *name,
3631 int flags,
3632 void (*callback)(char_u *fname, void *ck),
3633 void *cookie)
3634{
3635 return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
3636}
3637
3638/*
3639 * Source the file "name" from all directories in 'runtimepath'.
3640 * "name" can contain wildcards.
3641 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
3642 *
3643 * return FAIL when no file could be sourced, OK otherwise.
3644 */
3645 int
3646source_runtime(char_u *name, int flags)
3647{
3648 return source_in_path(p_rtp, name, flags);
3649}
3650
3651/*
3652 * Just like source_runtime(), but use "path" instead of 'runtimepath'.
3653 */
3654 int
3655source_in_path(char_u *path, char_u *name, int flags)
3656{
3657 return do_in_path_and_pp(path, name, flags, source_callback, NULL);
3658}
3659
3660
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003661#if defined(FEAT_EVAL) || defined(PROTO)
3662
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003663/*
Bram Moolenaarf3654822016-03-04 22:12:23 +01003664 * Expand wildcards in "pat" and invoke do_source() for each match.
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003665 */
3666 static void
Bram Moolenaarf3654822016-03-04 22:12:23 +01003667source_all_matches(char_u *pat)
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003668{
Bram Moolenaarf3654822016-03-04 22:12:23 +01003669 int num_files;
3670 char_u **files;
3671 int i;
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003672
Bram Moolenaarf3654822016-03-04 22:12:23 +01003673 if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK)
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003674 {
Bram Moolenaarf3654822016-03-04 22:12:23 +01003675 for (i = 0; i < num_files; ++i)
3676 (void)do_source(files[i], FALSE, DOSO_NONE);
3677 FreeWild(num_files, files);
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003678 }
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003679}
3680
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003681/*
3682 * Add the package directory to 'runtimepath'.
3683 */
3684 static int
3685add_pack_dir_to_rtp(char_u *fname)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003686{
Bram Moolenaarf3654822016-03-04 22:12:23 +01003687 char_u *p4, *p3, *p2, *p1, *p;
3688 char_u *insp;
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 Moolenaar2374faa2018-02-04 17:47:42 +01003694 char_u *afterdir = NULL;
Bram Moolenaarb0550662016-05-31 21:37:36 +02003695 size_t afterlen = 0;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003696 char_u *ffname = NULL;
Bram Moolenaarfef524b2016-07-02 22:07:22 +02003697 size_t fname_len;
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01003698 char_u *buf = NULL;
3699 char_u *rtp_ffname;
3700 int match;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003701 int retval = FAIL;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003702
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003703 p4 = p3 = p2 = p1 = get_past_head(fname);
3704 for (p = p1; *p; MB_PTR_ADV(p))
3705 if (vim_ispathsep_nocolon(*p))
3706 {
3707 p4 = p3; p3 = p2; p2 = p1; p1 = p;
3708 }
3709
3710 /* now we have:
3711 * rtp/pack/name/start/name
3712 * p4 p3 p2 p1
3713 *
3714 * find the part up to "pack" in 'runtimepath' */
3715 c = *++p4; /* append pathsep in order to expand symlink */
3716 *p4 = NUL;
3717 ffname = fix_fname(fname);
3718 *p4 = c;
Bram Moolenaar91715872016-03-03 17:13:03 +01003719 if (ffname == NULL)
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003720 return FAIL;
3721
3722 /* Find "ffname" in "p_rtp", ignoring '/' vs '\' differences. */
3723 fname_len = STRLEN(ffname);
3724 insp = p_rtp;
3725 buf = alloc(MAXPATHL);
3726 if (buf == NULL)
3727 goto theend;
3728 while (*insp != NUL)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003729 {
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003730 copy_option_part(&insp, buf, MAXPATHL, ",");
3731 add_pathsep(buf);
3732 rtp_ffname = fix_fname(buf);
3733 if (rtp_ffname == NULL)
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01003734 goto theend;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003735 match = vim_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
3736 vim_free(rtp_ffname);
3737 if (match)
3738 break;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003739 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003740
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003741 if (*insp == NUL)
3742 /* not found, append at the end */
3743 insp = p_rtp + STRLEN(p_rtp);
3744 else
3745 /* append after the matching directory. */
3746 --insp;
3747
3748 /* check if rtp/pack/name/start/name/after exists */
3749 afterdir = concat_fnames(fname, (char_u *)"after", TRUE);
3750 if (afterdir != NULL && mch_isdir(afterdir))
3751 afterlen = STRLEN(afterdir) + 1; /* add one for comma */
3752
3753 oldlen = STRLEN(p_rtp);
3754 addlen = STRLEN(fname) + 1; /* add one for comma */
3755 new_rtp = alloc((int)(oldlen + addlen + afterlen + 1));
3756 /* add one for NUL */
3757 if (new_rtp == NULL)
3758 goto theend;
3759 keep = (int)(insp - p_rtp);
3760 mch_memmove(new_rtp, p_rtp, keep);
3761 new_rtp[keep] = ',';
3762 mch_memmove(new_rtp + keep + 1, fname, addlen);
3763 if (p_rtp[keep] != NUL)
3764 mch_memmove(new_rtp + keep + addlen, p_rtp + keep, oldlen - keep + 1);
3765 if (afterlen > 0)
Bram Moolenaarf3654822016-03-04 22:12:23 +01003766 {
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003767 STRCAT(new_rtp, ",");
3768 STRCAT(new_rtp, afterdir);
Bram Moolenaarf3654822016-03-04 22:12:23 +01003769 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003770 set_option_value((char_u *)"rtp", 0L, new_rtp, 0);
3771 vim_free(new_rtp);
3772 retval = OK;
Bram Moolenaarf3654822016-03-04 22:12:23 +01003773
3774theend:
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01003775 vim_free(buf);
Bram Moolenaarf3654822016-03-04 22:12:23 +01003776 vim_free(ffname);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003777 vim_free(afterdir);
3778 return retval;
3779}
3780
3781/*
3782 * Load scripts in "plugin" and "ftdetect" directories of the package.
3783 */
3784 static int
3785load_pack_plugin(char_u *fname)
3786{
3787 static char *plugpat = "%s/plugin/**/*.vim";
3788 static char *ftpat = "%s/ftdetect/*.vim";
3789 int len;
3790 char_u *ffname = fix_fname(fname);
3791 char_u *pat = NULL;
3792 int retval = FAIL;
3793
3794 if (ffname == NULL)
3795 return FAIL;
3796 len = (int)STRLEN(ffname) + (int)STRLEN(ftpat);
3797 pat = alloc(len);
3798 if (pat == NULL)
3799 goto theend;
3800 vim_snprintf((char *)pat, len, plugpat, ffname);
3801 source_all_matches(pat);
3802
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003803 {
3804 char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes");
3805
3806 /* If runtime/filetype.vim wasn't loaded yet, the scripts will be
3807 * found when it loads. */
3808 if (cmd != NULL && eval_to_number(cmd) > 0)
3809 {
3810 do_cmdline_cmd((char_u *)"augroup filetypedetect");
3811 vim_snprintf((char *)pat, len, ftpat, ffname);
3812 source_all_matches(pat);
3813 do_cmdline_cmd((char_u *)"augroup END");
3814 }
3815 vim_free(cmd);
3816 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003817 vim_free(pat);
3818 retval = OK;
3819
3820theend:
3821 vim_free(ffname);
3822 return retval;
3823}
3824
3825/* used for "cookie" of add_pack_plugin() */
3826static int APP_ADD_DIR;
3827static int APP_LOAD;
3828static int APP_BOTH;
3829
3830 static void
3831add_pack_plugin(char_u *fname, void *cookie)
3832{
Bram Moolenaarf98a39c2018-04-18 22:18:23 +02003833 if (cookie != &APP_LOAD)
3834 {
3835 char_u *buf = alloc(MAXPATHL);
3836 char_u *p;
3837 int found = FALSE;
3838
3839 if (buf == NULL)
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003840 return;
Bram Moolenaarf98a39c2018-04-18 22:18:23 +02003841 p = p_rtp;
3842 while (*p != NUL)
3843 {
3844 copy_option_part(&p, buf, MAXPATHL, ",");
3845 if (pathcmp((char *)buf, (char *)fname, -1) == 0)
3846 {
3847 found = TRUE;
3848 break;
3849 }
3850 }
3851 vim_free(buf);
3852 if (!found)
3853 /* directory is not yet in 'runtimepath', add it */
3854 if (add_pack_dir_to_rtp(fname) == FAIL)
3855 return;
3856 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003857
3858 if (cookie != &APP_ADD_DIR)
3859 load_pack_plugin(fname);
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003860}
3861
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003862/*
3863 * Add all packages in the "start" directory to 'runtimepath'.
3864 */
3865 void
3866add_pack_start_dirs(void)
3867{
3868 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
3869 add_pack_plugin, &APP_ADD_DIR);
3870}
3871
3872/*
3873 * Load plugins from all packages in the "start" directory.
3874 */
3875 void
3876load_start_packages(void)
3877{
3878 did_source_packages = TRUE;
3879 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
3880 add_pack_plugin, &APP_LOAD);
3881}
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003882
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003883/*
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003884 * ":packloadall"
Bram Moolenaarf3654822016-03-04 22:12:23 +01003885 * Find plugins in the package directories and source them.
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003886 */
3887 void
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003888ex_packloadall(exarg_T *eap)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003889{
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003890 if (!did_source_packages || eap->forceit)
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003891 {
Bram Moolenaar49b27322016-04-05 21:13:00 +02003892 /* First do a round to add all directories to 'runtimepath', then load
3893 * the plugins. This allows for plugins to use an autoload directory
3894 * of another plugin. */
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003895 add_pack_start_dirs();
3896 load_start_packages();
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003897 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003898}
3899
3900/*
Bram Moolenaarf3654822016-03-04 22:12:23 +01003901 * ":packadd[!] {name}"
Bram Moolenaar91715872016-03-03 17:13:03 +01003902 */
3903 void
3904ex_packadd(exarg_T *eap)
3905{
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003906 static char *plugpat = "pack/*/%s/%s";
Bram Moolenaar91715872016-03-03 17:13:03 +01003907 int len;
3908 char *pat;
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003909 int round;
3910 int res = OK;
Bram Moolenaar91715872016-03-03 17:13:03 +01003911
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003912 /* Round 1: use "start", round 2: use "opt". */
3913 for (round = 1; round <= 2; ++round)
3914 {
3915 /* Only look under "start" when loading packages wasn't done yet. */
3916 if (round == 1 && did_source_packages)
3917 continue;
3918
3919 len = (int)STRLEN(plugpat) + (int)STRLEN(eap->arg) + 5;
3920 pat = (char *)alloc(len);
3921 if (pat == NULL)
3922 return;
3923 vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
3924 /* The first round don't give a "not found" error, in the second round
3925 * only when nothing was found in the first round. */
3926 res = do_in_path(p_pp, (char_u *)pat,
3927 DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
3928 add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
3929 vim_free(pat);
3930 }
Bram Moolenaar91715872016-03-03 17:13:03 +01003931}
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003932#endif
Bram Moolenaar91715872016-03-03 17:13:03 +01003933
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003934#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935/*
3936 * ":options"
3937 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003938 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003939ex_options(
3940 exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941{
Bram Moolenaarab6c8582017-08-11 17:15:09 +02003942 vim_setenv((char_u *)"OPTWIN_CMD", (char_u *)(cmdmod.tab ? "tab" : ""));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003943 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
3944}
3945#endif
3946
Bram Moolenaarf42dd3c2017-01-28 16:06:38 +01003947#if defined(FEAT_PYTHON3) || defined(FEAT_PYTHON) || defined(PROTO)
3948
3949# if (defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)) || defined(PROTO)
3950/*
3951 * Detect Python 3 or 2, and initialize 'pyxversion'.
3952 */
3953 void
3954init_pyxversion(void)
3955{
3956 if (p_pyx == 0)
3957 {
3958 if (python3_enabled(FALSE))
3959 p_pyx = 3;
3960 else if (python_enabled(FALSE))
3961 p_pyx = 2;
3962 }
3963}
3964# endif
3965
3966/*
3967 * Does a file contain one of the following strings at the beginning of any
3968 * line?
3969 * "#!(any string)python2" => returns 2
3970 * "#!(any string)python3" => returns 3
3971 * "# requires python 2.x" => returns 2
3972 * "# requires python 3.x" => returns 3
3973 * otherwise return 0.
3974 */
3975 static int
3976requires_py_version(char_u *filename)
3977{
3978 FILE *file;
3979 int requires_py_version = 0;
3980 int i, lines;
3981
3982 lines = (int)p_mls;
3983 if (lines < 0)
3984 lines = 5;
3985
3986 file = mch_fopen((char *)filename, "r");
3987 if (file != NULL)
3988 {
3989 for (i = 0; i < lines; i++)
3990 {
3991 if (vim_fgets(IObuff, IOSIZE, file))
3992 break;
3993 if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!')
3994 {
3995 /* Check shebang. */
3996 if (strstr((char *)IObuff + 2, "python2") != NULL)
3997 {
3998 requires_py_version = 2;
3999 break;
4000 }
4001 if (strstr((char *)IObuff + 2, "python3") != NULL)
4002 {
4003 requires_py_version = 3;
4004 break;
4005 }
4006 }
4007 IObuff[21] = '\0';
4008 if (STRCMP("# requires python 2.x", IObuff) == 0)
4009 {
4010 requires_py_version = 2;
4011 break;
4012 }
4013 if (STRCMP("# requires python 3.x", IObuff) == 0)
4014 {
4015 requires_py_version = 3;
4016 break;
4017 }
4018 }
4019 fclose(file);
4020 }
4021 return requires_py_version;
4022}
4023
4024
4025/*
4026 * Source a python file using the requested python version.
4027 */
4028 static void
4029source_pyx_file(exarg_T *eap, char_u *fname)
4030{
4031 exarg_T ex;
4032 int v = requires_py_version(fname);
4033
4034# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4035 init_pyxversion();
4036# endif
4037 if (v == 0)
4038 {
4039# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4040 /* user didn't choose a preference, 'pyx' is used */
4041 v = p_pyx;
4042# elif defined(FEAT_PYTHON)
4043 v = 2;
4044# elif defined(FEAT_PYTHON3)
4045 v = 3;
4046# endif
4047 }
4048
4049 /*
4050 * now source, if required python version is not supported show
4051 * unobtrusive message.
4052 */
4053 if (eap == NULL)
4054 vim_memset(&ex, 0, sizeof(ex));
4055 else
4056 ex = *eap;
4057 ex.arg = fname;
4058 ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3");
4059
4060 if (v == 2)
4061 {
4062# ifdef FEAT_PYTHON
4063 ex_pyfile(&ex);
4064# else
4065 vim_snprintf((char *)IObuff, IOSIZE,
4066 _("W20: Required python version 2.x not supported, ignoring file: %s"),
4067 fname);
4068 MSG(IObuff);
4069# endif
4070 return;
4071 }
4072 else
4073 {
4074# ifdef FEAT_PYTHON3
4075 ex_py3file(&ex);
4076# else
4077 vim_snprintf((char *)IObuff, IOSIZE,
4078 _("W21: Required python version 3.x not supported, ignoring file: %s"),
4079 fname);
4080 MSG(IObuff);
4081# endif
4082 return;
4083 }
4084}
4085
4086/*
4087 * ":pyxfile {fname}"
4088 */
4089 void
4090ex_pyxfile(exarg_T *eap)
4091{
4092 source_pyx_file(eap, eap->arg);
4093}
4094
4095/*
4096 * ":pyx"
4097 */
4098 void
4099ex_pyx(exarg_T *eap)
4100{
4101# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4102 init_pyxversion();
4103 if (p_pyx == 2)
4104 ex_python(eap);
4105 else
4106 ex_py3(eap);
4107# elif defined(FEAT_PYTHON)
4108 ex_python(eap);
4109# elif defined(FEAT_PYTHON3)
4110 ex_py3(eap);
4111# endif
4112}
4113
4114/*
4115 * ":pyxdo"
4116 */
4117 void
4118ex_pyxdo(exarg_T *eap)
4119{
4120# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4121 init_pyxversion();
4122 if (p_pyx == 2)
4123 ex_pydo(eap);
4124 else
4125 ex_py3do(eap);
4126# elif defined(FEAT_PYTHON)
4127 ex_pydo(eap);
4128# elif defined(FEAT_PYTHON3)
4129 ex_py3do(eap);
4130# endif
4131}
4132
4133#endif
4134
Bram Moolenaar071d4272004-06-13 20:20:40 +00004135/*
4136 * ":source {fname}"
4137 */
4138 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004139ex_source(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140{
4141#ifdef FEAT_BROWSE
4142 if (cmdmod.browse)
4143 {
4144 char_u *fname = NULL;
4145
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00004146 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg,
Bram Moolenaarc36651b2018-04-29 12:22:56 +02004147 NULL, NULL,
4148 (char_u *)_(BROWSE_FILTER_MACROS), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004149 if (fname != NULL)
4150 {
4151 cmd_source(fname, eap);
4152 vim_free(fname);
4153 }
4154 }
4155 else
4156#endif
4157 cmd_source(eap->arg, eap);
4158}
4159
4160 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004161cmd_source(char_u *fname, exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162{
4163 if (*fname == NUL)
4164 EMSG(_(e_argreq));
4165
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 else if (eap != NULL && eap->forceit)
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02004167 /* ":source!": read Normal mode commands
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004168 * Need to execute the commands directly. This is required at least
4169 * for:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 * - ":g" command busy
4171 * - after ":argdo", ":windo" or ":bufdo"
4172 * - another command follows
4173 * - inside a loop
4174 */
4175 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
4176#ifdef FEAT_EVAL
4177 || eap->cstack->cs_idx >= 0
4178#endif
4179 );
4180
4181 /* ":source" read ex commands */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004182 else if (do_source(fname, FALSE, DOSO_NONE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 EMSG2(_(e_notopen), fname);
4184}
4185
4186/*
4187 * ":source" and associated commands.
4188 */
4189/*
4190 * Structure used to store info for each sourced file.
4191 * It is shared between do_source() and getsourceline().
4192 * This is required, because it needs to be handed to do_cmdline() and
4193 * sourcing can be done recursively.
4194 */
4195struct source_cookie
4196{
4197 FILE *fp; /* opened file for sourcing */
4198 char_u *nextline; /* if not NULL: line that was read ahead */
4199 int finished; /* ":finish" used */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004200#if defined(USE_CRNL) || defined(USE_CR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
4202 int error; /* TRUE if LF found after CR-LF */
4203#endif
4204#ifdef FEAT_EVAL
4205 linenr_T breakpoint; /* next line with breakpoint or zero */
4206 char_u *fname; /* name of sourced file */
4207 int dbg_tick; /* debug_tick when breakpoint was set */
4208 int level; /* top nesting level of sourced file */
4209#endif
4210#ifdef FEAT_MBYTE
4211 vimconv_T conv; /* type of conversion */
4212#endif
4213};
4214
4215#ifdef FEAT_EVAL
4216/*
4217 * Return the address holding the next breakpoint line for a source cookie.
4218 */
4219 linenr_T *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004220source_breakpoint(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221{
4222 return &((struct source_cookie *)cookie)->breakpoint;
4223}
4224
4225/*
4226 * Return the address holding the debug tick for a source cookie.
4227 */
4228 int *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004229source_dbg_tick(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004230{
4231 return &((struct source_cookie *)cookie)->dbg_tick;
4232}
4233
4234/*
4235 * Return the nesting level for a source cookie.
4236 */
4237 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004238source_level(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004239{
4240 return ((struct source_cookie *)cookie)->level;
4241}
4242#endif
4243
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01004244static char_u *get_one_sourceline(struct source_cookie *sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004246#if (defined(WIN32) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC)
4247# define USE_FOPEN_NOINH
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01004248static FILE *fopen_noinh_readbin(char *filename);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249
4250/*
4251 * Special function to open a file without handle inheritance.
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004252 * When possible the handle is closed on exec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253 */
4254 static FILE *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004255fopen_noinh_readbin(char *filename)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004256{
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004257# ifdef WIN32
Bram Moolenaar8d8ef0b2010-01-20 21:41:47 +01004258 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
4259# else
4260 int fd_tmp = mch_open(filename, O_RDONLY, 0);
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004261# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262
4263 if (fd_tmp == -1)
4264 return NULL;
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004265
4266# ifdef HAVE_FD_CLOEXEC
4267 {
4268 int fdflags = fcntl(fd_tmp, F_GETFD);
4269 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarcde88542015-08-11 19:14:00 +02004270 (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004271 }
4272# endif
4273
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 return fdopen(fd_tmp, READBIN);
4275}
4276#endif
4277
4278
4279/*
4280 * do_source: Read the file "fname" and execute its lines as EX commands.
4281 *
4282 * This function may be called recursively!
4283 *
4284 * return FAIL if file could not be opened, OK otherwise
4285 */
4286 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004287do_source(
4288 char_u *fname,
4289 int check_other, /* check for .vimrc and _vimrc */
4290 int is_vimrc) /* DOSO_ value */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291{
4292 struct source_cookie cookie;
4293 char_u *save_sourcing_name;
4294 linenr_T save_sourcing_lnum;
4295 char_u *p;
4296 char_u *fname_exp;
Bram Moolenaar73881402009-02-04 16:50:47 +00004297 char_u *firstline = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298 int retval = FAIL;
4299#ifdef FEAT_EVAL
4300 scid_T save_current_SID;
4301 static scid_T last_current_SID = 0;
4302 void *save_funccalp;
4303 int save_debug_break_level = debug_break_level;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004304 scriptitem_T *si = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305# ifdef UNIX
Bram Moolenaar8767f522016-07-01 17:17:39 +02004306 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004307 int stat_ok;
4308# endif
4309#endif
4310#ifdef STARTUPTIME
4311 struct timeval tv_rel;
4312 struct timeval tv_start;
4313#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00004314#ifdef FEAT_PROFILE
4315 proftime_T wait_start;
4316#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004317
Bram Moolenaar071d4272004-06-13 20:20:40 +00004318 p = expand_env_save(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319 if (p == NULL)
4320 return retval;
4321 fname_exp = fix_fname(p);
4322 vim_free(p);
4323 if (fname_exp == NULL)
4324 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004325 if (mch_isdir(fname_exp))
4326 {
Bram Moolenaar555b2802005-05-19 21:08:39 +00004327 smsg((char_u *)_("Cannot source a directory: \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328 goto theend;
4329 }
4330
Bram Moolenaar8dd1aa52007-01-16 20:33:19 +00004331 /* Apply SourceCmd autocommands, they should get the file and source it. */
4332 if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
4333 && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
4334 FALSE, curbuf))
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004335 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004336#ifdef FEAT_EVAL
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004337 retval = aborting() ? FAIL : OK;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004338#else
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004339 retval = OK;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004340#endif
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004341 goto theend;
4342 }
Bram Moolenaar8dd1aa52007-01-16 20:33:19 +00004343
4344 /* Apply SourcePre autocommands, they may get the file. */
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004345 apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf);
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004346
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004347#ifdef USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
4349#else
4350 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
4351#endif
4352 if (cookie.fp == NULL && check_other)
4353 {
4354 /*
4355 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
4356 * and ".exrc" by "_exrc" or vice versa.
4357 */
4358 p = gettail(fname_exp);
4359 if ((*p == '.' || *p == '_')
4360 && (STRICMP(p + 1, "vimrc") == 0
4361 || STRICMP(p + 1, "gvimrc") == 0
4362 || STRICMP(p + 1, "exrc") == 0))
4363 {
4364 if (*p == '_')
4365 *p = '.';
4366 else
4367 *p = '_';
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004368#ifdef USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
4370#else
4371 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
4372#endif
4373 }
4374 }
4375
4376 if (cookie.fp == NULL)
4377 {
4378 if (p_verbose > 0)
4379 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004380 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 if (sourcing_name == NULL)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004382 smsg((char_u *)_("could not source \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 else
4384 smsg((char_u *)_("line %ld: could not source \"%s\""),
Bram Moolenaar555b2802005-05-19 21:08:39 +00004385 sourcing_lnum, fname);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004386 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 }
4388 goto theend;
4389 }
4390
4391 /*
4392 * The file exists.
4393 * - In verbose mode, give a message.
4394 * - For a vimrc file, may want to set 'compatible', call vimrc_found().
4395 */
4396 if (p_verbose > 1)
4397 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004398 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004399 if (sourcing_name == NULL)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004400 smsg((char_u *)_("sourcing \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401 else
4402 smsg((char_u *)_("line %ld: sourcing \"%s\""),
Bram Moolenaar555b2802005-05-19 21:08:39 +00004403 sourcing_lnum, fname);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004404 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004405 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004406 if (is_vimrc == DOSO_VIMRC)
4407 vimrc_found(fname_exp, (char_u *)"MYVIMRC");
4408 else if (is_vimrc == DOSO_GVIMRC)
4409 vimrc_found(fname_exp, (char_u *)"MYGVIMRC");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004410
4411#ifdef USE_CRNL
4412 /* If no automatic file format: Set default to CR-NL. */
4413 if (*p_ffs == NUL)
4414 cookie.fileformat = EOL_DOS;
4415 else
4416 cookie.fileformat = EOL_UNKNOWN;
4417 cookie.error = FALSE;
4418#endif
4419
4420#ifdef USE_CR
4421 /* If no automatic file format: Set default to CR. */
4422 if (*p_ffs == NUL)
4423 cookie.fileformat = EOL_MAC;
4424 else
4425 cookie.fileformat = EOL_UNKNOWN;
4426 cookie.error = FALSE;
4427#endif
4428
4429 cookie.nextline = NULL;
4430 cookie.finished = FALSE;
4431
4432#ifdef FEAT_EVAL
4433 /*
4434 * Check if this script has a breakpoint.
4435 */
4436 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
4437 cookie.fname = fname_exp;
4438 cookie.dbg_tick = debug_tick;
4439
4440 cookie.level = ex_nesting_level;
4441#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442
4443 /*
4444 * Keep the sourcing name/lnum, for recursive calls.
4445 */
4446 save_sourcing_name = sourcing_name;
4447 sourcing_name = fname_exp;
4448 save_sourcing_lnum = sourcing_lnum;
4449 sourcing_lnum = 0;
4450
4451#ifdef STARTUPTIME
Bram Moolenaarc4e41982010-01-19 16:31:47 +01004452 if (time_fd != NULL)
4453 time_push(&tv_rel, &tv_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454#endif
4455
4456#ifdef FEAT_EVAL
Bram Moolenaar05159a02005-02-26 23:04:13 +00004457# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004458 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004459 prof_child_enter(&wait_start); /* entering a child now */
4460# endif
4461
4462 /* Don't use local function variables, if called from a function.
4463 * Also starts profiling timer for nested script. */
4464 save_funccalp = save_funccal();
4465
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 /*
4467 * Check if this script was sourced before to finds its SID.
4468 * If it's new, generate a new SID.
4469 */
4470 save_current_SID = current_SID;
4471# ifdef UNIX
4472 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
4473# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00004474 for (current_SID = script_items.ga_len; current_SID > 0; --current_SID)
4475 {
4476 si = &SCRIPT_ITEM(current_SID);
4477 if (si->sn_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478 && (
4479# ifdef UNIX
Bram Moolenaar7c626922005-02-07 22:01:03 +00004480 /* Compare dev/ino when possible, it catches symbolic
4481 * links. Also compare file names, the inode may change
4482 * when the file was edited. */
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004483 ((stat_ok && si->sn_dev_valid)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004484 && (si->sn_dev == st.st_dev
4485 && si->sn_ino == st.st_ino)) ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00004487 fnamecmp(si->sn_name, fname_exp) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488 break;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004489 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 if (current_SID == 0)
4491 {
4492 current_SID = ++last_current_SID;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004493 if (ga_grow(&script_items, (int)(current_SID - script_items.ga_len))
4494 == FAIL)
4495 goto almosttheend;
4496 while (script_items.ga_len < current_SID)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00004498 ++script_items.ga_len;
4499 SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
4500# ifdef FEAT_PROFILE
4501 SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00004504 si = &SCRIPT_ITEM(current_SID);
4505 si->sn_name = fname_exp;
4506 fname_exp = NULL;
4507# ifdef UNIX
4508 if (stat_ok)
4509 {
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004510 si->sn_dev_valid = TRUE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004511 si->sn_dev = st.st_dev;
4512 si->sn_ino = st.st_ino;
4513 }
4514 else
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004515 si->sn_dev_valid = FALSE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004516# endif
4517
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518 /* Allocate the local script variables to use for this script. */
4519 new_script_vars(current_SID);
4520 }
4521
Bram Moolenaar05159a02005-02-26 23:04:13 +00004522# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004523 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004524 {
4525 int forceit;
4526
4527 /* Check if we do profiling for this script. */
4528 if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit))
4529 {
4530 script_do_profile(si);
4531 si->sn_pr_force = forceit;
4532 }
4533 if (si->sn_prof_on)
4534 {
4535 ++si->sn_pr_count;
4536 profile_start(&si->sn_pr_start);
4537 profile_zero(&si->sn_pr_children);
4538 }
4539 }
4540# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541#endif
4542
Bram Moolenaar67435d92017-10-19 21:04:37 +02004543#ifdef FEAT_MBYTE
4544 cookie.conv.vc_type = CONV_NONE; /* no conversion */
4545
4546 /* Read the first line so we can check for a UTF-8 BOM. */
4547 firstline = getsourceline(0, (void *)&cookie, 0);
4548 if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
4549 && firstline[1] == 0xbb && firstline[2] == 0xbf)
4550 {
4551 /* Found BOM; setup conversion, skip over BOM and recode the line. */
4552 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
4553 p = string_convert(&cookie.conv, firstline + 3, NULL);
4554 if (p == NULL)
4555 p = vim_strsave(firstline + 3);
4556 if (p != NULL)
4557 {
4558 vim_free(firstline);
4559 firstline = p;
4560 }
4561 }
4562#endif
4563
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564 /*
4565 * Call do_cmdline, which will call getsourceline() to get the lines.
4566 */
Bram Moolenaar73881402009-02-04 16:50:47 +00004567 do_cmdline(firstline, getsourceline, (void *)&cookie,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004569 retval = OK;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004570
4571#ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004572 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004573 {
4574 /* Get "si" again, "script_items" may have been reallocated. */
4575 si = &SCRIPT_ITEM(current_SID);
4576 if (si->sn_prof_on)
4577 {
4578 profile_end(&si->sn_pr_start);
4579 profile_sub_wait(&wait_start, &si->sn_pr_start);
4580 profile_add(&si->sn_pr_total, &si->sn_pr_start);
Bram Moolenaar1056d982006-03-09 22:37:52 +00004581 profile_self(&si->sn_pr_self, &si->sn_pr_start,
4582 &si->sn_pr_children);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004583 }
4584 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004585#endif
4586
4587 if (got_int)
4588 EMSG(_(e_interr));
4589 sourcing_name = save_sourcing_name;
4590 sourcing_lnum = save_sourcing_lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004591 if (p_verbose > 1)
4592 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004593 verbose_enter();
Bram Moolenaar555b2802005-05-19 21:08:39 +00004594 smsg((char_u *)_("finished sourcing %s"), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004595 if (sourcing_name != NULL)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004596 smsg((char_u *)_("continuing in %s"), sourcing_name);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004597 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004598 }
4599#ifdef STARTUPTIME
Bram Moolenaarc4e41982010-01-19 16:31:47 +01004600 if (time_fd != NULL)
4601 {
4602 vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname);
4603 time_msg((char *)IObuff, &tv_start);
4604 time_pop(&tv_rel);
4605 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004606#endif
4607
4608#ifdef FEAT_EVAL
4609 /*
4610 * After a "finish" in debug mode, need to break at first command of next
4611 * sourced file.
4612 */
4613 if (save_debug_break_level > ex_nesting_level
4614 && debug_break_level == ex_nesting_level)
4615 ++debug_break_level;
4616#endif
4617
Bram Moolenaar05159a02005-02-26 23:04:13 +00004618#ifdef FEAT_EVAL
4619almosttheend:
4620 current_SID = save_current_SID;
4621 restore_funccal(save_funccalp);
4622# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004623 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004624 prof_child_exit(&wait_start); /* leaving a child now */
4625# endif
4626#endif
4627 fclose(cookie.fp);
4628 vim_free(cookie.nextline);
Bram Moolenaar73881402009-02-04 16:50:47 +00004629 vim_free(firstline);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004630#ifdef FEAT_MBYTE
4631 convert_setup(&cookie.conv, NULL, NULL);
4632#endif
4633
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634theend:
4635 vim_free(fname_exp);
4636 return retval;
4637}
4638
4639#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004640
Bram Moolenaar071d4272004-06-13 20:20:40 +00004641/*
4642 * ":scriptnames"
4643 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004644 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004645ex_scriptnames(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646{
4647 int i;
4648
Bram Moolenaar05159a02005-02-26 23:04:13 +00004649 for (i = 1; i <= script_items.ga_len && !got_int; ++i)
4650 if (SCRIPT_ITEM(i).sn_name != NULL)
Bram Moolenaard58ea072011-06-26 04:25:30 +02004651 {
4652 home_replace(NULL, SCRIPT_ITEM(i).sn_name,
4653 NameBuff, MAXPATHL, TRUE);
4654 smsg((char_u *)"%3d: %s", i, NameBuff);
Bram Moolenaar970a1b82012-03-23 18:39:18 +01004655 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004656}
4657
4658# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
4659/*
4660 * Fix slashes in the list of script names for 'shellslash'.
4661 */
4662 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004663scriptnames_slash_adjust(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664{
4665 int i;
4666
Bram Moolenaar05159a02005-02-26 23:04:13 +00004667 for (i = 1; i <= script_items.ga_len; ++i)
4668 if (SCRIPT_ITEM(i).sn_name != NULL)
4669 slash_adjust(SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004670}
4671# endif
4672
4673/*
4674 * Get a pointer to a script name. Used for ":verbose set".
4675 */
4676 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004677get_scriptname(scid_T id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678{
4679 if (id == SID_MODELINE)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004680 return (char_u *)_("modeline");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 if (id == SID_CMDARG)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004682 return (char_u *)_("--cmd argument");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 if (id == SID_CARG)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004684 return (char_u *)_("-c argument");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004685 if (id == SID_ENV)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004686 return (char_u *)_("environment variable");
4687 if (id == SID_ERROR)
4688 return (char_u *)_("error handler");
Bram Moolenaar05159a02005-02-26 23:04:13 +00004689 return SCRIPT_ITEM(id).sn_name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004690}
Bram Moolenaar05159a02005-02-26 23:04:13 +00004691
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004692# if defined(EXITFREE) || defined(PROTO)
4693 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004694free_scriptnames(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004695{
4696 int i;
4697
4698 for (i = script_items.ga_len; i > 0; --i)
4699 vim_free(SCRIPT_ITEM(i).sn_name);
4700 ga_clear(&script_items);
4701}
4702# endif
4703
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704#endif
4705
4706#if defined(USE_CR) || defined(PROTO)
4707
4708# if defined(__MSL__) && (__MSL__ >= 22)
4709/*
4710 * Newer version of the Metrowerks library handle DOS and UNIX files
4711 * without help.
4712 * Test with earlier versions, MSL 2.2 is the library supplied with
4713 * Codewarrior Pro 2.
4714 */
4715 char *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004716fgets_cr(char *s, int n, FILE *stream)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004717{
4718 return fgets(s, n, stream);
4719}
4720# else
4721/*
4722 * Version of fgets() which also works for lines ending in a <CR> only
4723 * (Macintosh format).
4724 * For older versions of the Metrowerks library.
4725 * At least CodeWarrior 9 needed this code.
4726 */
4727 char *
Bram Moolenaard14e00e2016-01-31 17:30:51 +01004728fgets_cr(char *s, int n, FILE *stream)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729{
4730 int c = 0;
4731 int char_read = 0;
4732
4733 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
4734 {
4735 c = fgetc(stream);
4736 s[char_read++] = c;
4737 /* If the file is in DOS format, we need to skip a NL after a CR. I
4738 * thought it was the other way around, but this appears to work... */
4739 if (c == '\n')
4740 {
4741 c = fgetc(stream);
4742 if (c != '\r')
4743 ungetc(c, stream);
4744 }
4745 }
4746
4747 s[char_read] = 0;
4748 if (char_read == 0)
4749 return NULL;
4750
4751 if (feof(stream) && char_read == 1)
4752 return NULL;
4753
4754 return s;
4755}
4756# endif
4757#endif
4758
4759/*
4760 * Get one full line from a sourced file.
4761 * Called by do_cmdline() when it's called from do_source().
4762 *
4763 * Return a pointer to the line in allocated memory.
4764 * Return NULL for end-of-file or some error.
4765 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004767getsourceline(int c UNUSED, void *cookie, int indent UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768{
4769 struct source_cookie *sp = (struct source_cookie *)cookie;
4770 char_u *line;
Bram Moolenaarc047b9a2012-02-11 20:40:55 +01004771 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004772
4773#ifdef FEAT_EVAL
4774 /* If breakpoints have been added/deleted need to check for it. */
4775 if (sp->dbg_tick < debug_tick)
4776 {
4777 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
4778 sp->dbg_tick = debug_tick;
4779 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00004780# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004781 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004782 script_line_end();
4783# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784#endif
4785 /*
4786 * Get current line. If there is a read-ahead line, use it, otherwise get
4787 * one now.
4788 */
4789 if (sp->finished)
4790 line = NULL;
4791 else if (sp->nextline == NULL)
4792 line = get_one_sourceline(sp);
4793 else
4794 {
4795 line = sp->nextline;
4796 sp->nextline = NULL;
4797 ++sourcing_lnum;
4798 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004799#ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004800 if (line != NULL && do_profiling == PROF_YES)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004801 script_line_start();
4802#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803
4804 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
4805 * contain the 'C' flag. */
4806 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
4807 {
4808 /* compensate for the one line read-ahead */
4809 --sourcing_lnum;
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004810
4811 /* Get the next line and concatenate it when it starts with a
4812 * backslash. We always need to read the next line, keep it in
4813 * sp->nextline. */
4814 sp->nextline = get_one_sourceline(sp);
4815 if (sp->nextline != NULL && *(p = skipwhite(sp->nextline)) == '\\')
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816 {
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004817 garray_T ga;
4818
Bram Moolenaarb549a732012-02-22 18:29:33 +01004819 ga_init2(&ga, (int)sizeof(char_u), 400);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004820 ga_concat(&ga, line);
4821 ga_concat(&ga, p + 1);
4822 for (;;)
4823 {
4824 vim_free(sp->nextline);
4825 sp->nextline = get_one_sourceline(sp);
4826 if (sp->nextline == NULL)
4827 break;
4828 p = skipwhite(sp->nextline);
4829 if (*p != '\\')
4830 break;
Bram Moolenaarb549a732012-02-22 18:29:33 +01004831 /* Adjust the growsize to the current length to speed up
4832 * concatenating many lines. */
4833 if (ga.ga_len > 400)
4834 {
4835 if (ga.ga_len > 8000)
4836 ga.ga_growsize = 8000;
4837 else
4838 ga.ga_growsize = ga.ga_len;
4839 }
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004840 ga_concat(&ga, p + 1);
4841 }
4842 ga_append(&ga, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004843 vim_free(line);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004844 line = ga.ga_data;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004845 }
4846 }
4847
4848#ifdef FEAT_MBYTE
4849 if (line != NULL && sp->conv.vc_type != CONV_NONE)
4850 {
Bram Moolenaarc047b9a2012-02-11 20:40:55 +01004851 char_u *s;
4852
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853 /* Convert the encoding of the script line. */
4854 s = string_convert(&sp->conv, line, NULL);
4855 if (s != NULL)
4856 {
4857 vim_free(line);
4858 line = s;
4859 }
4860 }
4861#endif
4862
4863#ifdef FEAT_EVAL
4864 /* Did we encounter a breakpoint? */
4865 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
4866 {
4867 dbg_breakpoint(sp->fname, sourcing_lnum);
4868 /* Find next breakpoint. */
4869 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
4870 sp->dbg_tick = debug_tick;
4871 }
4872#endif
4873
4874 return line;
4875}
4876
4877 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004878get_one_sourceline(struct source_cookie *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004879{
4880 garray_T ga;
4881 int len;
4882 int c;
4883 char_u *buf;
4884#ifdef USE_CRNL
4885 int has_cr; /* CR-LF found */
4886#endif
4887#ifdef USE_CR
4888 char_u *scan;
4889#endif
4890 int have_read = FALSE;
4891
4892 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004893 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894
4895 /*
4896 * Loop until there is a finished line (or end-of-file).
4897 */
4898 sourcing_lnum++;
4899 for (;;)
4900 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004901 /* make room to read at least 120 (more) characters */
4902 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004903 break;
4904 buf = (char_u *)ga.ga_data;
4905
4906#ifdef USE_CR
4907 if (sp->fileformat == EOL_MAC)
4908 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00004909 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
4910 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004911 break;
4912 }
4913 else
4914#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00004915 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
4916 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004918 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004919#ifdef USE_CRNL
4920 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
4921 * CTRL-Z by its own, or after a NL. */
4922 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
4923 && sp->fileformat == EOL_DOS
4924 && buf[len - 1] == Ctrl_Z)
4925 {
4926 buf[len - 1] = NUL;
4927 break;
4928 }
4929#endif
4930
4931#ifdef USE_CR
4932 /* If the read doesn't stop on a new line, and there's
4933 * some CR then we assume a Mac format */
4934 if (sp->fileformat == EOL_UNKNOWN)
4935 {
4936 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
4937 sp->fileformat = EOL_MAC;
4938 else
4939 sp->fileformat = EOL_UNIX;
4940 }
4941
4942 if (sp->fileformat == EOL_MAC)
4943 {
4944 scan = vim_strchr(buf, '\r');
4945
4946 if (scan != NULL)
4947 {
4948 *scan = '\n';
4949 if (*(scan + 1) != 0)
4950 {
4951 *(scan + 1) = 0;
4952 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
4953 }
4954 }
4955 len = STRLEN(buf);
4956 }
4957#endif
4958
4959 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960 ga.ga_len = len;
4961
4962 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00004963 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00004964 continue;
4965
4966 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
4967 {
4968#ifdef USE_CRNL
4969 has_cr = (len >= 2 && buf[len - 2] == '\r');
4970 if (sp->fileformat == EOL_UNKNOWN)
4971 {
4972 if (has_cr)
4973 sp->fileformat = EOL_DOS;
4974 else
4975 sp->fileformat = EOL_UNIX;
4976 }
4977
4978 if (sp->fileformat == EOL_DOS)
4979 {
4980 if (has_cr) /* replace trailing CR */
4981 {
4982 buf[len - 2] = '\n';
4983 --len;
4984 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 }
4986 else /* lines like ":map xx yy^M" will have failed */
4987 {
4988 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00004989 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004990 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004991 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00004992 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993 sp->error = TRUE;
4994 sp->fileformat = EOL_UNIX;
4995 }
4996 }
4997#endif
4998 /* The '\n' is escaped if there is an odd number of ^V's just
4999 * before it, first set "c" just before the 'V's and then check
5000 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
5001 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
5002 ;
5003 if ((len & 1) != (c & 1)) /* escaped NL, read more */
5004 {
5005 sourcing_lnum++;
5006 continue;
5007 }
5008
5009 buf[len - 1] = NUL; /* remove the NL */
5010 }
5011
5012 /*
5013 * Check for ^C here now and then, so recursive :so can be broken.
5014 */
5015 line_breakcheck();
5016 break;
5017 }
5018
5019 if (have_read)
5020 return (char_u *)ga.ga_data;
5021
5022 vim_free(ga.ga_data);
5023 return NULL;
5024}
5025
Bram Moolenaar05159a02005-02-26 23:04:13 +00005026#if defined(FEAT_PROFILE) || defined(PROTO)
5027/*
5028 * Called when starting to read a script line.
5029 * "sourcing_lnum" must be correct!
5030 * When skipping lines it may not actually be executed, but we won't find out
5031 * until later and we need to store the time now.
5032 */
5033 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005034script_line_start(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005035{
5036 scriptitem_T *si;
5037 sn_prl_T *pp;
5038
5039 if (current_SID <= 0 || current_SID > script_items.ga_len)
5040 return;
5041 si = &SCRIPT_ITEM(current_SID);
5042 if (si->sn_prof_on && sourcing_lnum >= 1)
5043 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005044 /* Grow the array before starting the timer, so that the time spent
Bram Moolenaar05159a02005-02-26 23:04:13 +00005045 * here isn't counted. */
Bram Moolenaar67435d92017-10-19 21:04:37 +02005046 (void)ga_grow(&si->sn_prl_ga,
5047 (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
Bram Moolenaar05159a02005-02-26 23:04:13 +00005048 si->sn_prl_idx = sourcing_lnum - 1;
5049 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
5050 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
5051 {
5052 /* Zero counters for a line that was not used before. */
5053 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
5054 pp->snp_count = 0;
5055 profile_zero(&pp->sn_prl_total);
5056 profile_zero(&pp->sn_prl_self);
5057 ++si->sn_prl_ga.ga_len;
5058 }
5059 si->sn_prl_execed = FALSE;
5060 profile_start(&si->sn_prl_start);
5061 profile_zero(&si->sn_prl_children);
5062 profile_get_wait(&si->sn_prl_wait);
5063 }
5064}
5065
5066/*
5067 * Called when actually executing a function line.
5068 */
5069 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005070script_line_exec(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005071{
5072 scriptitem_T *si;
5073
5074 if (current_SID <= 0 || current_SID > script_items.ga_len)
5075 return;
5076 si = &SCRIPT_ITEM(current_SID);
5077 if (si->sn_prof_on && si->sn_prl_idx >= 0)
5078 si->sn_prl_execed = TRUE;
5079}
5080
5081/*
Bram Moolenaar67435d92017-10-19 21:04:37 +02005082 * Called when done with a script line.
Bram Moolenaar05159a02005-02-26 23:04:13 +00005083 */
5084 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005085script_line_end(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005086{
5087 scriptitem_T *si;
5088 sn_prl_T *pp;
5089
5090 if (current_SID <= 0 || current_SID > script_items.ga_len)
5091 return;
5092 si = &SCRIPT_ITEM(current_SID);
5093 if (si->sn_prof_on && si->sn_prl_idx >= 0
5094 && si->sn_prl_idx < si->sn_prl_ga.ga_len)
5095 {
5096 if (si->sn_prl_execed)
5097 {
5098 pp = &PRL_ITEM(si, si->sn_prl_idx);
5099 ++pp->snp_count;
5100 profile_end(&si->sn_prl_start);
5101 profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005102 profile_add(&pp->sn_prl_total, &si->sn_prl_start);
Bram Moolenaar1056d982006-03-09 22:37:52 +00005103 profile_self(&pp->sn_prl_self, &si->sn_prl_start,
5104 &si->sn_prl_children);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005105 }
5106 si->sn_prl_idx = -1;
5107 }
5108}
5109#endif
5110
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111/*
5112 * ":scriptencoding": Set encoding conversion for a sourced script.
5113 * Without the multi-byte feature it's simply ignored.
5114 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005116ex_scriptencoding(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117{
5118#ifdef FEAT_MBYTE
5119 struct source_cookie *sp;
5120 char_u *name;
5121
5122 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
5123 {
5124 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
5125 return;
5126 }
5127
5128 if (*eap->arg != NUL)
5129 {
5130 name = enc_canonize(eap->arg);
5131 if (name == NULL) /* out of memory */
5132 return;
5133 }
5134 else
5135 name = eap->arg;
5136
5137 /* Setup for conversion from the specified encoding to 'encoding'. */
5138 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
5139 convert_setup(&sp->conv, name, p_enc);
5140
5141 if (name != eap->arg)
5142 vim_free(name);
5143#endif
5144}
5145
5146#if defined(FEAT_EVAL) || defined(PROTO)
5147/*
5148 * ":finish": Mark a sourced file as finished.
5149 */
5150 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005151ex_finish(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152{
5153 if (getline_equal(eap->getline, eap->cookie, getsourceline))
5154 do_finish(eap, FALSE);
5155 else
5156 EMSG(_("E168: :finish used outside of a sourced file"));
5157}
5158
5159/*
5160 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
5161 * Also called for a pending finish at the ":endtry" or after returning from
5162 * an extra do_cmdline(). "reanimate" is used in the latter case.
5163 */
5164 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005165do_finish(exarg_T *eap, int reanimate)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005166{
5167 int idx;
5168
5169 if (reanimate)
5170 ((struct source_cookie *)getline_cookie(eap->getline,
5171 eap->cookie))->finished = FALSE;
5172
5173 /*
5174 * Cleanup (and inactivate) conditionals, but stop when a try conditional
5175 * not in its finally clause (which then is to be executed next) is found.
5176 * In this case, make the ":finish" pending for execution at the ":endtry".
5177 * Otherwise, finish normally.
5178 */
5179 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
5180 if (idx >= 0)
5181 {
5182 eap->cstack->cs_pending[idx] = CSTP_FINISH;
5183 report_make_pending(CSTP_FINISH, NULL);
5184 }
5185 else
5186 ((struct source_cookie *)getline_cookie(eap->getline,
5187 eap->cookie))->finished = TRUE;
5188}
5189
5190
5191/*
5192 * Return TRUE when a sourced file had the ":finish" command: Don't give error
5193 * message for missing ":endif".
5194 * Return FALSE when not sourcing a file.
5195 */
5196 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005197source_finished(
5198 char_u *(*fgetline)(int, void *, int),
5199 void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005200{
Bram Moolenaar89d40322006-08-29 15:30:07 +00005201 return (getline_equal(fgetline, cookie, getsourceline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005202 && ((struct source_cookie *)getline_cookie(
Bram Moolenaar89d40322006-08-29 15:30:07 +00005203 fgetline, cookie))->finished);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005204}
5205#endif
5206
Bram Moolenaar071d4272004-06-13 20:20:40 +00005207/*
5208 * ":checktime [buffer]"
5209 */
5210 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005211ex_checktime(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212{
5213 buf_T *buf;
5214 int save_no_check_timestamps = no_check_timestamps;
5215
5216 no_check_timestamps = 0;
5217 if (eap->addr_count == 0) /* default is all buffers */
5218 check_timestamps(FALSE);
5219 else
5220 {
5221 buf = buflist_findnr((int)eap->line2);
5222 if (buf != NULL) /* cannot happen? */
5223 (void)buf_check_timestamp(buf, FALSE);
5224 }
5225 no_check_timestamps = save_no_check_timestamps;
5226}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005227
Bram Moolenaar071d4272004-06-13 20:20:40 +00005228#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5229 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005230# define HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005231static char_u *get_locale_val(int what);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005232
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005233 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005234get_locale_val(int what)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005235{
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005236 char_u *loc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005237
Bram Moolenaar48e330a2016-02-23 14:53:34 +01005238 /* Obtain the locale value from the libraries. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005239 loc = (char_u *)setlocale(what, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005240
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005241# ifdef WIN32
Bram Moolenaar071d4272004-06-13 20:20:40 +00005242 if (loc != NULL)
5243 {
5244 char_u *p;
5245
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005246 /* setocale() returns something like "LC_COLLATE=<name>;LC_..." when
5247 * one of the values (e.g., LC_CTYPE) differs. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005248 p = vim_strchr(loc, '=');
5249 if (p != NULL)
5250 {
5251 loc = ++p;
5252 while (*p != NUL) /* remove trailing newline */
5253 {
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005254 if (*p < ' ' || *p == ';')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005255 {
5256 *p = NUL;
5257 break;
5258 }
5259 ++p;
5260 }
5261 }
5262 }
5263# endif
5264
5265 return loc;
5266}
5267#endif
5268
5269
5270#ifdef WIN32
5271/*
5272 * On MS-Windows locale names are strings like "German_Germany.1252", but
5273 * gettext expects "de". Try to translate one into another here for a few
5274 * supported languages.
5275 */
5276 static char_u *
5277gettext_lang(char_u *name)
5278{
5279 int i;
5280 static char *(mtable[]) = {
5281 "afrikaans", "af",
5282 "czech", "cs",
5283 "dutch", "nl",
5284 "german", "de",
5285 "english_united kingdom", "en_GB",
5286 "spanish", "es",
5287 "french", "fr",
5288 "italian", "it",
5289 "japanese", "ja",
5290 "korean", "ko",
5291 "norwegian", "no",
5292 "polish", "pl",
5293 "russian", "ru",
5294 "slovak", "sk",
5295 "swedish", "sv",
5296 "ukrainian", "uk",
5297 "chinese_china", "zh_CN",
5298 "chinese_taiwan", "zh_TW",
5299 NULL};
5300
5301 for (i = 0; mtable[i] != NULL; i += 2)
5302 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005303 return (char_u *)mtable[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005304 return name;
5305}
5306#endif
5307
5308#if defined(FEAT_MULTI_LANG) || defined(PROTO)
5309/*
5310 * Obtain the current messages language. Used to set the default for
5311 * 'helplang'. May return NULL or an empty string.
5312 */
5313 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005314get_mess_lang(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315{
5316 char_u *p;
5317
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005318# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319# if defined(LC_MESSAGES)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005320 p = get_locale_val(LC_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005321# else
5322 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005323 * may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
5324 * and LC_MONETARY may be set differently for a Japanese working in the
5325 * US. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005326 p = get_locale_val(LC_COLLATE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005327# endif
5328# else
5329 p = mch_getenv((char_u *)"LC_ALL");
5330 if (p == NULL || *p == NUL)
5331 {
5332 p = mch_getenv((char_u *)"LC_MESSAGES");
5333 if (p == NULL || *p == NUL)
5334 p = mch_getenv((char_u *)"LANG");
5335 }
5336# endif
5337# ifdef WIN32
5338 p = gettext_lang(p);
5339# endif
5340 return p;
5341}
5342#endif
5343
Bram Moolenaardef9e822004-12-31 20:58:58 +00005344/* Complicated #if; matches with where get_mess_env() is used below. */
5345#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5346 && defined(LC_MESSAGES))) \
5347 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5348 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
5349 && !defined(LC_MESSAGES))
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01005350static char_u *get_mess_env(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351
5352/*
5353 * Get the language used for messages from the environment.
5354 */
5355 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005356get_mess_env(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005357{
5358 char_u *p;
5359
5360 p = mch_getenv((char_u *)"LC_ALL");
5361 if (p == NULL || *p == NUL)
5362 {
5363 p = mch_getenv((char_u *)"LC_MESSAGES");
5364 if (p == NULL || *p == NUL)
5365 {
5366 p = mch_getenv((char_u *)"LANG");
5367 if (p != NULL && VIM_ISDIGIT(*p))
5368 p = NULL; /* ignore something like "1043" */
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005369# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005370 if (p == NULL || *p == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005371 p = get_locale_val(LC_CTYPE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005372# endif
5373 }
5374 }
5375 return p;
5376}
5377#endif
5378
5379#if defined(FEAT_EVAL) || defined(PROTO)
5380
5381/*
5382 * Set the "v:lang" variable according to the current locale setting.
5383 * Also do "v:lc_time"and "v:ctype".
5384 */
5385 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005386set_lang_var(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005387{
5388 char_u *loc;
5389
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005390# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005391 loc = get_locale_val(LC_CTYPE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005392# else
5393 /* setlocale() not supported: use the default value */
5394 loc = (char_u *)"C";
5395# endif
5396 set_vim_var_string(VV_CTYPE, loc, -1);
5397
5398 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
5399 * back to LC_CTYPE if it's empty. */
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005400# if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005401 loc = get_locale_val(LC_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005402# else
5403 loc = get_mess_env();
5404# endif
5405 set_vim_var_string(VV_LANG, loc, -1);
5406
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005407# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005408 loc = get_locale_val(LC_TIME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005409# endif
5410 set_vim_var_string(VV_LC_TIME, loc, -1);
5411}
5412#endif
5413
5414#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5415 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
5416/*
5417 * ":language": Set the language (locale).
5418 */
5419 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005420ex_language(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005421{
5422 char *loc;
5423 char_u *p;
5424 char_u *name;
5425 int what = LC_ALL;
5426 char *whatstr = "";
5427#ifdef LC_MESSAGES
5428# define VIM_LC_MESSAGES LC_MESSAGES
5429#else
5430# define VIM_LC_MESSAGES 6789
5431#endif
5432
5433 name = eap->arg;
5434
5435 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
5436 * Allow abbreviation, but require at least 3 characters to avoid
5437 * confusion with a two letter language name "me" or "ct". */
5438 p = skiptowhite(eap->arg);
Bram Moolenaar1c465442017-03-12 20:10:05 +01005439 if ((*p == NUL || VIM_ISWHITE(*p)) && p - eap->arg >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005440 {
5441 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
5442 {
5443 what = VIM_LC_MESSAGES;
5444 name = skipwhite(p);
5445 whatstr = "messages ";
5446 }
5447 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
5448 {
5449 what = LC_CTYPE;
5450 name = skipwhite(p);
5451 whatstr = "ctype ";
5452 }
5453 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
5454 {
5455 what = LC_TIME;
5456 name = skipwhite(p);
5457 whatstr = "time ";
5458 }
5459 }
5460
5461 if (*name == NUL)
5462 {
5463#ifndef LC_MESSAGES
5464 if (what == VIM_LC_MESSAGES)
5465 p = get_mess_env();
5466 else
5467#endif
5468 p = (char_u *)setlocale(what, NULL);
5469 if (p == NULL || *p == NUL)
5470 p = (char_u *)"Unknown";
5471 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
5472 }
5473 else
5474 {
5475#ifndef LC_MESSAGES
5476 if (what == VIM_LC_MESSAGES)
5477 loc = "";
5478 else
5479#endif
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005480 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005481 loc = setlocale(what, (char *)name);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005482#if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
5483 /* Make sure strtod() uses a decimal point, not a comma. */
5484 setlocale(LC_NUMERIC, "C");
5485#endif
5486 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005487 if (loc == NULL)
5488 EMSG2(_("E197: Cannot set language to \"%s\""), name);
5489 else
5490 {
5491#ifdef HAVE_NL_MSG_CAT_CNTR
5492 /* Need to do this for GNU gettext, otherwise cached translations
5493 * will be used again. */
5494 extern int _nl_msg_cat_cntr;
5495
5496 ++_nl_msg_cat_cntr;
5497#endif
Bram Moolenaarb8017e72007-05-10 18:59:07 +00005498 /* Reset $LC_ALL, otherwise it would overrule everything. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005499 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
5500
5501 if (what != LC_TIME)
5502 {
5503 /* Tell gettext() what to translate to. It apparently doesn't
5504 * use the currently effective locale. Also do this when
5505 * FEAT_GETTEXT isn't defined, so that shell commands use this
5506 * value. */
5507 if (what == LC_ALL)
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00005508 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005509 vim_setenv((char_u *)"LANG", name);
Bram Moolenaare3a0b532013-06-28 20:36:30 +02005510
5511 /* Clear $LANGUAGE because GNU gettext uses it. */
5512 vim_setenv((char_u *)"LANGUAGE", (char_u *)"");
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00005513# ifdef WIN32
5514 /* Apparently MS-Windows printf() may cause a crash when
5515 * we give it 8-bit text while it's expecting text in the
5516 * current locale. This call avoids that. */
5517 setlocale(LC_CTYPE, "C");
5518# endif
5519 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520 if (what != LC_CTYPE)
5521 {
5522 char_u *mname;
5523#ifdef WIN32
5524 mname = gettext_lang(name);
5525#else
5526 mname = name;
5527#endif
5528 vim_setenv((char_u *)"LC_MESSAGES", mname);
5529#ifdef FEAT_MULTI_LANG
5530 set_helplang_default(mname);
5531#endif
5532 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005533 }
5534
5535# ifdef FEAT_EVAL
5536 /* Set v:lang, v:lc_time and v:ctype to the final result. */
5537 set_lang_var();
5538# endif
Bram Moolenaar6cc00c72011-10-20 21:41:09 +02005539# ifdef FEAT_TITLE
5540 maketitle();
5541# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005542 }
5543 }
5544}
5545
5546# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005547
5548static char_u **locales = NULL; /* Array of all available locales */
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01005549
5550# ifndef WIN32
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005551static int did_init_locales = FALSE;
5552
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005553/* Return an array of strings for all available locales + NULL for the
5554 * last element. Return NULL in case of error. */
5555 static char_u **
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005556find_locales(void)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005557{
5558 garray_T locales_ga;
5559 char_u *loc;
5560
5561 /* Find all available locales by running command "locale -a". If this
5562 * doesn't work we won't have completion. */
5563 char_u *locale_a = get_cmd_output((char_u *)"locale -a",
Bram Moolenaar39c29ed2014-04-05 19:44:40 +02005564 NULL, SHELL_SILENT, NULL);
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005565 if (locale_a == NULL)
5566 return NULL;
5567 ga_init2(&locales_ga, sizeof(char_u *), 20);
5568
5569 /* Transform locale_a string where each locale is separated by "\n"
5570 * into an array of locale strings. */
5571 loc = (char_u *)strtok((char *)locale_a, "\n");
5572
5573 while (loc != NULL)
5574 {
5575 if (ga_grow(&locales_ga, 1) == FAIL)
5576 break;
5577 loc = vim_strsave(loc);
5578 if (loc == NULL)
5579 break;
5580
5581 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
5582 loc = (char_u *)strtok(NULL, "\n");
5583 }
5584 vim_free(locale_a);
5585 if (ga_grow(&locales_ga, 1) == FAIL)
5586 {
5587 ga_clear(&locales_ga);
5588 return NULL;
5589 }
5590 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
5591 return (char_u **)locales_ga.ga_data;
5592}
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01005593# endif
5594
5595/*
5596 * Lazy initialization of all available locales.
5597 */
5598 static void
5599init_locales(void)
5600{
5601# ifndef WIN32
5602 if (!did_init_locales)
5603 {
5604 did_init_locales = TRUE;
5605 locales = find_locales();
5606 }
5607# endif
5608}
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005609
5610# if defined(EXITFREE) || defined(PROTO)
5611 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005612free_locales(void)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005613{
5614 int i;
5615 if (locales != NULL)
5616 {
5617 for (i = 0; locales[i] != NULL; i++)
5618 vim_free(locales[i]);
Bram Moolenaard23a8232018-02-10 18:45:26 +01005619 VIM_CLEAR(locales);
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005620 }
5621}
5622# endif
5623
Bram Moolenaar071d4272004-06-13 20:20:40 +00005624/*
5625 * Function given to ExpandGeneric() to obtain the possible arguments of the
5626 * ":language" command.
5627 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005629get_lang_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005630{
5631 if (idx == 0)
5632 return (char_u *)"messages";
5633 if (idx == 1)
5634 return (char_u *)"ctype";
5635 if (idx == 2)
5636 return (char_u *)"time";
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005637
5638 init_locales();
5639 if (locales == NULL)
5640 return NULL;
5641 return locales[idx - 3];
5642}
5643
5644/*
5645 * Function given to ExpandGeneric() to obtain the available locales.
5646 */
5647 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005648get_locales(expand_T *xp UNUSED, int idx)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005649{
5650 init_locales();
5651 if (locales == NULL)
5652 return NULL;
5653 return locales[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005654}
5655# endif
5656
5657#endif