blob: f7ca41de4426485270a4657187b172553f97c830 [file] [log] [blame]
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
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 * autocmd.c: Autocommand related functions
12 */
13
14#include "vim.h"
15
16/*
17 * The autocommands are stored in a list for each event.
18 * Autocommands for the same pattern, that are consecutive, are joined
19 * together, to avoid having to match the pattern too often.
20 * The result is an array of Autopat lists, which point to AutoCmd lists:
21 *
22 * last_autopat[0] -----------------------------+
23 * V
24 * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
25 * Autopat.cmds Autopat.cmds
26 * | |
27 * V V
28 * AutoCmd.next AutoCmd.next
29 * | |
30 * V V
31 * AutoCmd.next NULL
32 * |
33 * V
34 * NULL
35 *
36 * last_autopat[1] --------+
37 * V
38 * first_autopat[1] --> Autopat.next --> NULL
39 * Autopat.cmds
40 * |
41 * V
42 * AutoCmd.next
43 * |
44 * V
45 * NULL
46 * etc.
47 *
48 * The order of AutoCmds is important, this is the order in which they were
49 * defined and will have to be executed.
50 */
51typedef struct AutoCmd
52{
53 char_u *cmd; // The command to be executed (NULL
54 // when command has been removed).
Bram Moolenaareb93f3f2019-04-04 15:04:56 +020055 char once; // "One shot": removed after execution
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010056 char nested; // If autocommands nest here.
57 char last; // last command in list
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010058 sctx_T script_ctx; // script context where defined
Bram Moolenaar3e460fd2019-01-26 16:21:07 +010059 struct AutoCmd *next; // next AutoCmd in list
60} AutoCmd;
61
62typedef struct AutoPat
63{
64 struct AutoPat *next; // Next AutoPat in AutoPat list; MUST
65 // be the first entry.
66 char_u *pat; // pattern as typed (NULL when pattern
67 // has been removed)
68 regprog_T *reg_prog; // compiled regprog for pattern
69 AutoCmd *cmds; // list of commands to do
70 int group; // group ID
71 int patlen; // strlen() of pat
72 int buflocal_nr; // !=0 for buffer-local AutoPat
73 char allow_dirs; // Pattern may match whole path
74 char last; // last pattern for apply_autocmds()
75} AutoPat;
76
77static struct event_name
78{
79 char *name; // event name
80 event_T event; // event number
81} event_names[] =
82{
83 {"BufAdd", EVENT_BUFADD},
84 {"BufCreate", EVENT_BUFADD},
85 {"BufDelete", EVENT_BUFDELETE},
86 {"BufEnter", EVENT_BUFENTER},
87 {"BufFilePost", EVENT_BUFFILEPOST},
88 {"BufFilePre", EVENT_BUFFILEPRE},
89 {"BufHidden", EVENT_BUFHIDDEN},
90 {"BufLeave", EVENT_BUFLEAVE},
91 {"BufNew", EVENT_BUFNEW},
92 {"BufNewFile", EVENT_BUFNEWFILE},
93 {"BufRead", EVENT_BUFREADPOST},
94 {"BufReadCmd", EVENT_BUFREADCMD},
95 {"BufReadPost", EVENT_BUFREADPOST},
96 {"BufReadPre", EVENT_BUFREADPRE},
97 {"BufUnload", EVENT_BUFUNLOAD},
98 {"BufWinEnter", EVENT_BUFWINENTER},
99 {"BufWinLeave", EVENT_BUFWINLEAVE},
100 {"BufWipeout", EVENT_BUFWIPEOUT},
101 {"BufWrite", EVENT_BUFWRITEPRE},
102 {"BufWritePost", EVENT_BUFWRITEPOST},
103 {"BufWritePre", EVENT_BUFWRITEPRE},
104 {"BufWriteCmd", EVENT_BUFWRITECMD},
105 {"CmdlineChanged", EVENT_CMDLINECHANGED},
106 {"CmdlineEnter", EVENT_CMDLINEENTER},
107 {"CmdlineLeave", EVENT_CMDLINELEAVE},
108 {"CmdwinEnter", EVENT_CMDWINENTER},
109 {"CmdwinLeave", EVENT_CMDWINLEAVE},
110 {"CmdUndefined", EVENT_CMDUNDEFINED},
111 {"ColorScheme", EVENT_COLORSCHEME},
112 {"ColorSchemePre", EVENT_COLORSCHEMEPRE},
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200113 {"CompleteChanged", EVENT_COMPLETECHANGED},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100114 {"CompleteDone", EVENT_COMPLETEDONE},
Bram Moolenaar3f169ce2020-01-26 22:43:31 +0100115 {"CompleteDonePre", EVENT_COMPLETEDONEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100116 {"CursorHold", EVENT_CURSORHOLD},
117 {"CursorHoldI", EVENT_CURSORHOLDI},
118 {"CursorMoved", EVENT_CURSORMOVED},
119 {"CursorMovedI", EVENT_CURSORMOVEDI},
120 {"DiffUpdated", EVENT_DIFFUPDATED},
121 {"DirChanged", EVENT_DIRCHANGED},
122 {"EncodingChanged", EVENT_ENCODINGCHANGED},
123 {"ExitPre", EVENT_EXITPRE},
124 {"FileEncoding", EVENT_ENCODINGCHANGED},
125 {"FileAppendPost", EVENT_FILEAPPENDPOST},
126 {"FileAppendPre", EVENT_FILEAPPENDPRE},
127 {"FileAppendCmd", EVENT_FILEAPPENDCMD},
128 {"FileChangedShell",EVENT_FILECHANGEDSHELL},
129 {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST},
130 {"FileChangedRO", EVENT_FILECHANGEDRO},
131 {"FileReadPost", EVENT_FILEREADPOST},
132 {"FileReadPre", EVENT_FILEREADPRE},
133 {"FileReadCmd", EVENT_FILEREADCMD},
134 {"FileType", EVENT_FILETYPE},
135 {"FileWritePost", EVENT_FILEWRITEPOST},
136 {"FileWritePre", EVENT_FILEWRITEPRE},
137 {"FileWriteCmd", EVENT_FILEWRITECMD},
138 {"FilterReadPost", EVENT_FILTERREADPOST},
139 {"FilterReadPre", EVENT_FILTERREADPRE},
140 {"FilterWritePost", EVENT_FILTERWRITEPOST},
141 {"FilterWritePre", EVENT_FILTERWRITEPRE},
142 {"FocusGained", EVENT_FOCUSGAINED},
143 {"FocusLost", EVENT_FOCUSLOST},
144 {"FuncUndefined", EVENT_FUNCUNDEFINED},
145 {"GUIEnter", EVENT_GUIENTER},
146 {"GUIFailed", EVENT_GUIFAILED},
147 {"InsertChange", EVENT_INSERTCHANGE},
148 {"InsertEnter", EVENT_INSERTENTER},
149 {"InsertLeave", EVENT_INSERTLEAVE},
Bram Moolenaarb53e13a2020-10-21 12:19:53 +0200150 {"InsertLeavePre", EVENT_INSERTLEAVEPRE},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100151 {"InsertCharPre", EVENT_INSERTCHARPRE},
152 {"MenuPopup", EVENT_MENUPOPUP},
153 {"OptionSet", EVENT_OPTIONSET},
154 {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST},
155 {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
156 {"QuitPre", EVENT_QUITPRE},
157 {"RemoteReply", EVENT_REMOTEREPLY},
Bram Moolenaar8aeec402019-09-15 23:02:04 +0200158 {"SafeState", EVENT_SAFESTATE},
Bram Moolenaar69198cb2019-09-16 21:58:13 +0200159 {"SafeStateAgain", EVENT_SAFESTATEAGAIN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100160 {"SessionLoadPost", EVENT_SESSIONLOADPOST},
161 {"ShellCmdPost", EVENT_SHELLCMDPOST},
162 {"ShellFilterPost", EVENT_SHELLFILTERPOST},
Bram Moolenaarbe5ee862020-06-10 20:56:58 +0200163 {"SigUSR1", EVENT_SIGUSR1},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100164 {"SourceCmd", EVENT_SOURCECMD},
165 {"SourcePre", EVENT_SOURCEPRE},
166 {"SourcePost", EVENT_SOURCEPOST},
167 {"SpellFileMissing",EVENT_SPELLFILEMISSING},
168 {"StdinReadPost", EVENT_STDINREADPOST},
169 {"StdinReadPre", EVENT_STDINREADPRE},
170 {"SwapExists", EVENT_SWAPEXISTS},
171 {"Syntax", EVENT_SYNTAX},
172 {"TabNew", EVENT_TABNEW},
173 {"TabClosed", EVENT_TABCLOSED},
174 {"TabEnter", EVENT_TABENTER},
175 {"TabLeave", EVENT_TABLEAVE},
176 {"TermChanged", EVENT_TERMCHANGED},
177 {"TerminalOpen", EVENT_TERMINALOPEN},
Bram Moolenaar28ed4df2019-10-26 16:21:40 +0200178 {"TerminalWinOpen", EVENT_TERMINALWINOPEN},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100179 {"TermResponse", EVENT_TERMRESPONSE},
180 {"TextChanged", EVENT_TEXTCHANGED},
181 {"TextChangedI", EVENT_TEXTCHANGEDI},
182 {"TextChangedP", EVENT_TEXTCHANGEDP},
183 {"User", EVENT_USER},
184 {"VimEnter", EVENT_VIMENTER},
185 {"VimLeave", EVENT_VIMLEAVE},
186 {"VimLeavePre", EVENT_VIMLEAVEPRE},
187 {"WinNew", EVENT_WINNEW},
188 {"WinEnter", EVENT_WINENTER},
189 {"WinLeave", EVENT_WINLEAVE},
190 {"VimResized", EVENT_VIMRESIZED},
191 {"TextYankPost", EVENT_TEXTYANKPOST},
Bram Moolenaar100118c2020-12-11 19:30:34 +0100192 {"VimSuspend", EVENT_VIMSUSPEND},
193 {"VimResume", EVENT_VIMRESUME},
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100194 {NULL, (event_T)0}
195};
196
197static AutoPat *first_autopat[NUM_EVENTS] =
198{
199 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
200 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
201 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
202 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
203 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
204 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
205};
206
207static AutoPat *last_autopat[NUM_EVENTS] =
208{
209 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
210 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
211 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
212 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
213 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
215};
216
217#define AUGROUP_DEFAULT -1 // default autocmd group
218#define AUGROUP_ERROR -2 // erroneous autocmd group
219#define AUGROUP_ALL -3 // all autocmd groups
220
221/*
222 * struct used to keep status while executing autocommands for an event.
223 */
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100224struct AutoPatCmd_S
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100225{
226 AutoPat *curpat; // next AutoPat to examine
227 AutoCmd *nextcmd; // next AutoCmd to execute
228 int group; // group being used
229 char_u *fname; // fname to match with
230 char_u *sfname; // sfname to match with
231 char_u *tail; // tail of fname
232 event_T event; // current event
233 int arg_bufnr; // Initially equal to <abuf>, set to zero when
234 // buf is deleted.
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100235 AutoPatCmd *next; // chain of active apc-s for auto-invalidation
236};
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100237
Bram Moolenaarc667da52019-11-30 20:52:27 +0100238static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100239
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200240// Macro to loop over all the patterns for an autocmd event
241#define FOR_ALL_AUTOCMD_PATTERNS(event, ap) \
242 for ((ap) = first_autopat[(int)(event)]; (ap) != NULL; (ap) = (ap)->next)
243
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100244/*
245 * augroups stores a list of autocmd group names.
246 */
247static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
248#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
Bram Moolenaarc667da52019-11-30 20:52:27 +0100249// use get_deleted_augroup() to get this
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100250static char_u *deleted_augroup = NULL;
251
252/*
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100253 * The ID of the current group. Group 0 is the default one.
254 */
255static int current_augroup = AUGROUP_DEFAULT;
256
Bram Moolenaarc667da52019-11-30 20:52:27 +0100257static int au_need_clean = FALSE; // need to delete marked patterns
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100258
259static char_u *event_nr2name(event_T event);
260static int au_get_grouparg(char_u **argp);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200261static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100262static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
263static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
264static int au_find_group(char_u *name);
265
266static event_T last_event;
267static int last_group;
Bram Moolenaarc667da52019-11-30 20:52:27 +0100268static int autocmd_blocked = 0; // block all autocmds
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100269
270 static char_u *
271get_deleted_augroup(void)
272{
273 if (deleted_augroup == NULL)
274 deleted_augroup = (char_u *)_("--Deleted--");
275 return deleted_augroup;
276}
277
278/*
279 * Show the autocommands for one AutoPat.
280 */
281 static void
282show_autocmd(AutoPat *ap, event_T event)
283{
284 AutoCmd *ac;
285
286 // Check for "got_int" (here and at various places below), which is set
287 // when "q" has been hit for the "--more--" prompt
288 if (got_int)
289 return;
290 if (ap->pat == NULL) // pattern has been removed
291 return;
292
293 msg_putchar('\n');
294 if (got_int)
295 return;
296 if (event != last_event || ap->group != last_group)
297 {
298 if (ap->group != AUGROUP_DEFAULT)
299 {
300 if (AUGROUP_NAME(ap->group) == NULL)
301 msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
302 else
303 msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
304 msg_puts(" ");
305 }
306 msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
307 last_event = event;
308 last_group = ap->group;
309 msg_putchar('\n');
310 if (got_int)
311 return;
312 }
313 msg_col = 4;
314 msg_outtrans(ap->pat);
315
316 for (ac = ap->cmds; ac != NULL; ac = ac->next)
317 {
318 if (ac->cmd != NULL) // skip removed commands
319 {
320 if (msg_col >= 14)
321 msg_putchar('\n');
322 msg_col = 14;
323 if (got_int)
324 return;
325 msg_outtrans(ac->cmd);
326#ifdef FEAT_EVAL
327 if (p_verbose > 0)
328 last_set_msg(ac->script_ctx);
329#endif
330 if (got_int)
331 return;
332 if (ac->next != NULL)
333 {
334 msg_putchar('\n');
335 if (got_int)
336 return;
337 }
338 }
339 }
340}
341
342/*
343 * Mark an autocommand pattern for deletion.
344 */
345 static void
346au_remove_pat(AutoPat *ap)
347{
348 VIM_CLEAR(ap->pat);
349 ap->buflocal_nr = -1;
350 au_need_clean = TRUE;
351}
352
353/*
354 * Mark all commands for a pattern for deletion.
355 */
356 static void
357au_remove_cmds(AutoPat *ap)
358{
359 AutoCmd *ac;
360
361 for (ac = ap->cmds; ac != NULL; ac = ac->next)
362 VIM_CLEAR(ac->cmd);
363 au_need_clean = TRUE;
364}
365
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200366// Delete one command from an autocmd pattern.
367static void au_del_cmd(AutoCmd *ac)
368{
369 VIM_CLEAR(ac->cmd);
370 au_need_clean = TRUE;
371}
372
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100373/*
374 * Cleanup autocommands and patterns that have been deleted.
375 * This is only done when not executing autocommands.
376 */
377 static void
378au_cleanup(void)
379{
380 AutoPat *ap, **prev_ap;
381 AutoCmd *ac, **prev_ac;
382 event_T event;
383
384 if (autocmd_busy || !au_need_clean)
385 return;
386
387 // loop over all events
388 for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
389 event = (event_T)((int)event + 1))
390 {
391 // loop over all autocommand patterns
392 prev_ap = &(first_autopat[(int)event]);
393 for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
394 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200395 int has_cmd = FALSE;
396
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200397 // loop over all commands for this pattern
398 prev_ac = &(ap->cmds);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100399 for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
400 {
401 // remove the command if the pattern is to be deleted or when
402 // the command has been marked for deletion
403 if (ap->pat == NULL || ac->cmd == NULL)
404 {
405 *prev_ac = ac->next;
406 vim_free(ac->cmd);
407 vim_free(ac);
408 }
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200409 else
410 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200411 has_cmd = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100412 prev_ac = &(ac->next);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200413 }
414 }
415
Bram Moolenaar8f4aeb52019-04-04 15:40:56 +0200416 if (ap->pat != NULL && !has_cmd)
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200417 // Pattern was not marked for deletion, but all of its
418 // commands were. So mark the pattern for deletion.
419 au_remove_pat(ap);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100420
421 // remove the pattern if it has been marked for deletion
422 if (ap->pat == NULL)
423 {
424 if (ap->next == NULL)
425 {
426 if (prev_ap == &(first_autopat[(int)event]))
427 last_autopat[(int)event] = NULL;
428 else
429 // this depends on the "next" field being the first in
430 // the struct
431 last_autopat[(int)event] = (AutoPat *)prev_ap;
432 }
433 *prev_ap = ap->next;
434 vim_regfree(ap->reg_prog);
435 vim_free(ap);
436 }
437 else
438 prev_ap = &(ap->next);
439 }
440 }
441
442 au_need_clean = FALSE;
443}
444
445/*
446 * Called when buffer is freed, to remove/invalidate related buffer-local
447 * autocmds.
448 */
449 void
450aubuflocal_remove(buf_T *buf)
451{
452 AutoPat *ap;
453 event_T event;
454 AutoPatCmd *apc;
455
456 // invalidate currently executing autocommands
457 for (apc = active_apc_list; apc; apc = apc->next)
458 if (buf->b_fnum == apc->arg_bufnr)
459 apc->arg_bufnr = 0;
460
461 // invalidate buflocals looping through events
462 for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
463 event = (event_T)((int)event + 1))
464 // loop over all autocommand patterns
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200465 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100466 if (ap->buflocal_nr == buf->b_fnum)
467 {
468 au_remove_pat(ap);
469 if (p_verbose >= 6)
470 {
471 verbose_enter();
472 smsg(_("auto-removing autocommand: %s <buffer=%d>"),
473 event_nr2name(event), buf->b_fnum);
474 verbose_leave();
475 }
476 }
477 au_cleanup();
478}
479
480/*
481 * Add an autocmd group name.
482 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
483 */
484 static int
485au_new_group(char_u *name)
486{
487 int i;
488
489 i = au_find_group(name);
490 if (i == AUGROUP_ERROR) // the group doesn't exist yet, add it
491 {
492 // First try using a free entry.
493 for (i = 0; i < augroups.ga_len; ++i)
494 if (AUGROUP_NAME(i) == NULL)
495 break;
496 if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
497 return AUGROUP_ERROR;
498
499 AUGROUP_NAME(i) = vim_strsave(name);
500 if (AUGROUP_NAME(i) == NULL)
501 return AUGROUP_ERROR;
502 if (i == augroups.ga_len)
503 ++augroups.ga_len;
504 }
505
506 return i;
507}
508
509 static void
510au_del_group(char_u *name)
511{
512 int i;
513
514 i = au_find_group(name);
515 if (i == AUGROUP_ERROR) // the group doesn't exist
516 semsg(_("E367: No such group: \"%s\""), name);
517 else if (i == current_augroup)
518 emsg(_("E936: Cannot delete the current group"));
519 else
520 {
521 event_T event;
522 AutoPat *ap;
523 int in_use = FALSE;
524
525 for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
526 event = (event_T)((int)event + 1))
527 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200528 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100529 if (ap->group == i && ap->pat != NULL)
530 {
531 give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
532 in_use = TRUE;
533 event = NUM_EVENTS;
534 break;
535 }
536 }
537 vim_free(AUGROUP_NAME(i));
538 if (in_use)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100539 AUGROUP_NAME(i) = get_deleted_augroup();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100540 else
541 AUGROUP_NAME(i) = NULL;
542 }
543}
544
545/*
546 * Find the ID of an autocmd group name.
547 * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
548 */
549 static int
550au_find_group(char_u *name)
551{
552 int i;
553
554 for (i = 0; i < augroups.ga_len; ++i)
555 if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
556 && STRCMP(AUGROUP_NAME(i), name) == 0)
557 return i;
558 return AUGROUP_ERROR;
559}
560
561/*
562 * Return TRUE if augroup "name" exists.
563 */
564 int
565au_has_group(char_u *name)
566{
567 return au_find_group(name) != AUGROUP_ERROR;
568}
569
570/*
571 * ":augroup {name}".
572 */
573 void
574do_augroup(char_u *arg, int del_group)
575{
576 int i;
577
578 if (del_group)
579 {
580 if (*arg == NUL)
581 emsg(_(e_argreq));
582 else
583 au_del_group(arg);
584 }
585 else if (STRICMP(arg, "end") == 0) // ":aug end": back to group 0
586 current_augroup = AUGROUP_DEFAULT;
587 else if (*arg) // ":aug xxx": switch to group xxx
588 {
589 i = au_new_group(arg);
590 if (i != AUGROUP_ERROR)
591 current_augroup = i;
592 }
593 else // ":aug": list the group names
594 {
595 msg_start();
596 for (i = 0; i < augroups.ga_len; ++i)
597 {
598 if (AUGROUP_NAME(i) != NULL)
599 {
600 msg_puts((char *)AUGROUP_NAME(i));
601 msg_puts(" ");
602 }
603 }
604 msg_clr_eos();
605 msg_end();
606 }
607}
608
609#if defined(EXITFREE) || defined(PROTO)
610 void
611free_all_autocmds(void)
612{
613 int i;
614 char_u *s;
615
616 for (current_augroup = -1; current_augroup < augroups.ga_len;
617 ++current_augroup)
618 do_autocmd((char_u *)"", TRUE);
619
620 for (i = 0; i < augroups.ga_len; ++i)
621 {
622 s = ((char_u **)(augroups.ga_data))[i];
623 if (s != get_deleted_augroup())
624 vim_free(s);
625 }
626 ga_clear(&augroups);
627}
628#endif
629
630/*
631 * Return the event number for event name "start".
632 * Return NUM_EVENTS if the event name was not found.
633 * Return a pointer to the next event name in "end".
634 */
635 static event_T
636event_name2nr(char_u *start, char_u **end)
637{
638 char_u *p;
639 int i;
640 int len;
641
642 // the event name ends with end of line, '|', a blank or a comma
643 for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
644 ;
645 for (i = 0; event_names[i].name != NULL; ++i)
646 {
647 len = (int)STRLEN(event_names[i].name);
648 if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
649 break;
650 }
651 if (*p == ',')
652 ++p;
653 *end = p;
654 if (event_names[i].name == NULL)
655 return NUM_EVENTS;
656 return event_names[i].event;
657}
658
659/*
660 * Return the name for event "event".
661 */
662 static char_u *
663event_nr2name(event_T event)
664{
665 int i;
666
667 for (i = 0; event_names[i].name != NULL; ++i)
668 if (event_names[i].event == event)
669 return (char_u *)event_names[i].name;
670 return (char_u *)"Unknown";
671}
672
673/*
674 * Scan over the events. "*" stands for all events.
675 */
676 static char_u *
677find_end_event(
678 char_u *arg,
679 int have_group) // TRUE when group name was found
680{
681 char_u *pat;
682 char_u *p;
683
684 if (*arg == '*')
685 {
686 if (arg[1] && !VIM_ISWHITE(arg[1]))
687 {
688 semsg(_("E215: Illegal character after *: %s"), arg);
689 return NULL;
690 }
691 pat = arg + 1;
692 }
693 else
694 {
695 for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
696 {
697 if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS)
698 {
699 if (have_group)
700 semsg(_("E216: No such event: %s"), pat);
701 else
702 semsg(_("E216: No such group or event: %s"), pat);
703 return NULL;
704 }
705 }
706 }
707 return pat;
708}
709
710/*
711 * Return TRUE if "event" is included in 'eventignore'.
712 */
713 static int
714event_ignored(event_T event)
715{
716 char_u *p = p_ei;
717
718 while (*p != NUL)
719 {
720 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
721 return TRUE;
722 if (event_name2nr(p, &p) == event)
723 return TRUE;
724 }
725
726 return FALSE;
727}
728
729/*
730 * Return OK when the contents of p_ei is valid, FAIL otherwise.
731 */
732 int
733check_ei(void)
734{
735 char_u *p = p_ei;
736
737 while (*p)
738 {
739 if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
740 {
741 p += 3;
742 if (*p == ',')
743 ++p;
744 }
745 else if (event_name2nr(p, &p) == NUM_EVENTS)
746 return FAIL;
747 }
748
749 return OK;
750}
751
752# if defined(FEAT_SYN_HL) || defined(PROTO)
753
754/*
755 * Add "what" to 'eventignore' to skip loading syntax highlighting for every
756 * buffer loaded into the window. "what" must start with a comma.
757 * Returns the old value of 'eventignore' in allocated memory.
758 */
759 char_u *
760au_event_disable(char *what)
761{
762 char_u *new_ei;
763 char_u *save_ei;
764
765 save_ei = vim_strsave(p_ei);
766 if (save_ei != NULL)
767 {
Bram Moolenaardf44a272020-06-07 20:49:05 +0200768 new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100769 if (new_ei != NULL)
770 {
771 if (*what == ',' && *p_ei == NUL)
772 STRCPY(new_ei, what + 1);
773 else
774 STRCAT(new_ei, what);
775 set_string_option_direct((char_u *)"ei", -1, new_ei,
776 OPT_FREE, SID_NONE);
777 vim_free(new_ei);
778 }
779 }
780 return save_ei;
781}
782
783 void
784au_event_restore(char_u *old_ei)
785{
786 if (old_ei != NULL)
787 {
788 set_string_option_direct((char_u *)"ei", -1, old_ei,
789 OPT_FREE, SID_NONE);
790 vim_free(old_ei);
791 }
792}
Bram Moolenaarc667da52019-11-30 20:52:27 +0100793# endif // FEAT_SYN_HL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100794
795/*
796 * do_autocmd() -- implements the :autocmd command. Can be used in the
797 * following ways:
798 *
799 * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
800 * will be automatically executed for <event>
801 * when editing a file matching <pat>, in
802 * the current group.
803 * :autocmd <event> <pat> Show the autocommands associated with
804 * <event> and <pat>.
805 * :autocmd <event> Show the autocommands associated with
806 * <event>.
807 * :autocmd Show all autocommands.
808 * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
809 * <event> and <pat>, and add the command
810 * <cmd>, for the current group.
811 * :autocmd! <event> <pat> Remove all autocommands associated with
812 * <event> and <pat> for the current group.
813 * :autocmd! <event> Remove all autocommands associated with
814 * <event> for the current group.
815 * :autocmd! Remove ALL autocommands for the current
816 * group.
817 *
818 * Multiple events and patterns may be given separated by commas. Here are
819 * some examples:
820 * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
821 * :autocmd bufleave * set tw=79 nosmartindent ic infercase
822 *
823 * :autocmd * *.c show all autocommands for *.c files.
824 *
825 * Mostly a {group} argument can optionally appear before <event>.
826 */
827 void
828do_autocmd(char_u *arg_in, int forceit)
829{
830 char_u *arg = arg_in;
831 char_u *pat;
832 char_u *envpat = NULL;
833 char_u *cmd;
834 event_T event;
835 int need_free = FALSE;
836 int nested = FALSE;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200837 int once = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100838 int group;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200839 int i;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100840
841 if (*arg == '|')
842 {
843 arg = (char_u *)"";
844 group = AUGROUP_ALL; // no argument, use all groups
845 }
846 else
847 {
848 /*
849 * Check for a legal group name. If not, use AUGROUP_ALL.
850 */
851 group = au_get_grouparg(&arg);
852 if (arg == NULL) // out of memory
853 return;
854 }
855
856 /*
857 * Scan over the events.
858 * If we find an illegal name, return here, don't do anything.
859 */
860 pat = find_end_event(arg, group != AUGROUP_ALL);
861 if (pat == NULL)
862 return;
863
864 pat = skipwhite(pat);
865 if (*pat == '|')
866 {
867 pat = (char_u *)"";
868 cmd = (char_u *)"";
869 }
870 else
871 {
872 /*
873 * Scan over the pattern. Put a NUL at the end.
874 */
875 cmd = pat;
876 while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
877 cmd++;
878 if (*cmd)
879 *cmd++ = NUL;
880
881 // Expand environment variables in the pattern. Set 'shellslash', we
882 // want forward slashes here.
883 if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
884 {
885#ifdef BACKSLASH_IN_FILENAME
886 int p_ssl_save = p_ssl;
887
888 p_ssl = TRUE;
889#endif
890 envpat = expand_env_save(pat);
891#ifdef BACKSLASH_IN_FILENAME
892 p_ssl = p_ssl_save;
893#endif
894 if (envpat != NULL)
895 pat = envpat;
896 }
897
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100898 cmd = skipwhite(cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200899 for (i = 0; i < 2; i++)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100900 {
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200901 if (*cmd != NUL)
902 {
903 // Check for "++once" flag.
904 if (STRNCMP(cmd, "++once", 6) == 0 && VIM_ISWHITE(cmd[6]))
905 {
906 if (once)
907 semsg(_(e_duparg2), "++once");
908 once = TRUE;
909 cmd = skipwhite(cmd + 6);
910 }
911
912 // Check for "++nested" flag.
913 if ((STRNCMP(cmd, "++nested", 8) == 0 && VIM_ISWHITE(cmd[8])))
914 {
915 if (nested)
916 semsg(_(e_duparg2), "++nested");
917 nested = TRUE;
918 cmd = skipwhite(cmd + 8);
919 }
920
921 // Check for the old "nested" flag.
922 if (STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
923 {
924 if (nested)
925 semsg(_(e_duparg2), "nested");
926 nested = TRUE;
927 cmd = skipwhite(cmd + 6);
928 }
929 }
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100930 }
931
932 /*
933 * Find the start of the commands.
934 * Expand <sfile> in it.
935 */
936 if (*cmd != NUL)
937 {
938 cmd = expand_sfile(cmd);
939 if (cmd == NULL) // some error
940 return;
941 need_free = TRUE;
942 }
943 }
944
945 /*
946 * Print header when showing autocommands.
947 */
948 if (!forceit && *cmd == NUL)
949 // Highlight title
950 msg_puts_title(_("\n--- Autocommands ---"));
951
952 /*
953 * Loop over the events.
954 */
955 last_event = (event_T)-1; // for listing the event name
956 last_group = AUGROUP_ERROR; // for listing the group name
957 if (*arg == '*' || *arg == NUL || *arg == '|')
958 {
959 for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
960 event = (event_T)((int)event + 1))
961 if (do_autocmd_event(event, pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200962 once, nested, cmd, forceit, group) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100963 break;
964 }
965 else
966 {
967 while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
968 if (do_autocmd_event(event_name2nr(arg, &arg), pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +0200969 once, nested, cmd, forceit, group) == FAIL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100970 break;
971 }
972
973 if (need_free)
974 vim_free(cmd);
975 vim_free(envpat);
976}
977
978/*
979 * Find the group ID in a ":autocmd" or ":doautocmd" argument.
980 * The "argp" argument is advanced to the following argument.
981 *
982 * Returns the group ID, AUGROUP_ERROR for error (out of memory).
983 */
984 static int
985au_get_grouparg(char_u **argp)
986{
987 char_u *group_name;
988 char_u *p;
989 char_u *arg = *argp;
990 int group = AUGROUP_ALL;
991
992 for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
993 ;
994 if (p > arg)
995 {
Bram Moolenaardf44a272020-06-07 20:49:05 +0200996 group_name = vim_strnsave(arg, p - arg);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +0100997 if (group_name == NULL) // out of memory
998 return AUGROUP_ERROR;
999 group = au_find_group(group_name);
1000 if (group == AUGROUP_ERROR)
1001 group = AUGROUP_ALL; // no match, use all groups
1002 else
1003 *argp = skipwhite(p); // match, skip over group name
1004 vim_free(group_name);
1005 }
1006 return group;
1007}
1008
1009/*
1010 * do_autocmd() for one event.
1011 * If *pat == NUL do for all patterns.
1012 * If *cmd == NUL show entries.
1013 * If forceit == TRUE delete entries.
1014 * If group is not AUGROUP_ALL, only use this group.
1015 */
1016 static int
1017do_autocmd_event(
1018 event_T event,
1019 char_u *pat,
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001020 int once,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001021 int nested,
1022 char_u *cmd,
1023 int forceit,
1024 int group)
1025{
1026 AutoPat *ap;
1027 AutoPat **prev_ap;
1028 AutoCmd *ac;
1029 AutoCmd **prev_ac;
1030 int brace_level;
1031 char_u *endpat;
1032 int findgroup;
1033 int allgroups;
1034 int patlen;
1035 int is_buflocal;
1036 int buflocal_nr;
Bram Moolenaarc667da52019-11-30 20:52:27 +01001037 char_u buflocal_pat[25]; // for "<buffer=X>"
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001038
1039 if (group == AUGROUP_ALL)
1040 findgroup = current_augroup;
1041 else
1042 findgroup = group;
1043 allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
1044
1045 /*
1046 * Show or delete all patterns for an event.
1047 */
1048 if (*pat == NUL)
1049 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001050 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001051 {
1052 if (forceit) // delete the AutoPat, if it's in the current group
1053 {
1054 if (ap->group == findgroup)
1055 au_remove_pat(ap);
1056 }
1057 else if (group == AUGROUP_ALL || ap->group == group)
1058 show_autocmd(ap, event);
1059 }
1060 }
1061
1062 /*
1063 * Loop through all the specified patterns.
1064 */
1065 for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
1066 {
1067 /*
1068 * Find end of the pattern.
1069 * Watch out for a comma in braces, like "*.\{obj,o\}".
1070 */
1071 brace_level = 0;
1072 for (endpat = pat; *endpat && (*endpat != ',' || brace_level
1073 || (endpat > pat && endpat[-1] == '\\')); ++endpat)
1074 {
1075 if (*endpat == '{')
1076 brace_level++;
1077 else if (*endpat == '}')
1078 brace_level--;
1079 }
1080 if (pat == endpat) // ignore single comma
1081 continue;
1082 patlen = (int)(endpat - pat);
1083
1084 /*
1085 * detect special <buflocal[=X]> buffer-local patterns
1086 */
1087 is_buflocal = FALSE;
1088 buflocal_nr = 0;
1089
1090 if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
1091 && pat[patlen - 1] == '>')
1092 {
1093 // "<buffer...>": Error will be printed only for addition.
1094 // printing and removing will proceed silently.
1095 is_buflocal = TRUE;
1096 if (patlen == 8)
1097 // "<buffer>"
1098 buflocal_nr = curbuf->b_fnum;
1099 else if (patlen > 9 && pat[7] == '=')
1100 {
1101 if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
1102 // "<buffer=abuf>"
1103 buflocal_nr = autocmd_bufnr;
1104 else if (skipdigits(pat + 8) == pat + patlen - 1)
1105 // "<buffer=123>"
1106 buflocal_nr = atoi((char *)pat + 8);
1107 }
1108 }
1109
1110 if (is_buflocal)
1111 {
1112 // normalize pat into standard "<buffer>#N" form
1113 sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
1114 pat = buflocal_pat; // can modify pat and patlen
1115 patlen = (int)STRLEN(buflocal_pat); // but not endpat
1116 }
1117
1118 /*
1119 * Find AutoPat entries with this pattern. When adding a command it
1120 * always goes at or after the last one, so start at the end.
1121 */
1122 if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
1123 prev_ap = &last_autopat[(int)event];
1124 else
1125 prev_ap = &first_autopat[(int)event];
1126 while ((ap = *prev_ap) != NULL)
1127 {
1128 if (ap->pat != NULL)
1129 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001130 /*
1131 * Accept a pattern when:
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001132 * - a group was specified and it's that group, or a group was
1133 * not specified and it's the current group, or a group was
1134 * not specified and we are listing
1135 * - the length of the pattern matches
1136 * - the pattern matches.
1137 * For <buffer[=X]>, this condition works because we normalize
1138 * all buffer-local patterns.
1139 */
1140 if ((allgroups || ap->group == findgroup)
1141 && ap->patlen == patlen
1142 && STRNCMP(pat, ap->pat, patlen) == 0)
1143 {
1144 /*
1145 * Remove existing autocommands.
1146 * If adding any new autocmd's for this AutoPat, don't
1147 * delete the pattern from the autopat list, append to
1148 * this list.
1149 */
1150 if (forceit)
1151 {
1152 if (*cmd != NUL && ap->next == NULL)
1153 {
1154 au_remove_cmds(ap);
1155 break;
1156 }
1157 au_remove_pat(ap);
1158 }
1159
1160 /*
1161 * Show autocmd's for this autopat, or buflocals <buffer=X>
1162 */
1163 else if (*cmd == NUL)
1164 show_autocmd(ap, event);
1165
1166 /*
1167 * Add autocmd to this autopat, if it's the last one.
1168 */
1169 else if (ap->next == NULL)
1170 break;
1171 }
1172 }
1173 prev_ap = &ap->next;
1174 }
1175
1176 /*
1177 * Add a new command.
1178 */
1179 if (*cmd != NUL)
1180 {
1181 /*
1182 * If the pattern we want to add a command to does appear at the
1183 * end of the list (or not is not in the list at all), add the
1184 * pattern at the end of the list.
1185 */
1186 if (ap == NULL)
1187 {
Bram Moolenaarc667da52019-11-30 20:52:27 +01001188 // refuse to add buffer-local ap if buffer number is invalid
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001189 if (is_buflocal && (buflocal_nr == 0
1190 || buflist_findnr(buflocal_nr) == NULL))
1191 {
1192 semsg(_("E680: <buffer=%d>: invalid buffer number "),
1193 buflocal_nr);
1194 return FAIL;
1195 }
1196
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001197 ap = ALLOC_ONE(AutoPat);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001198 if (ap == NULL)
1199 return FAIL;
1200 ap->pat = vim_strnsave(pat, patlen);
1201 ap->patlen = patlen;
1202 if (ap->pat == NULL)
1203 {
1204 vim_free(ap);
1205 return FAIL;
1206 }
1207
1208 if (is_buflocal)
1209 {
1210 ap->buflocal_nr = buflocal_nr;
1211 ap->reg_prog = NULL;
1212 }
1213 else
1214 {
1215 char_u *reg_pat;
1216
1217 ap->buflocal_nr = 0;
1218 reg_pat = file_pat_to_reg_pat(pat, endpat,
1219 &ap->allow_dirs, TRUE);
1220 if (reg_pat != NULL)
1221 ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
1222 vim_free(reg_pat);
1223 if (reg_pat == NULL || ap->reg_prog == NULL)
1224 {
1225 vim_free(ap->pat);
1226 vim_free(ap);
1227 return FAIL;
1228 }
1229 }
1230 ap->cmds = NULL;
1231 *prev_ap = ap;
1232 last_autopat[(int)event] = ap;
1233 ap->next = NULL;
1234 if (group == AUGROUP_ALL)
1235 ap->group = current_augroup;
1236 else
1237 ap->group = group;
1238 }
1239
1240 /*
1241 * Add the autocmd at the end of the AutoCmd list.
1242 */
1243 prev_ac = &(ap->cmds);
1244 while ((ac = *prev_ac) != NULL)
1245 prev_ac = &ac->next;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001246 ac = ALLOC_ONE(AutoCmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001247 if (ac == NULL)
1248 return FAIL;
1249 ac->cmd = vim_strsave(cmd);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001250 ac->script_ctx = current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001251#ifdef FEAT_EVAL
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001252 ac->script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001253#endif
1254 if (ac->cmd == NULL)
1255 {
1256 vim_free(ac);
1257 return FAIL;
1258 }
1259 ac->next = NULL;
1260 *prev_ac = ac;
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02001261 ac->once = once;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001262 ac->nested = nested;
1263 }
1264 }
1265
1266 au_cleanup(); // may really delete removed patterns/commands now
1267 return OK;
1268}
1269
1270/*
1271 * Implementation of ":doautocmd [group] event [fname]".
1272 * Return OK for success, FAIL for failure;
1273 */
1274 int
1275do_doautocmd(
1276 char_u *arg,
1277 int do_msg, // give message for no matching autocmds?
1278 int *did_something)
1279{
1280 char_u *fname;
1281 int nothing_done = TRUE;
1282 int group;
1283
1284 if (did_something != NULL)
1285 *did_something = FALSE;
1286
1287 /*
1288 * Check for a legal group name. If not, use AUGROUP_ALL.
1289 */
1290 group = au_get_grouparg(&arg);
1291 if (arg == NULL) // out of memory
1292 return FAIL;
1293
1294 if (*arg == '*')
1295 {
1296 emsg(_("E217: Can't execute autocommands for ALL events"));
1297 return FAIL;
1298 }
1299
1300 /*
1301 * Scan over the events.
1302 * If we find an illegal name, return here, don't do anything.
1303 */
1304 fname = find_end_event(arg, group != AUGROUP_ALL);
1305 if (fname == NULL)
1306 return FAIL;
1307
1308 fname = skipwhite(fname);
1309
1310 /*
1311 * Loop over the events.
1312 */
1313 while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
1314 if (apply_autocmds_group(event_name2nr(arg, &arg),
1315 fname, NULL, TRUE, group, curbuf, NULL))
1316 nothing_done = FALSE;
1317
1318 if (nothing_done && do_msg)
1319 msg(_("No matching autocommands"));
1320 if (did_something != NULL)
1321 *did_something = !nothing_done;
1322
1323#ifdef FEAT_EVAL
1324 return aborting() ? FAIL : OK;
1325#else
1326 return OK;
1327#endif
1328}
1329
1330/*
1331 * ":doautoall": execute autocommands for each loaded buffer.
1332 */
1333 void
1334ex_doautoall(exarg_T *eap)
1335{
1336 int retval;
1337 aco_save_T aco;
1338 buf_T *buf;
1339 bufref_T bufref;
1340 char_u *arg = eap->arg;
1341 int call_do_modelines = check_nomodeline(&arg);
1342 int did_aucmd;
1343
1344 /*
1345 * This is a bit tricky: For some commands curwin->w_buffer needs to be
1346 * equal to curbuf, but for some buffers there may not be a window.
1347 * So we change the buffer for the current window for a moment. This
1348 * gives problems when the autocommands make changes to the list of
1349 * buffers or windows...
1350 */
1351 FOR_ALL_BUFFERS(buf)
1352 {
Bram Moolenaar89adc3a2019-05-30 17:29:40 +02001353 if (buf->b_ml.ml_mfp != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001354 {
1355 // find a window for this buffer and save some values
1356 aucmd_prepbuf(&aco, buf);
1357 set_bufref(&bufref, buf);
1358
1359 // execute the autocommands for this buffer
1360 retval = do_doautocmd(arg, FALSE, &did_aucmd);
1361
1362 if (call_do_modelines && did_aucmd)
1363 {
1364 // Execute the modeline settings, but don't set window-local
1365 // options if we are using the current window for another
1366 // buffer.
1367 do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
1368 }
1369
1370 // restore the current window
1371 aucmd_restbuf(&aco);
1372
1373 // stop if there is some error or buffer was deleted
1374 if (retval == FAIL || !bufref_valid(&bufref))
1375 break;
1376 }
1377 }
1378
1379 check_cursor(); // just in case lines got deleted
1380}
1381
1382/*
1383 * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
1384 * return TRUE and advance *argp to after it.
1385 * Thus return TRUE when do_modelines() should be called.
1386 */
1387 int
1388check_nomodeline(char_u **argp)
1389{
1390 if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
1391 {
1392 *argp = skipwhite(*argp + 12);
1393 return FALSE;
1394 }
1395 return TRUE;
1396}
1397
1398/*
1399 * Prepare for executing autocommands for (hidden) buffer "buf".
1400 * Search for a visible window containing the current buffer. If there isn't
1401 * one then use "aucmd_win".
1402 * Set "curbuf" and "curwin" to match "buf".
1403 */
1404 void
1405aucmd_prepbuf(
1406 aco_save_T *aco, // structure to save values in
1407 buf_T *buf) // new curbuf
1408{
1409 win_T *win;
1410 int save_ea;
1411#ifdef FEAT_AUTOCHDIR
1412 int save_acd;
1413#endif
1414
1415 // Find a window that is for the new buffer
1416 if (buf == curbuf) // be quick when buf is curbuf
1417 win = curwin;
1418 else
1419 FOR_ALL_WINDOWS(win)
1420 if (win->w_buffer == buf)
1421 break;
1422
1423 // Allocate "aucmd_win" when needed. If this fails (out of memory) fall
1424 // back to using the current window.
1425 if (win == NULL && aucmd_win == NULL)
1426 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001427 aucmd_win = win_alloc_popup_win();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001428 if (aucmd_win == NULL)
1429 win = curwin;
1430 }
1431 if (win == NULL && aucmd_win_used)
1432 // Strange recursive autocommand, fall back to using the current
1433 // window. Expect a few side effects...
1434 win = curwin;
1435
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001436 aco->save_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001437 aco->save_curbuf = curbuf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001438 aco->save_prevwin_id = prevwin == NULL ? 0 : prevwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001439 if (win != NULL)
1440 {
1441 // There is a window for "buf" in the current tab page, make it the
1442 // curwin. This is preferred, it has the least side effects (esp. if
1443 // "buf" is curbuf).
1444 aco->use_aucmd_win = FALSE;
1445 curwin = win;
1446 }
1447 else
1448 {
1449 // There is no window for "buf", use "aucmd_win". To minimize the side
1450 // effects, insert it in the current tab page.
1451 // Anything related to a window (e.g., setting folds) may have
1452 // unexpected results.
1453 aco->use_aucmd_win = TRUE;
1454 aucmd_win_used = TRUE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001455
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001456 win_init_popup_win(aucmd_win, buf);
1457
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001458 aco->globaldir = globaldir;
1459 globaldir = NULL;
1460
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001461 // Split the current window, put the aucmd_win in the upper half.
1462 // We don't want the BufEnter or WinEnter autocommands.
1463 block_autocmds();
1464 make_snapshot(SNAP_AUCMD_IDX);
1465 save_ea = p_ea;
1466 p_ea = FALSE;
1467
1468#ifdef FEAT_AUTOCHDIR
1469 // Prevent chdir() call in win_enter_ext(), through do_autochdir().
1470 save_acd = p_acd;
1471 p_acd = FALSE;
1472#endif
1473
1474 (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
1475 (void)win_comp_pos(); // recompute window positions
1476 p_ea = save_ea;
1477#ifdef FEAT_AUTOCHDIR
1478 p_acd = save_acd;
1479#endif
1480 unblock_autocmds();
1481 curwin = aucmd_win;
1482 }
1483 curbuf = buf;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001484 aco->new_curwin_id = curwin->w_id;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001485 set_bufref(&aco->new_curbuf, curbuf);
1486}
1487
1488/*
1489 * Cleanup after executing autocommands for a (hidden) buffer.
1490 * Restore the window as it was (if possible).
1491 */
1492 void
1493aucmd_restbuf(
1494 aco_save_T *aco) // structure holding saved values
1495{
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001496 int dummy;
1497 win_T *save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001498
1499 if (aco->use_aucmd_win)
1500 {
1501 --curbuf->b_nwindows;
1502 // Find "aucmd_win", it can't be closed, but it may be in another tab
1503 // page. Do not trigger autocommands here.
1504 block_autocmds();
1505 if (curwin != aucmd_win)
1506 {
1507 tabpage_T *tp;
1508 win_T *wp;
1509
1510 FOR_ALL_TAB_WINDOWS(tp, wp)
1511 {
1512 if (wp == aucmd_win)
1513 {
1514 if (tp != curtab)
1515 goto_tabpage_tp(tp, TRUE, TRUE);
1516 win_goto(aucmd_win);
1517 goto win_found;
1518 }
1519 }
1520 }
1521win_found:
1522
1523 // Remove the window and frame from the tree of frames.
1524 (void)winframe_remove(curwin, &dummy, NULL);
1525 win_remove(curwin, NULL);
1526 aucmd_win_used = FALSE;
1527 last_status(FALSE); // may need to remove last status line
1528
1529 if (!valid_tabpage_win(curtab))
1530 // no valid window in current tabpage
1531 close_tabpage(curtab);
1532
1533 restore_snapshot(SNAP_AUCMD_IDX, FALSE);
1534 (void)win_comp_pos(); // recompute window positions
1535 unblock_autocmds();
1536
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001537 save_curwin = win_find_by_id(aco->save_curwin_id);
1538 if (save_curwin != NULL)
1539 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001540 else
1541 // Hmm, original window disappeared. Just use the first one.
1542 curwin = firstwin;
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02001543 curbuf = curwin->w_buffer;
1544#ifdef FEAT_JOB_CHANNEL
1545 // May need to restore insert mode for a prompt buffer.
1546 entering_window(curwin);
1547#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001548 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001549#ifdef FEAT_EVAL
1550 vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
1551 hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
1552#endif
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001553 vim_free(globaldir);
1554 globaldir = aco->globaldir;
1555
1556 // the buffer contents may have changed
1557 check_cursor();
1558 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1559 {
1560 curwin->w_topline = curbuf->b_ml.ml_line_count;
1561#ifdef FEAT_DIFF
1562 curwin->w_topfill = 0;
1563#endif
1564 }
1565#if defined(FEAT_GUI)
1566 // Hide the scrollbars from the aucmd_win and update.
1567 gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
1568 gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
1569 gui_may_update_scrollbars();
1570#endif
1571 }
1572 else
1573 {
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001574 // Restore curwin. Use the window ID, a window may have been closed
1575 // and the memory re-used for another one.
1576 save_curwin = win_find_by_id(aco->save_curwin_id);
1577 if (save_curwin != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001578 {
1579 // Restore the buffer which was previously edited by curwin, if
1580 // it was changed, we are still the same window and the buffer is
1581 // valid.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001582 if (curwin->w_id == aco->new_curwin_id
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001583 && curbuf != aco->new_curbuf.br_buf
1584 && bufref_valid(&aco->new_curbuf)
1585 && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
1586 {
1587# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
1588 if (curwin->w_s == &curbuf->b_s)
1589 curwin->w_s = &aco->new_curbuf.br_buf->b_s;
1590# endif
1591 --curbuf->b_nwindows;
1592 curbuf = aco->new_curbuf.br_buf;
1593 curwin->w_buffer = curbuf;
1594 ++curbuf->b_nwindows;
1595 }
1596
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001597 curwin = save_curwin;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001598 curbuf = curwin->w_buffer;
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001599 prevwin = win_find_by_id(aco->save_prevwin_id);
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001600 // In case the autocommand moves the cursor to a position that
1601 // does not exist in curbuf.
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001602 check_cursor();
1603 }
1604 }
1605}
1606
1607static int autocmd_nested = FALSE;
1608
1609/*
1610 * Execute autocommands for "event" and file name "fname".
1611 * Return TRUE if some commands were executed.
1612 */
1613 int
1614apply_autocmds(
1615 event_T event,
1616 char_u *fname, // NULL or empty means use actual file name
1617 char_u *fname_io, // fname to use for <afile> on cmdline
1618 int force, // when TRUE, ignore autocmd_busy
1619 buf_T *buf) // buffer for <abuf>
1620{
1621 return apply_autocmds_group(event, fname, fname_io, force,
1622 AUGROUP_ALL, buf, NULL);
1623}
1624
1625/*
1626 * Like apply_autocmds(), but with extra "eap" argument. This takes care of
1627 * setting v:filearg.
1628 */
1629 int
1630apply_autocmds_exarg(
1631 event_T event,
1632 char_u *fname,
1633 char_u *fname_io,
1634 int force,
1635 buf_T *buf,
1636 exarg_T *eap)
1637{
1638 return apply_autocmds_group(event, fname, fname_io, force,
1639 AUGROUP_ALL, buf, eap);
1640}
1641
1642/*
1643 * Like apply_autocmds(), but handles the caller's retval. If the script
1644 * processing is being aborted or if retval is FAIL when inside a try
1645 * conditional, no autocommands are executed. If otherwise the autocommands
1646 * cause the script to be aborted, retval is set to FAIL.
1647 */
1648 int
1649apply_autocmds_retval(
1650 event_T event,
1651 char_u *fname, // NULL or empty means use actual file name
1652 char_u *fname_io, // fname to use for <afile> on cmdline
1653 int force, // when TRUE, ignore autocmd_busy
1654 buf_T *buf, // buffer for <abuf>
1655 int *retval) // pointer to caller's retval
1656{
1657 int did_cmd;
1658
1659#ifdef FEAT_EVAL
1660 if (should_abort(*retval))
1661 return FALSE;
1662#endif
1663
1664 did_cmd = apply_autocmds_group(event, fname, fname_io, force,
1665 AUGROUP_ALL, buf, NULL);
1666 if (did_cmd
1667#ifdef FEAT_EVAL
1668 && aborting()
1669#endif
1670 )
1671 *retval = FAIL;
1672 return did_cmd;
1673}
1674
1675/*
1676 * Return TRUE when there is a CursorHold autocommand defined.
1677 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001678 static int
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001679has_cursorhold(void)
1680{
1681 return (first_autopat[(int)(get_real_state() == NORMAL_BUSY
1682 ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
1683}
1684
1685/*
1686 * Return TRUE if the CursorHold event can be triggered.
1687 */
1688 int
1689trigger_cursorhold(void)
1690{
1691 int state;
1692
1693 if (!did_cursorhold
1694 && has_cursorhold()
1695 && reg_recording == 0
1696 && typebuf.tb_len == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001697 && !ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001698 {
1699 state = get_real_state();
1700 if (state == NORMAL_BUSY || (state & INSERT) != 0)
1701 return TRUE;
1702 }
1703 return FALSE;
1704}
1705
1706/*
1707 * Return TRUE when there is a CursorMoved autocommand defined.
1708 */
1709 int
1710has_cursormoved(void)
1711{
1712 return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
1713}
1714
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001715/*
1716 * Return TRUE when there is a CursorMovedI autocommand defined.
1717 */
1718 int
1719has_cursormovedI(void)
1720{
1721 return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
1722}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001723
1724/*
1725 * Return TRUE when there is a TextChanged autocommand defined.
1726 */
1727 int
1728has_textchanged(void)
1729{
1730 return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
1731}
1732
1733/*
1734 * Return TRUE when there is a TextChangedI autocommand defined.
1735 */
1736 int
1737has_textchangedI(void)
1738{
1739 return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
1740}
1741
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001742/*
1743 * Return TRUE when there is a TextChangedP autocommand defined.
1744 */
1745 int
1746has_textchangedP(void)
1747{
1748 return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
1749}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001750
1751/*
1752 * Return TRUE when there is an InsertCharPre autocommand defined.
1753 */
1754 int
1755has_insertcharpre(void)
1756{
1757 return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
1758}
1759
1760/*
1761 * Return TRUE when there is an CmdUndefined autocommand defined.
1762 */
1763 int
1764has_cmdundefined(void)
1765{
1766 return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
1767}
1768
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001769#if defined(FEAT_EVAL) || defined(PROTO)
1770/*
1771 * Return TRUE when there is a TextYankPost autocommand defined.
1772 */
1773 int
1774has_textyankpost(void)
1775{
1776 return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
1777}
1778#endif
1779
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001780#if defined(FEAT_EVAL) || defined(PROTO)
1781/*
1782 * Return TRUE when there is a CompleteChanged autocommand defined.
1783 */
1784 int
1785has_completechanged(void)
1786{
1787 return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
1788}
1789#endif
1790
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001791/*
1792 * Execute autocommands for "event" and file name "fname".
1793 * Return TRUE if some commands were executed.
1794 */
1795 static int
1796apply_autocmds_group(
1797 event_T event,
1798 char_u *fname, // NULL or empty means use actual file name
1799 char_u *fname_io, // fname to use for <afile> on cmdline, NULL means
1800 // use fname
1801 int force, // when TRUE, ignore autocmd_busy
1802 int group, // group ID, or AUGROUP_ALL
1803 buf_T *buf, // buffer for <abuf>
1804 exarg_T *eap UNUSED) // command arguments
1805{
1806 char_u *sfname = NULL; // short file name
1807 char_u *tail;
1808 int save_changed;
1809 buf_T *old_curbuf;
1810 int retval = FALSE;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001811 char_u *save_autocmd_fname;
1812 int save_autocmd_fname_full;
1813 int save_autocmd_bufnr;
1814 char_u *save_autocmd_match;
1815 int save_autocmd_busy;
1816 int save_autocmd_nested;
1817 static int nesting = 0;
1818 AutoPatCmd patcmd;
1819 AutoPat *ap;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001820 sctx_T save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01001821#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001822 funccal_entry_T funccal_entry;
1823 char_u *save_cmdarg;
1824 long save_cmdbang;
1825#endif
1826 static int filechangeshell_busy = FALSE;
1827#ifdef FEAT_PROFILE
1828 proftime_T wait_time;
1829#endif
1830 int did_save_redobuff = FALSE;
1831 save_redo_T save_redo;
1832 int save_KeyTyped = KeyTyped;
Bram Moolenaare31ee862020-01-07 20:59:34 +01001833 ESTACK_CHECK_DECLARATION
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01001834
1835 /*
1836 * Quickly return if there are no autocommands for this event or
1837 * autocommands are blocked.
1838 */
1839 if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
1840 || autocmd_blocked > 0)
1841 goto BYPASS_AU;
1842
1843 /*
1844 * When autocommands are busy, new autocommands are only executed when
1845 * explicitly enabled with the "nested" flag.
1846 */
1847 if (autocmd_busy && !(force || autocmd_nested))
1848 goto BYPASS_AU;
1849
1850#ifdef FEAT_EVAL
1851 /*
1852 * Quickly return when immediately aborting on error, or when an interrupt
1853 * occurred or an exception was thrown but not caught.
1854 */
1855 if (aborting())
1856 goto BYPASS_AU;
1857#endif
1858
1859 /*
1860 * FileChangedShell never nests, because it can create an endless loop.
1861 */
1862 if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
1863 || event == EVENT_FILECHANGEDSHELLPOST))
1864 goto BYPASS_AU;
1865
1866 /*
1867 * Ignore events in 'eventignore'.
1868 */
1869 if (event_ignored(event))
1870 goto BYPASS_AU;
1871
1872 /*
1873 * Allow nesting of autocommands, but restrict the depth, because it's
1874 * possible to create an endless loop.
1875 */
1876 if (nesting == 10)
1877 {
1878 emsg(_("E218: autocommand nesting too deep"));
1879 goto BYPASS_AU;
1880 }
1881
1882 /*
1883 * Check if these autocommands are disabled. Used when doing ":all" or
1884 * ":ball".
1885 */
1886 if ( (autocmd_no_enter
1887 && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
1888 || (autocmd_no_leave
1889 && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
1890 goto BYPASS_AU;
1891
1892 /*
1893 * Save the autocmd_* variables and info about the current buffer.
1894 */
1895 save_autocmd_fname = autocmd_fname;
1896 save_autocmd_fname_full = autocmd_fname_full;
1897 save_autocmd_bufnr = autocmd_bufnr;
1898 save_autocmd_match = autocmd_match;
1899 save_autocmd_busy = autocmd_busy;
1900 save_autocmd_nested = autocmd_nested;
1901 save_changed = curbuf->b_changed;
1902 old_curbuf = curbuf;
1903
1904 /*
1905 * Set the file name to be used for <afile>.
1906 * Make a copy to avoid that changing a buffer name or directory makes it
1907 * invalid.
1908 */
1909 if (fname_io == NULL)
1910 {
1911 if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
1912 || event == EVENT_OPTIONSET)
1913 autocmd_fname = NULL;
1914 else if (fname != NULL && !ends_excmd(*fname))
1915 autocmd_fname = fname;
1916 else if (buf != NULL)
1917 autocmd_fname = buf->b_ffname;
1918 else
1919 autocmd_fname = NULL;
1920 }
1921 else
1922 autocmd_fname = fname_io;
1923 if (autocmd_fname != NULL)
1924 autocmd_fname = vim_strsave(autocmd_fname);
1925 autocmd_fname_full = FALSE; // call FullName_save() later
1926
1927 /*
1928 * Set the buffer number to be used for <abuf>.
1929 */
1930 if (buf == NULL)
1931 autocmd_bufnr = 0;
1932 else
1933 autocmd_bufnr = buf->b_fnum;
1934
1935 /*
1936 * When the file name is NULL or empty, use the file name of buffer "buf".
1937 * Always use the full path of the file name to match with, in case
1938 * "allow_dirs" is set.
1939 */
1940 if (fname == NULL || *fname == NUL)
1941 {
1942 if (buf == NULL)
1943 fname = NULL;
1944 else
1945 {
1946#ifdef FEAT_SYN_HL
1947 if (event == EVENT_SYNTAX)
1948 fname = buf->b_p_syn;
1949 else
1950#endif
1951 if (event == EVENT_FILETYPE)
1952 fname = buf->b_p_ft;
1953 else
1954 {
1955 if (buf->b_sfname != NULL)
1956 sfname = vim_strsave(buf->b_sfname);
1957 fname = buf->b_ffname;
1958 }
1959 }
1960 if (fname == NULL)
1961 fname = (char_u *)"";
1962 fname = vim_strsave(fname); // make a copy, so we can change it
1963 }
1964 else
1965 {
1966 sfname = vim_strsave(fname);
1967 // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
1968 // ColorScheme, QuickFixCmd* or DirChanged
1969 if (event == EVENT_FILETYPE
1970 || event == EVENT_SYNTAX
1971 || event == EVENT_CMDLINECHANGED
1972 || event == EVENT_CMDLINEENTER
1973 || event == EVENT_CMDLINELEAVE
1974 || event == EVENT_CMDWINENTER
1975 || event == EVENT_CMDWINLEAVE
1976 || event == EVENT_CMDUNDEFINED
1977 || event == EVENT_FUNCUNDEFINED
1978 || event == EVENT_REMOTEREPLY
1979 || event == EVENT_SPELLFILEMISSING
1980 || event == EVENT_QUICKFIXCMDPRE
1981 || event == EVENT_COLORSCHEME
1982 || event == EVENT_COLORSCHEMEPRE
1983 || event == EVENT_OPTIONSET
1984 || event == EVENT_QUICKFIXCMDPOST
1985 || event == EVENT_DIRCHANGED)
1986 {
1987 fname = vim_strsave(fname);
1988 autocmd_fname_full = TRUE; // don't expand it later
1989 }
1990 else
1991 fname = FullName_save(fname, FALSE);
1992 }
1993 if (fname == NULL) // out of memory
1994 {
1995 vim_free(sfname);
1996 retval = FALSE;
1997 goto BYPASS_AU;
1998 }
1999
2000#ifdef BACKSLASH_IN_FILENAME
2001 /*
2002 * Replace all backslashes with forward slashes. This makes the
2003 * autocommand patterns portable between Unix and MS-DOS.
2004 */
2005 if (sfname != NULL)
2006 forward_slash(sfname);
2007 forward_slash(fname);
2008#endif
2009
2010#ifdef VMS
2011 // remove version for correct match
2012 if (sfname != NULL)
2013 vms_remove_version(sfname);
2014 vms_remove_version(fname);
2015#endif
2016
2017 /*
2018 * Set the name to be used for <amatch>.
2019 */
2020 autocmd_match = fname;
2021
2022
2023 // Don't redraw while doing autocommands.
2024 ++RedrawingDisabled;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002025
2026 // name and lnum are filled in later
2027 estack_push(ETYPE_AUCMD, NULL, 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002028 ESTACK_CHECK_SETUP
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002029
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002030 save_current_sctx = current_sctx;
2031
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002032#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002033# ifdef FEAT_PROFILE
2034 if (do_profiling == PROF_YES)
2035 prof_child_enter(&wait_time); // doesn't count for the caller itself
2036# endif
2037
2038 // Don't use local function variables, if called from a function.
2039 save_funccal(&funccal_entry);
2040#endif
2041
2042 /*
2043 * When starting to execute autocommands, save the search patterns.
2044 */
2045 if (!autocmd_busy)
2046 {
2047 save_search_patterns();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002048 if (!ins_compl_active())
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002049 {
2050 saveRedobuff(&save_redo);
2051 did_save_redobuff = TRUE;
2052 }
2053 did_filetype = keep_filetype;
2054 }
2055
2056 /*
2057 * Note that we are applying autocmds. Some commands need to know.
2058 */
2059 autocmd_busy = TRUE;
2060 filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
2061 ++nesting; // see matching decrement below
2062
2063 // Remember that FileType was triggered. Used for did_filetype().
2064 if (event == EVENT_FILETYPE)
2065 did_filetype = TRUE;
2066
2067 tail = gettail(fname);
2068
2069 // Find first autocommand that matches
2070 patcmd.curpat = first_autopat[(int)event];
2071 patcmd.nextcmd = NULL;
2072 patcmd.group = group;
2073 patcmd.fname = fname;
2074 patcmd.sfname = sfname;
2075 patcmd.tail = tail;
2076 patcmd.event = event;
2077 patcmd.arg_bufnr = autocmd_bufnr;
2078 patcmd.next = NULL;
2079 auto_next_pat(&patcmd, FALSE);
2080
2081 // found one, start executing the autocommands
2082 if (patcmd.curpat != NULL)
2083 {
2084 // add to active_apc_list
2085 patcmd.next = active_apc_list;
2086 active_apc_list = &patcmd;
2087
2088#ifdef FEAT_EVAL
2089 // set v:cmdarg (only when there is a matching pattern)
2090 save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
2091 if (eap != NULL)
2092 {
2093 save_cmdarg = set_cmdarg(eap, NULL);
2094 set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
2095 }
2096 else
2097 save_cmdarg = NULL; // avoid gcc warning
2098#endif
2099 retval = TRUE;
2100 // mark the last pattern, to avoid an endless loop when more patterns
2101 // are added when executing autocommands
2102 for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
2103 ap->last = FALSE;
2104 ap->last = TRUE;
Bram Moolenaara68e5952019-04-25 22:22:01 +02002105
2106 // make sure cursor and topline are valid
2107 check_lnums(TRUE);
2108
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002109 do_cmdline(NULL, getnextac, (void *)&patcmd,
2110 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
Bram Moolenaara68e5952019-04-25 22:22:01 +02002111
2112 // restore cursor and topline, unless they were changed
2113 reset_lnums();
2114
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002115#ifdef FEAT_EVAL
2116 if (eap != NULL)
2117 {
2118 (void)set_cmdarg(NULL, save_cmdarg);
2119 set_vim_var_nr(VV_CMDBANG, save_cmdbang);
2120 }
2121#endif
2122 // delete from active_apc_list
2123 if (active_apc_list == &patcmd) // just in case
2124 active_apc_list = patcmd.next;
2125 }
2126
2127 --RedrawingDisabled;
2128 autocmd_busy = save_autocmd_busy;
2129 filechangeshell_busy = FALSE;
2130 autocmd_nested = save_autocmd_nested;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002131 vim_free(SOURCING_NAME);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002132 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002133 estack_pop();
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002134 vim_free(autocmd_fname);
2135 autocmd_fname = save_autocmd_fname;
2136 autocmd_fname_full = save_autocmd_fname_full;
2137 autocmd_bufnr = save_autocmd_bufnr;
2138 autocmd_match = save_autocmd_match;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002139 current_sctx = save_current_sctx;
Bram Moolenaar9b8d6222020-12-28 18:26:00 +01002140#ifdef FEAT_EVAL
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002141 restore_funccal();
2142# ifdef FEAT_PROFILE
2143 if (do_profiling == PROF_YES)
2144 prof_child_exit(&wait_time);
2145# endif
2146#endif
2147 KeyTyped = save_KeyTyped;
2148 vim_free(fname);
2149 vim_free(sfname);
2150 --nesting; // see matching increment above
2151
2152 /*
2153 * When stopping to execute autocommands, restore the search patterns and
2154 * the redo buffer. Free any buffers in the au_pending_free_buf list and
2155 * free any windows in the au_pending_free_win list.
2156 */
2157 if (!autocmd_busy)
2158 {
2159 restore_search_patterns();
2160 if (did_save_redobuff)
2161 restoreRedobuff(&save_redo);
2162 did_filetype = FALSE;
2163 while (au_pending_free_buf != NULL)
2164 {
2165 buf_T *b = au_pending_free_buf->b_next;
2166 vim_free(au_pending_free_buf);
2167 au_pending_free_buf = b;
2168 }
2169 while (au_pending_free_win != NULL)
2170 {
2171 win_T *w = au_pending_free_win->w_next;
2172 vim_free(au_pending_free_win);
2173 au_pending_free_win = w;
2174 }
2175 }
2176
2177 /*
2178 * Some events don't set or reset the Changed flag.
2179 * Check if still in the same buffer!
2180 */
2181 if (curbuf == old_curbuf
2182 && (event == EVENT_BUFREADPOST
2183 || event == EVENT_BUFWRITEPOST
2184 || event == EVENT_FILEAPPENDPOST
2185 || event == EVENT_VIMLEAVE
2186 || event == EVENT_VIMLEAVEPRE))
2187 {
2188#ifdef FEAT_TITLE
2189 if (curbuf->b_changed != save_changed)
2190 need_maketitle = TRUE;
2191#endif
2192 curbuf->b_changed = save_changed;
2193 }
2194
2195 au_cleanup(); // may really delete removed patterns/commands now
2196
2197BYPASS_AU:
2198 // When wiping out a buffer make sure all its buffer-local autocommands
2199 // are deleted.
2200 if (event == EVENT_BUFWIPEOUT && buf != NULL)
2201 aubuflocal_remove(buf);
2202
2203 if (retval == OK && event == EVENT_FILETYPE)
2204 au_did_filetype = TRUE;
2205
2206 return retval;
2207}
2208
2209# ifdef FEAT_EVAL
2210static char_u *old_termresponse = NULL;
2211# endif
2212
2213/*
2214 * Block triggering autocommands until unblock_autocmd() is called.
2215 * Can be used recursively, so long as it's symmetric.
2216 */
2217 void
2218block_autocmds(void)
2219{
2220# ifdef FEAT_EVAL
2221 // Remember the value of v:termresponse.
2222 if (autocmd_blocked == 0)
2223 old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
2224# endif
2225 ++autocmd_blocked;
2226}
2227
2228 void
2229unblock_autocmds(void)
2230{
2231 --autocmd_blocked;
2232
2233# ifdef FEAT_EVAL
2234 // When v:termresponse was set while autocommands were blocked, trigger
2235 // the autocommands now. Esp. useful when executing a shell command
2236 // during startup (vimdiff).
2237 if (autocmd_blocked == 0
2238 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
2239 apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
2240# endif
2241}
2242
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002243 int
2244is_autocmd_blocked(void)
2245{
2246 return autocmd_blocked != 0;
2247}
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002248
2249/*
2250 * Find next autocommand pattern that matches.
2251 */
2252 static void
2253auto_next_pat(
2254 AutoPatCmd *apc,
2255 int stop_at_last) // stop when 'last' flag is set
2256{
2257 AutoPat *ap;
2258 AutoCmd *cp;
2259 char_u *name;
2260 char *s;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002261 char_u **sourcing_namep = &SOURCING_NAME;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002262
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002263 VIM_CLEAR(*sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002264
2265 for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
2266 {
2267 apc->curpat = NULL;
2268
2269 // Only use a pattern when it has not been removed, has commands and
2270 // the group matches. For buffer-local autocommands only check the
2271 // buffer number.
2272 if (ap->pat != NULL && ap->cmds != NULL
2273 && (apc->group == AUGROUP_ALL || apc->group == ap->group))
2274 {
2275 // execution-condition
2276 if (ap->buflocal_nr == 0
2277 ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
2278 apc->sfname, apc->tail, ap->allow_dirs))
2279 : ap->buflocal_nr == apc->arg_bufnr)
2280 {
2281 name = event_nr2name(apc->event);
2282 s = _("%s Autocommands for \"%s\"");
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002283 *sourcing_namep = alloc(STRLEN(s)
Bram Moolenaar964b3742019-05-24 18:54:09 +02002284 + STRLEN(name) + ap->patlen + 1);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002285 if (*sourcing_namep != NULL)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002286 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002287 sprintf((char *)*sourcing_namep, s,
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002288 (char *)name, (char *)ap->pat);
2289 if (p_verbose >= 8)
2290 {
2291 verbose_enter();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002292 smsg(_("Executing %s"), *sourcing_namep);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002293 verbose_leave();
2294 }
2295 }
2296
2297 apc->curpat = ap;
2298 apc->nextcmd = ap->cmds;
2299 // mark last command
2300 for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
2301 cp->last = FALSE;
2302 cp->last = TRUE;
2303 }
2304 line_breakcheck();
2305 if (apc->curpat != NULL) // found a match
2306 break;
2307 }
2308 if (stop_at_last && ap->last)
2309 break;
2310 }
2311}
2312
2313/*
2314 * Get next autocommand command.
2315 * Called by do_cmdline() to get the next line for ":if".
2316 * Returns allocated string, or NULL for end of autocommands.
2317 */
2318 char_u *
Bram Moolenaar66250c92020-08-20 15:02:42 +02002319getnextac(
2320 int c UNUSED,
2321 void *cookie,
2322 int indent UNUSED,
2323 getline_opt_T options UNUSED)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002324{
2325 AutoPatCmd *acp = (AutoPatCmd *)cookie;
2326 char_u *retval;
2327 AutoCmd *ac;
2328
2329 // Can be called again after returning the last line.
2330 if (acp->curpat == NULL)
2331 return NULL;
2332
2333 // repeat until we find an autocommand to execute
2334 for (;;)
2335 {
2336 // skip removed commands
2337 while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
2338 if (acp->nextcmd->last)
2339 acp->nextcmd = NULL;
2340 else
2341 acp->nextcmd = acp->nextcmd->next;
2342
2343 if (acp->nextcmd != NULL)
2344 break;
2345
2346 // at end of commands, find next pattern that matches
2347 if (acp->curpat->last)
2348 acp->curpat = NULL;
2349 else
2350 acp->curpat = acp->curpat->next;
2351 if (acp->curpat != NULL)
2352 auto_next_pat(acp, TRUE);
2353 if (acp->curpat == NULL)
2354 return NULL;
2355 }
2356
2357 ac = acp->nextcmd;
2358
2359 if (p_verbose >= 9)
2360 {
2361 verbose_enter_scroll();
2362 smsg(_("autocommand %s"), ac->cmd);
2363 msg_puts("\n"); // don't overwrite this either
2364 verbose_leave_scroll();
2365 }
2366 retval = vim_strsave(ac->cmd);
Bram Moolenaareb93f3f2019-04-04 15:04:56 +02002367 // Remove one-shot ("once") autocmd in anticipation of its execution.
2368 if (ac->once)
2369 au_del_cmd(ac);
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002370 autocmd_nested = ac->nested;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002371 current_sctx = ac->script_ctx;
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002372 if (ac->last)
2373 acp->nextcmd = NULL;
2374 else
2375 acp->nextcmd = ac->next;
2376 return retval;
2377}
2378
2379/*
2380 * Return TRUE if there is a matching autocommand for "fname".
2381 * To account for buffer-local autocommands, function needs to know
2382 * in which buffer the file will be opened.
2383 */
2384 int
2385has_autocmd(event_T event, char_u *sfname, buf_T *buf)
2386{
2387 AutoPat *ap;
2388 char_u *fname;
2389 char_u *tail = gettail(sfname);
2390 int retval = FALSE;
2391
2392 fname = FullName_save(sfname, FALSE);
2393 if (fname == NULL)
2394 return FALSE;
2395
2396#ifdef BACKSLASH_IN_FILENAME
2397 /*
2398 * Replace all backslashes with forward slashes. This makes the
2399 * autocommand patterns portable between Unix and MS-DOS.
2400 */
2401 sfname = vim_strsave(sfname);
2402 if (sfname != NULL)
2403 forward_slash(sfname);
2404 forward_slash(fname);
2405#endif
2406
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002407 FOR_ALL_AUTOCMD_PATTERNS(event, ap)
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002408 if (ap->pat != NULL && ap->cmds != NULL
2409 && (ap->buflocal_nr == 0
2410 ? match_file_pat(NULL, &ap->reg_prog,
2411 fname, sfname, tail, ap->allow_dirs)
2412 : buf != NULL && ap->buflocal_nr == buf->b_fnum
2413 ))
2414 {
2415 retval = TRUE;
2416 break;
2417 }
2418
2419 vim_free(fname);
2420#ifdef BACKSLASH_IN_FILENAME
2421 vim_free(sfname);
2422#endif
2423
2424 return retval;
2425}
2426
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002427/*
2428 * Function given to ExpandGeneric() to obtain the list of autocommand group
2429 * names.
2430 */
2431 char_u *
2432get_augroup_name(expand_T *xp UNUSED, int idx)
2433{
2434 if (idx == augroups.ga_len) // add "END" add the end
2435 return (char_u *)"END";
2436 if (idx >= augroups.ga_len) // end of list
2437 return NULL;
2438 if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
2439 // skip deleted entries
2440 return (char_u *)"";
2441 return AUGROUP_NAME(idx); // return a name
2442}
2443
2444static int include_groups = FALSE;
2445
2446 char_u *
2447set_context_in_autocmd(
2448 expand_T *xp,
2449 char_u *arg,
2450 int doautocmd) // TRUE for :doauto*, FALSE for :autocmd
2451{
2452 char_u *p;
2453 int group;
2454
2455 // check for a group name, skip it if present
2456 include_groups = FALSE;
2457 p = arg;
2458 group = au_get_grouparg(&arg);
2459 if (group == AUGROUP_ERROR)
2460 return NULL;
2461 // If there only is a group name that's what we expand.
2462 if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
2463 {
2464 arg = p;
2465 group = AUGROUP_ALL;
2466 }
2467
2468 // skip over event name
2469 for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
2470 if (*p == ',')
2471 arg = p + 1;
2472 if (*p == NUL)
2473 {
2474 if (group == AUGROUP_ALL)
2475 include_groups = TRUE;
2476 xp->xp_context = EXPAND_EVENTS; // expand event name
2477 xp->xp_pattern = arg;
2478 return NULL;
2479 }
2480
2481 // skip over pattern
2482 arg = skipwhite(p);
2483 while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
2484 arg++;
2485 if (*arg)
2486 return arg; // expand (next) command
2487
2488 if (doautocmd)
2489 xp->xp_context = EXPAND_FILES; // expand file names
2490 else
2491 xp->xp_context = EXPAND_NOTHING; // pattern is not expanded
2492 return NULL;
2493}
2494
2495/*
2496 * Function given to ExpandGeneric() to obtain the list of event names.
2497 */
2498 char_u *
2499get_event_name(expand_T *xp UNUSED, int idx)
2500{
2501 if (idx < augroups.ga_len) // First list group names, if wanted
2502 {
2503 if (!include_groups || AUGROUP_NAME(idx) == NULL
2504 || AUGROUP_NAME(idx) == get_deleted_augroup())
2505 return (char_u *)""; // skip deleted entries
2506 return AUGROUP_NAME(idx); // return a name
2507 }
2508 return (char_u *)event_names[idx - augroups.ga_len].name;
2509}
2510
Bram Moolenaar3e460fd2019-01-26 16:21:07 +01002511
2512#if defined(FEAT_EVAL) || defined(PROTO)
2513/*
2514 * Return TRUE if autocmd is supported.
2515 */
2516 int
2517autocmd_supported(char_u *name)
2518{
2519 char_u *p;
2520
2521 return (event_name2nr(name, &p) != NUM_EVENTS);
2522}
2523
2524/*
2525 * Return TRUE if an autocommand is defined for a group, event and
2526 * pattern: The group can be omitted to accept any group. "event" and "pattern"
2527 * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
2528 * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
2529 * Used for:
2530 * exists("#Group") or
2531 * exists("#Group#Event") or
2532 * exists("#Group#Event#pat") or
2533 * exists("#Event") or
2534 * exists("#Event#pat")
2535 */
2536 int
2537au_exists(char_u *arg)
2538{
2539 char_u *arg_save;
2540 char_u *pattern = NULL;
2541 char_u *event_name;
2542 char_u *p;
2543 event_T event;
2544 AutoPat *ap;
2545 buf_T *buflocal_buf = NULL;
2546 int group;
2547 int retval = FALSE;
2548
2549 // Make a copy so that we can change the '#' chars to a NUL.
2550 arg_save = vim_strsave(arg);
2551 if (arg_save == NULL)
2552 return FALSE;
2553 p = vim_strchr(arg_save, '#');
2554 if (p != NULL)
2555 *p++ = NUL;
2556
2557 // First, look for an autocmd group name
2558 group = au_find_group(arg_save);
2559 if (group == AUGROUP_ERROR)
2560 {
2561 // Didn't match a group name, assume the first argument is an event.
2562 group = AUGROUP_ALL;
2563 event_name = arg_save;
2564 }
2565 else
2566 {
2567 if (p == NULL)
2568 {
2569 // "Group": group name is present and it's recognized
2570 retval = TRUE;
2571 goto theend;
2572 }
2573
2574 // Must be "Group#Event" or "Group#Event#pat".
2575 event_name = p;
2576 p = vim_strchr(event_name, '#');
2577 if (p != NULL)
2578 *p++ = NUL; // "Group#Event#pat"
2579 }
2580
2581 pattern = p; // "pattern" is NULL when there is no pattern
2582
2583 // find the index (enum) for the event name
2584 event = event_name2nr(event_name, &p);
2585
2586 // return FALSE if the event name is not recognized
2587 if (event == NUM_EVENTS)
2588 goto theend;
2589
2590 // Find the first autocommand for this event.
2591 // If there isn't any, return FALSE;
2592 // If there is one and no pattern given, return TRUE;
2593 ap = first_autopat[(int)event];
2594 if (ap == NULL)
2595 goto theend;
2596
2597 // if pattern is "<buffer>", special handling is needed which uses curbuf
2598 // for pattern "<buffer=N>, fnamecmp() will work fine
2599 if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
2600 buflocal_buf = curbuf;
2601
2602 // Check if there is an autocommand with the given pattern.
2603 for ( ; ap != NULL; ap = ap->next)
2604 // only use a pattern when it has not been removed and has commands.
2605 // For buffer-local autocommands, fnamecmp() works fine.
2606 if (ap->pat != NULL && ap->cmds != NULL
2607 && (group == AUGROUP_ALL || ap->group == group)
2608 && (pattern == NULL
2609 || (buflocal_buf == NULL
2610 ? fnamecmp(ap->pat, pattern) == 0
2611 : ap->buflocal_nr == buflocal_buf->b_fnum)))
2612 {
2613 retval = TRUE;
2614 break;
2615 }
2616
2617theend:
2618 vim_free(arg_save);
2619 return retval;
2620}
2621#endif