summaryrefslogtreecommitdiffstats
path: root/src/fileio.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-01-26 16:21:07 +0100
committerBram Moolenaar <Bram@vim.org>2019-01-26 16:21:07 +0100
commit3e460fd8b72db905fbf9f01b00371384ffc415b8 (patch)
treeb02002682babdf9d7ef513fb3b33b06fcf585c70 /src/fileio.c
parent1ecc5e4a995ade68ae216bb56f6ac9bd5c0b7e4b (diff)
patch 8.1.0825: code for autocommands is mixed with file I/O codev8.1.0825
Problem: Code for autocommands is mixed with file I/O code. Solution: Move autocommand code to a separate file. (Yegappan Lakshmanan, closes #3863)
Diffstat (limited to 'src/fileio.c')
-rw-r--r--src/fileio.c2588
1 files changed, 6 insertions, 2582 deletions
diff --git a/src/fileio.c b/src/fileio.c
index 4cb13f27a5..bf724f642f 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -42,12 +42,6 @@ static int msg_add_fileformat(int eol_type);
static void msg_add_eol(void);
static int check_mtime(buf_T *buf, stat_T *s);
static int time_differs(long t1, long t2);
-static int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap);
-static int au_find_group(char_u *name);
-
-#define AUGROUP_DEFAULT -1 /* default autocmd group */
-#define AUGROUP_ERROR -2 /* erroneous autocmd group */
-#define AUGROUP_ALL -3 /* all autocmd groups */
#define HAS_BW_FLAGS
#define FIO_LATIN1 0x01 /* convert Latin1 */
@@ -120,16 +114,6 @@ static int get_mac_fio_flags(char_u *ptr);
#endif
static char *e_auchangedbuf = N_("E812: Autocommands changed buffer or buffer name");
-/*
- * Set by the apply_autocmds_group function if the given event is equal to
- * EVENT_FILETYPE. Used by the readfile function in order to determine if
- * EVENT_BUFREADPOST triggered the EVENT_FILETYPE.
- *
- * Relying on this value requires one to reset it prior calling
- * apply_autocmds_group.
- */
-static int au_did_filetype INIT(= FALSE);
-
void
filemess(
buf_T *buf,
@@ -6866,6 +6850,11 @@ buf_check_timestamp(
reason = "deleted";
else if (bufIsChanged(buf))
reason = "conflict";
+ /*
+ * Check if the file contents really changed to avoid giving a
+ * warning when only the timestamp was set (e.g., checked out of
+ * CVS). Always warn when the buffer was changed.
+ */
else if (orig_size != buf->b_orig_size || buf_contents_changed(buf))
reason = "changed";
else if (orig_mode != buf->b_orig_mode)
@@ -6912,12 +6901,6 @@ buf_check_timestamp(
#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
can_reload = TRUE;
#endif
- /*
- * Check if the file contents really changed to avoid
- * giving a warning when only the timestamp was set (e.g.,
- * checked out of CVS). Always warn when the buffer was
- * changed.
- */
if (reason[2] == 'n')
{
mesg = _("W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as well");
@@ -7552,2565 +7535,6 @@ forward_slash(char_u *fname)
}
#endif
-
-/*
- * Code for automatic commands.
- */
-
-/*
- * The autocommands are stored in a list for each event.
- * Autocommands for the same pattern, that are consecutive, are joined
- * together, to avoid having to match the pattern too often.
- * The result is an array of Autopat lists, which point to AutoCmd lists:
- *
- * last_autopat[0] -----------------------------+
- * V
- * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
- * Autopat.cmds Autopat.cmds
- * | |
- * V V
- * AutoCmd.next AutoCmd.next
- * | |
- * V V
- * AutoCmd.next NULL
- * |
- * V
- * NULL
- *
- * last_autopat[1] --------+
- * V
- * first_autopat[1] --> Autopat.next --> NULL
- * Autopat.cmds
- * |
- * V
- * AutoCmd.next
- * |
- * V
- * NULL
- * etc.
- *
- * The order of AutoCmds is important, this is the order in which they were
- * defined and will have to be executed.
- */
-typedef struct AutoCmd
-{
- char_u *cmd; /* The command to be executed (NULL
- when command has been removed) */
- char nested; /* If autocommands nest here */
- char last; /* last command in list */
-#ifdef FEAT_EVAL
- sctx_T script_ctx; /* script context where defined */
-#endif
- struct AutoCmd *next; /* Next AutoCmd in list */
-} AutoCmd;
-
-typedef struct AutoPat
-{
- struct AutoPat *next; /* next AutoPat in AutoPat list; MUST
- * be the first entry */
- char_u *pat; /* pattern as typed (NULL when pattern
- has been removed) */
- regprog_T *reg_prog; /* compiled regprog for pattern */
- AutoCmd *cmds; /* list of commands to do */
- int group; /* group ID */
- int patlen; /* strlen() of pat */
- int buflocal_nr; /* !=0 for buffer-local AutoPat */
- char allow_dirs; /* Pattern may match whole path */
- char last; /* last pattern for apply_autocmds() */
-} AutoPat;
-
-static struct event_name
-{
- char *name; /* event name */
- event_T event; /* event number */
-} event_names[] =
-{
- {"BufAdd", EVENT_BUFADD},
- {"BufCreate", EVENT_BUFADD},
- {"BufDelete", EVENT_BUFDELETE},
- {"BufEnter", EVENT_BUFENTER},
- {"BufFilePost", EVENT_BUFFILEPOST},
- {"BufFilePre", EVENT_BUFFILEPRE},
- {"BufHidden", EVENT_BUFHIDDEN},
- {"BufLeave", EVENT_BUFLEAVE},
- {"BufNew", EVENT_BUFNEW},
- {"BufNewFile", EVENT_BUFNEWFILE},
- {"BufRead", EVENT_BUFREADPOST},
- {"BufReadCmd", EVENT_BUFREADCMD},
- {"BufReadPost", EVENT_BUFREADPOST},
- {"BufReadPre", EVENT_BUFREADPRE},
- {"BufUnload", EVENT_BUFUNLOAD},
- {"BufWinEnter", EVENT_BUFWINENTER},
- {"BufWinLeave", EVENT_BUFWINLEAVE},
- {"BufWipeout", EVENT_BUFWIPEOUT},
- {"BufWrite", EVENT_BUFWRITEPRE},
- {"BufWritePost", EVENT_BUFWRITEPOST},
- {"BufWritePre", EVENT_BUFWRITEPRE},
- {"BufWriteCmd", EVENT_BUFWRITECMD},
- {"CmdlineChanged", EVENT_CMDLINECHANGED},
- {"CmdlineEnter", EVENT_CMDLINEENTER},
- {"CmdlineLeave", EVENT_CMDLINELEAVE},
- {"CmdwinEnter", EVENT_CMDWINENTER},
- {"CmdwinLeave", EVENT_CMDWINLEAVE},
- {"CmdUndefined", EVENT_CMDUNDEFINED},
- {"ColorScheme", EVENT_COLORSCHEME},
- {"ColorSchemePre", EVENT_COLORSCHEMEPRE},
- {"CompleteDone", EVENT_COMPLETEDONE},
- {"CursorHold", EVENT_CURSORHOLD},
- {"CursorHoldI", EVENT_CURSORHOLDI},
- {"CursorMoved", EVENT_CURSORMOVED},
- {"CursorMovedI", EVENT_CURSORMOVEDI},
- {"DiffUpdated", EVENT_DIFFUPDATED},
- {"DirChanged", EVENT_DIRCHANGED},
- {"EncodingChanged", EVENT_ENCODINGCHANGED},
- {"ExitPre", EVENT_EXITPRE},
- {"FileEncoding", EVENT_ENCODINGCHANGED},
- {"FileAppendPost", EVENT_FILEAPPENDPOST},
- {"FileAppendPre", EVENT_FILEAPPENDPRE},
- {"FileAppendCmd", EVENT_FILEAPPENDCMD},
- {"FileChangedShell",EVENT_FILECHANGEDSHELL},
- {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST},
- {"FileChangedRO", EVENT_FILECHANGEDRO},
- {"FileReadPost", EVENT_FILEREADPOST},
- {"FileReadPre", EVENT_FILEREADPRE},
- {"FileReadCmd", EVENT_FILEREADCMD},
- {"FileType", EVENT_FILETYPE},
- {"FileWritePost", EVENT_FILEWRITEPOST},
- {"FileWritePre", EVENT_FILEWRITEPRE},
- {"FileWriteCmd", EVENT_FILEWRITECMD},
- {"FilterReadPost", EVENT_FILTERREADPOST},
- {"FilterReadPre", EVENT_FILTERREADPRE},
- {"FilterWritePost", EVENT_FILTERWRITEPOST},
- {"FilterWritePre", EVENT_FILTERWRITEPRE},
- {"FocusGained", EVENT_FOCUSGAINED},
- {"FocusLost", EVENT_FOCUSLOST},
- {"FuncUndefined", EVENT_FUNCUNDEFINED},
- {"GUIEnter", EVENT_GUIENTER},
- {"GUIFailed", EVENT_GUIFAILED},
- {"InsertChange", EVENT_INSERTCHANGE},
- {"InsertEnter", EVENT_INSERTENTER},
- {"InsertLeave", EVENT_INSERTLEAVE},
- {"InsertCharPre", EVENT_INSERTCHARPRE},
- {"MenuPopup", EVENT_MENUPOPUP},
- {"OptionSet", EVENT_OPTIONSET},
- {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST},
- {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
- {"QuitPre", EVENT_QUITPRE},
- {"RemoteReply", EVENT_REMOTEREPLY},
- {"SessionLoadPost", EVENT_SESSIONLOADPOST},
- {"ShellCmdPost", EVENT_SHELLCMDPOST},
- {"ShellFilterPost", EVENT_SHELLFILTERPOST},
- {"SourceCmd", EVENT_SOURCECMD},
- {"SourcePre", EVENT_SOURCEPRE},
- {"SourcePost", EVENT_SOURCEPOST},
- {"SpellFileMissing",EVENT_SPELLFILEMISSING},
- {"StdinReadPost", EVENT_STDINREADPOST},
- {"StdinReadPre", EVENT_STDINREADPRE},
- {"SwapExists", EVENT_SWAPEXISTS},
- {"Syntax", EVENT_SYNTAX},
- {"TabNew", EVENT_TABNEW},
- {"TabClosed", EVENT_TABCLOSED},
- {"TabEnter", EVENT_TABENTER},
- {"TabLeave", EVENT_TABLEAVE},
- {"TermChanged", EVENT_TERMCHANGED},
- {"TerminalOpen", EVENT_TERMINALOPEN},
- {"TermResponse", EVENT_TERMRESPONSE},
- {"TextChanged", EVENT_TEXTCHANGED},
- {"TextChangedI", EVENT_TEXTCHANGEDI},
- {"TextChangedP", EVENT_TEXTCHANGEDP},
- {"User", EVENT_USER},
- {"VimEnter", EVENT_VIMENTER},
- {"VimLeave", EVENT_VIMLEAVE},
- {"VimLeavePre", EVENT_VIMLEAVEPRE},
- {"WinNew", EVENT_WINNEW},
- {"WinEnter", EVENT_WINENTER},
- {"WinLeave", EVENT_WINLEAVE},
- {"VimResized", EVENT_VIMRESIZED},
- {"TextYankPost", EVENT_TEXTYANKPOST},
- {NULL, (event_T)0}
-};
-
-static AutoPat *first_autopat[NUM_EVENTS] =
-{
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-static AutoPat *last_autopat[NUM_EVENTS] =
-{
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-/*
- * struct used to keep status while executing autocommands for an event.
- */
-typedef struct AutoPatCmd
-{
- AutoPat *curpat; /* next AutoPat to examine */
- AutoCmd *nextcmd; /* next AutoCmd to execute */
- int group; /* group being used */
- char_u *fname; /* fname to match with */
- char_u *sfname; /* sfname to match with */
- char_u *tail; /* tail of fname */
- event_T event; /* current event */
- int arg_bufnr; /* initially equal to <abuf>, set to zero when
- buf is deleted */
- struct AutoPatCmd *next; /* chain of active apc-s for auto-invalidation*/
-} AutoPatCmd;
-
-static AutoPatCmd *active_apc_list = NULL; /* stack of active autocommands */
-
-/*
- * augroups stores a list of autocmd group names.
- */
-static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
-#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
-/* use get_deleted_augroup() to get this */
-static char_u *deleted_augroup = NULL;
-
-/*
- * The ID of the current group. Group 0 is the default one.
- */
-static int current_augroup = AUGROUP_DEFAULT;
-
-static int au_need_clean = FALSE; /* need to delete marked patterns */
-
-static char_u *event_nr2name(event_T event);
-static int au_get_grouparg(char_u **argp);
-static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, int forceit, int group);
-static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap);
-static void auto_next_pat(AutoPatCmd *apc, int stop_at_last);
-static int match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname, char_u *sfname, char_u *tail, int allow_dirs);
-
-
-static event_T last_event;
-static int last_group;
-static int autocmd_blocked = 0; /* block all autocmds */
-
- static char_u *
-get_deleted_augroup(void)
-{
- if (deleted_augroup == NULL)
- deleted_augroup = (char_u *)_("--Deleted--");
- return deleted_augroup;
-}
-
-/*
- * Show the autocommands for one AutoPat.
- */
- static void
-show_autocmd(AutoPat *ap, event_T event)
-{
- AutoCmd *ac;
-
- /* Check for "got_int" (here and at various places below), which is set
- * when "q" has been hit for the "--more--" prompt */
- if (got_int)
- return;
- if (ap->pat == NULL) /* pattern has been removed */
- return;
-
- msg_putchar('\n');
- if (got_int)
- return;
- if (event != last_event || ap->group != last_group)
- {
- if (ap->group != AUGROUP_DEFAULT)
- {
- if (AUGROUP_NAME(ap->group) == NULL)
- msg_puts_attr((char *)get_deleted_augroup(), HL_ATTR(HLF_E));
- else
- msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
- msg_puts(" ");
- }
- msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T));
- last_event = event;
- last_group = ap->group;
- msg_putchar('\n');
- if (got_int)
- return;
- }
- msg_col = 4;
- msg_outtrans(ap->pat);
-
- for (ac = ap->cmds; ac != NULL; ac = ac->next)
- {
- if (ac->cmd != NULL) /* skip removed commands */
- {
- if (msg_col >= 14)
- msg_putchar('\n');
- msg_col = 14;
- if (got_int)
- return;
- msg_outtrans(ac->cmd);
-#ifdef FEAT_EVAL
- if (p_verbose > 0)
- last_set_msg(ac->script_ctx);
-#endif
- if (got_int)
- return;
- if (ac->next != NULL)
- {
- msg_putchar('\n');
- if (got_int)
- return;
- }
- }
- }
-}
-
-/*
- * Mark an autocommand pattern for deletion.
- */
- static void
-au_remove_pat(AutoPat *ap)
-{
- VIM_CLEAR(ap->pat);
- ap->buflocal_nr = -1;
- au_need_clean = TRUE;
-}
-
-/*
- * Mark all commands for a pattern for deletion.
- */
- static void
-au_remove_cmds(AutoPat *ap)
-{
- AutoCmd *ac;
-
- for (ac = ap->cmds; ac != NULL; ac = ac->next)
- VIM_CLEAR(ac->cmd);
- au_need_clean = TRUE;
-}
-
-/*
- * Cleanup autocommands and patterns that have been deleted.
- * This is only done when not executing autocommands.
- */
- static void
-au_cleanup(void)
-{
- AutoPat *ap, **prev_ap;
- AutoCmd *ac, **prev_ac;
- event_T event;
-
- if (autocmd_busy || !au_need_clean)
- return;
-
- /* loop over all events */
- for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
- event = (event_T)((int)event + 1))
- {
- /* loop over all autocommand patterns */
- prev_ap = &(first_autopat[(int)event]);
- for (ap = *prev_ap; ap != NULL; ap = *prev_ap)
- {
- /* loop over all commands for this pattern */
- prev_ac = &(ap->cmds);
- for (ac = *prev_ac; ac != NULL; ac = *prev_ac)
- {
- /* remove the command if the pattern is to be deleted or when
- * the command has been marked for deletion */
- if (ap->pat == NULL || ac->cmd == NULL)
- {
- *prev_ac = ac->next;
- vim_free(ac->cmd);
- vim_free(ac);
- }
- else
- prev_ac = &(ac->next);
- }
-
- /* remove the pattern if it has been marked for deletion */
- if (ap->pat == NULL)
- {
- if (ap->next == NULL)
- {
- if (prev_ap == &(first_autopat[(int)event]))
- last_autopat[(int)event] = NULL;
- else
- /* this depends on the "next" field being the first in
- * the struct */
- last_autopat[(int)event] = (AutoPat *)prev_ap;
- }
- *prev_ap = ap->next;
- vim_regfree(ap->reg_prog);
- vim_free(ap);
- }
- else
- prev_ap = &(ap->next);
- }
- }
-
- au_need_clean = FALSE;
-}
-
-/*
- * Called when buffer is freed, to remove/invalidate related buffer-local
- * autocmds.
- */
- void
-aubuflocal_remove(buf_T *buf)
-{
- AutoPat *ap;
- event_T event;
- AutoPatCmd *apc;
-
- /* invalidate currently executing autocommands */
- for (apc = active_apc_list; apc; apc = apc->next)
- if (buf->b_fnum == apc->arg_bufnr)
- apc->arg_bufnr = 0;
-
- /* invalidate buflocals looping through events */
- for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
- event = (event_T)((int)event + 1))
- /* loop over all autocommand patterns */
- for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
- if (ap->buflocal_nr == buf->b_fnum)
- {
- au_remove_pat(ap);
- if (p_verbose >= 6)
- {
- verbose_enter();
- smsg(_("auto-removing autocommand: %s <buffer=%d>"),
- event_nr2name(event), buf->b_fnum);
- verbose_leave();
- }
- }
- au_cleanup();
-}
-
-/*
- * Add an autocmd group name.
- * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
- */
- static int
-au_new_group(char_u *name)
-{
- int i;
-
- i = au_find_group(name);
- if (i == AUGROUP_ERROR) /* the group doesn't exist yet, add it */
- {
- /* First try using a free entry. */
- for (i = 0; i < augroups.ga_len; ++i)
- if (AUGROUP_NAME(i) == NULL)
- break;
- if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
- return AUGROUP_ERROR;
-
- AUGROUP_NAME(i) = vim_strsave(name);
- if (AUGROUP_NAME(i) == NULL)
- return AUGROUP_ERROR;
- if (i == augroups.ga_len)
- ++augroups.ga_len;
- }
-
- return i;
-}
-
- static void
-au_del_group(char_u *name)
-{
- int i;
-
- i = au_find_group(name);
- if (i == AUGROUP_ERROR) /* the group doesn't exist */
- semsg(_("E367: No such group: \"%s\""), name);
- else if (i == current_augroup)
- emsg(_("E936: Cannot delete the current group"));
- else
- {
- event_T event;
- AutoPat *ap;
- int in_use = FALSE;
-
- for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
- event = (event_T)((int)event + 1))
- {
- for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
- if (ap->group == i && ap->pat != NULL)
- {
- give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
- in_use = TRUE;
- event = NUM_EVENTS;
- break;
- }
- }
- vim_free(AUGROUP_NAME(i));
- if (in_use)
- {
- AUGROUP_NAME(i) = get_deleted_augroup();
- }
- else
- AUGROUP_NAME(i) = NULL;
- }
-}
-
-/*
- * Find the ID of an autocmd group name.
- * Return its ID. Returns AUGROUP_ERROR (< 0) for error.
- */
- static int
-au_find_group(char_u *name)
-{
- int i;
-
- for (i = 0; i < augroups.ga_len; ++i)
- if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
- && STRCMP(AUGROUP_NAME(i), name) == 0)
- return i;
- return AUGROUP_ERROR;
-}
-
-/*
- * Return TRUE if augroup "name" exists.
- */
- int
-au_has_group(char_u *name)
-{
- return au_find_group(name) != AUGROUP_ERROR;
-}
-
-/*
- * ":augroup {name}".
- */
- void
-do_augroup(char_u *arg, int del_group)
-{
- int i;
-
- if (del_group)
- {
- if (*arg == NUL)
- emsg(_(e_argreq));
- else
- au_del_group(arg);
- }
- else if (STRICMP(arg, "end") == 0) /* ":aug end": back to group 0 */
- current_augroup = AUGROUP_DEFAULT;
- else if (*arg) /* ":aug xxx": switch to group xxx */
- {
- i = au_new_group(arg);
- if (i != AUGROUP_ERROR)
- current_augroup = i;
- }
- else /* ":aug": list the group names */
- {
- msg_start();
- for (i = 0; i < augroups.ga_len; ++i)
- {
- if (AUGROUP_NAME(i) != NULL)
- {
- msg_puts((char *)AUGROUP_NAME(i));
- msg_puts(" ");
- }
- }
- msg_clr_eos();
- msg_end();
- }
-}
-
-#if defined(EXITFREE) || defined(PROTO)
- void
-free_all_autocmds(void)
-{
- int i;
- char_u *s;
-
- for (current_augroup = -1; current_augroup < augroups.ga_len;
- ++current_augroup)
- do_autocmd((char_u *)"", TRUE);
-
- for (i = 0; i < augroups.ga_len; ++i)
- {
- s = ((char_u **)(augroups.ga_data))[i];
- if (s != get_deleted_augroup())
- vim_free(s);
- }
- ga_clear(&augroups);
-}
-#endif
-
-/*
- * Return the event number for event name "start".
- * Return NUM_EVENTS if the event name was not found.
- * Return a pointer to the next event name in "end".
- */
- static event_T
-event_name2nr(char_u *start, char_u **end)
-{
- char_u *p;
- int i;
- int len;
-
- /* the event name ends with end of line, '|', a blank or a comma */
- for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
- ;
- for (i = 0; event_names[i].name != NULL; ++i)
- {
- len = (int)STRLEN(event_names[i].name);
- if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
- break;
- }
- if (*p == ',')
- ++p;
- *end = p;
- if (event_names[i].name == NULL)
- return NUM_EVENTS;
- return event_names[i].event;
-}
-
-/*
- * Return the name for event "event".
- */
- static char_u *
-event_nr2name(event_T event)
-{
- int i;
-
- for (i = 0; event_names[i].name != NULL; ++i)
- if (event_names[i].event == event)
- return (char_u *)event_names[i].name;
- return (char_u *)"Unknown";
-}
-
-/*
- * Scan over the events. "*" stands for all events.
- */
- static char_u *
-find_end_event(
- char_u *arg,
- int have_group) /* TRUE when group name was found */
-{
- char_u *pat;
- char_u *p;
-
- if (*arg == '*')
- {
- if (arg[1] && !VIM_ISWHITE(arg[1]))
- {
- semsg(_("E215: Illegal character after *: %s"), arg);
- return NULL;
- }
- pat = arg + 1;
- }
- else
- {
- for (pat = arg; *pat && *pat != '|' && !VIM_ISWHITE(*pat); pat = p)
- {
- if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS)
- {
- if (have_group)
- semsg(_("E216: No such event: %s"), pat);
- else
- semsg(_("E216: No such group or event: %s"), pat);
- return NULL;
- }
- }
- }
- return pat;
-}
-
-/*
- * Return TRUE if "event" is included in 'eventignore'.
- */
- static int
-event_ignored(event_T event)
-{
- char_u *p = p_ei;
-
- while (*p != NUL)
- {
- if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
- return TRUE;
- if (event_name2nr(p, &p) == event)
- return TRUE;
- }
-
- return FALSE;
-}
-
-/*
- * Return OK when the contents of p_ei is valid, FAIL otherwise.
- */
- int
-check_ei(void)
-{
- char_u *p = p_ei;
-
- while (*p)
- {
- if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
- {
- p += 3;
- if (*p == ',')
- ++p;
- }
- else if (event_name2nr(p, &p) == NUM_EVENTS)
- return FAIL;
- }
-
- return OK;
-}
-
-# if defined(FEAT_SYN_HL) || defined(PROTO)
-
-/*
- * Add "what" to 'eventignore' to skip loading syntax highlighting for every
- * buffer loaded into the window. "what" must start with a comma.
- * Returns the old value of 'eventignore' in allocated memory.
- */
- char_u *
-au_event_disable(char *what)
-{
- char_u *new_ei;
- char_u *save_ei;
-
- save_ei = vim_strsave(p_ei);
- if (save_ei != NULL)
- {
- new_ei = vim_strnsave(p_ei, (int)(STRLEN(p_ei) + STRLEN(what)));
- if (new_ei != NULL)
- {
- if (*what == ',' && *p_ei == NUL)
- STRCPY(new_ei, what + 1);
- else
- STRCAT(new_ei, what);
- set_string_option_direct((char_u *)"ei", -1, new_ei,
- OPT_FREE, SID_NONE);
- vim_free(new_ei);
- }
- }
- return save_ei;
-}
-
- void
-au_event_restore(char_u *old_ei)
-{
- if (old_ei != NULL)
- {
- set_string_option_direct((char_u *)"ei", -1, old_ei,
- OPT_FREE, SID_NONE);
- vim_free(old_ei);
- }
-}
-# endif /* FEAT_SYN_HL */
-
-/*
- * do_autocmd() -- implements the :autocmd command. Can be used in the
- * following ways:
- *
- * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
- * will be automatically executed for <event>
- * when editing a file matching <pat>, in
- * the current group.
- * :autocmd <event> <pat> Show the autocommands associated with
- * <event> and <pat>.
- * :autocmd <event> Show the autocommands associated with
- * <event>.
- * :autocmd Show all autocommands.
- * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
- * <event> and <pat>, and add the command
- * <cmd>, for the current group.
- * :autocmd! <event> <pat> Remove all autocommands associated with
- * <event> and <pat> for the current group.
- * :autocmd! <event> Remove all autocommands associated with
- * <event> for the current group.
- * :autocmd! Remove ALL autocommands for the current
- * group.
- *
- * Multiple events and patterns may be given separated by commas. Here are
- * some examples:
- * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
- * :autocmd bufleave * set tw=79 nosmartindent ic infercase
- *
- * :autocmd * *.c show all autocommands for *.c files.
- *
- * Mostly a {group} argument can optionally appear before <event>.
- */
- void
-do_autocmd(char_u *arg_in, int forceit)
-{
- char_u *arg = arg_in;
- char_u *pat;
- char_u *envpat = NULL;
- char_u *cmd;
- event_T event;
- int need_free = FALSE;
- int nested = FALSE;
- int group;
-
- if (*arg == '|')
- {
- arg = (char_u *)"";
- group = AUGROUP_ALL; /* no argument, use all groups */
- }
- else
- {
- /*
- * Check for a legal group name. If not, use AUGROUP_ALL.
- */
- group = au_get_grouparg(&arg);
- if (arg == NULL) /* out of memory */
- return;
- }
-
- /*
- * Scan over the events.
- * If we find an illegal name, return here, don't do anything.
- */
- pat = find_end_event(arg, group != AUGROUP_ALL);
- if (pat == NULL)
- return;
-
- pat = skipwhite(pat);
- if (*pat == '|')
- {
- pat = (char_u *)"";
- cmd = (char_u *)"";
- }
- else
- {
- /*
- * Scan over the pattern. Put a NUL at the end.
- */
- cmd = pat;
- while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
- cmd++;
- if (*cmd)
- *cmd++ = NUL;
-
- /* Expand environment variables in the pattern. Set 'shellslash', we want
- * forward slashes here. */
- if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
- {
-#ifdef BACKSLASH_IN_FILENAME
- int p_ssl_save = p_ssl;
-
- p_ssl = TRUE;
-#endif
- envpat = expand_env_save(pat);
-#ifdef BACKSLASH_IN_FILENAME
- p_ssl = p_ssl_save;
-#endif
- if (envpat != NULL)
- pat = envpat;
- }
-
- /*
- * Check for "nested" flag.
- */
- cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 && VIM_ISWHITE(cmd[6]))
- {
- nested = TRUE;
- cmd = skipwhite(cmd + 6);
- }
-
- /*
- * Find the start of the commands.
- * Expand <sfile> in it.
- */
- if (*cmd != NUL)
- {
- cmd = expand_sfile(cmd);
- if (cmd == NULL) /* some error */
- return;
- need_free = TRUE;
- }
- }
-
- /*
- * Print header when showing autocommands.
- */
- if (!forceit && *cmd == NUL)
- {
- /* Highlight title */
- msg_puts_title(_("\n--- Autocommands ---"));
- }
-
- /*
- * Loop over the events.
- */
- last_event = (event_T)-1; /* for listing the event name */
- last_group = AUGROUP_ERROR; /* for listing the group name */
- if (*arg == '*' || *arg == NUL || *arg == '|')
- {
- for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
- event = (event_T)((int)event + 1))
- if (do_autocmd_event(event, pat,
- nested, cmd, forceit, group) == FAIL)
- break;
- }
- else
- {
- while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
- if (do_autocmd_event(event_name2nr(arg, &arg), pat,
- nested, cmd, forceit, group) == FAIL)
- break;
- }
-
- if (need_free)
- vim_free(cmd);
- vim_free(envpat);
-}
-
-/*
- * Find the group ID in a ":autocmd" or ":doautocmd" argument.
- * The "argp" argument is advanced to the following argument.
- *
- * Returns the group ID, AUGROUP_ERROR for error (out of memory).
- */
- static int
-au_get_grouparg(char_u **argp)
-{
- char_u *group_name;
- char_u *p;
- char_u *arg = *argp;
- int group = AUGROUP_ALL;
-
- for (p = arg; *p && !VIM_ISWHITE(*p) && *p != '|'; ++p)
- ;
- if (p > arg)
- {
- group_name = vim_strnsave(arg, (int)(p - arg));
- if (group_name == NULL) /* out of memory */
- return AUGROUP_ERROR;
- group = au_find_group(group_name);
- if (group == AUGROUP_ERROR)
- group = AUGROUP_ALL; /* no match, use all groups */
- else
- *argp = skipwhite(p); /* match, skip over group name */
- vim_free(group_name);
- }
- return group;
-}
-
-/*
- * do_autocmd() for one event.
- * If *pat == NUL do for all patterns.
- * If *cmd == NUL show entries.
- * If forceit == TRUE delete entries.
- * If group is not AUGROUP_ALL, only use this group.
- */
- static int
-do_autocmd_event(
- event_T event,
- char_u *pat,
- int nested,
- char_u *cmd,
- int forceit,
- int group)
-{
- AutoPat *ap;
- AutoPat **prev_ap;
- AutoCmd *ac;
- AutoCmd **prev_ac;
- int brace_level;
- char_u *endpat;
- int findgroup;
- int allgroups;
- int patlen;
- int is_buflocal;
- int buflocal_nr;
- char_u buflocal_pat[25]; /* for "<buffer=X>" */
-
- if (group == AUGROUP_ALL)
- findgroup = current_augroup;
- else
- findgroup = group;
- allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
-
- /*
- * Show or delete all patterns for an event.
- */
- if (*pat == NUL)
- {
- for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
- {
- if (forceit) /* delete the AutoPat, if it's in the current group */
- {
- if (ap->group == findgroup)
- au_remove_pat(ap);
- }
- else if (group == AUGROUP_ALL || ap->group == group)
- show_autocmd(ap, event);
- }
- }
-
- /*
- * Loop through all the specified patterns.
- */
- for ( ; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat))
- {
- /*
- * Find end of the pattern.
- * Watch out for a comma in braces, like "*.\{obj,o\}".
- */
- brace_level = 0;
- for (endpat = pat; *endpat && (*endpat != ',' || brace_level
- || (endpat > pat && endpat[-1] == '\\')); ++endpat)
- {
- if (*endpat == '{')
- brace_level++;
- else if (*endpat == '}')
- brace_level--;
- }
- if (pat == endpat) /* ignore single comma */
- continue;
- patlen = (int)(endpat - pat);
-
- /*
- * detect special <buflocal[=X]> buffer-local patterns
- */
- is_buflocal = FALSE;
- buflocal_nr = 0;
-
- if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
- && pat[patlen - 1] == '>')
- {
- /* "<buffer...>": Error will be printed only for addition.
- * printing and removing will proceed silently. */
- is_buflocal = TRUE;
- if (patlen == 8)
- /* "<buffer>" */
- buflocal_nr = curbuf->b_fnum;
- else if (patlen > 9 && pat[7] == '=')
- {
- if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0)
- /* "<buffer=abuf>" */
- buflocal_nr = autocmd_bufnr;
- else if (skipdigits(pat + 8) == pat + patlen - 1)
- /* "<buffer=123>" */
- buflocal_nr = atoi((char *)pat + 8);
- }
- }
-
- if (is_buflocal)
- {
- /* normalize pat into standard "<buffer>#N" form */
- sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
- pat = buflocal_pat; /* can modify pat and patlen */
- patlen = (int)STRLEN(buflocal_pat); /* but not endpat */
- }
-
- /*
- * Find AutoPat entries with this pattern. When adding a command it
- * always goes at or after the last one, so start at the end.
- */
- if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL)
- prev_ap = &last_autopat[(int)event];
- else
- prev_ap = &first_autopat[(int)event];
- while ((ap = *prev_ap) != NULL)
- {
- if (ap->pat != NULL)
- {
- /* Accept a pattern when:
- * - a group was specified and it's that group, or a group was
- * not specified and it's the current group, or a group was
- * not specified and we are listing
- * - the length of the pattern matches
- * - the pattern matches.
- * For <buffer[=X]>, this condition works because we normalize
- * all buffer-local patterns.
- */
- if ((allgroups || ap->group == findgroup)
- && ap->patlen == patlen
- && STRNCMP(pat, ap->pat, patlen) == 0)
- {
- /*
- * Remove existing autocommands.
- * If adding any new autocmd's for this AutoPat, don't
- * delete the pattern from the autopat list, append to
- * this list.
- */
- if (forceit)
- {
- if (*cmd != NUL && ap->next == NULL)
- {
- au_remove_cmds(ap);
- break;
- }
- au_remove_pat(ap);
- }
-
- /*
- * Show autocmd's for this autopat, or buflocals <buffer=X>
- */
- else if (*cmd == NUL)
- show_autocmd(ap, event);
-
- /*
- * Add autocmd to this autopat, if it's the last one.
- */
- else if (ap->next == NULL)
- break;
- }
- }
- prev_ap = &ap->next;
- }
-
- /*
- * Add a new command.
- */
- if (*cmd != NUL)
- {
- /*
- * If the pattern we want to add a command to does appear at the
- * end of the list (or not is not in the list at all), add the
-