diff options
author | Bram Moolenaar <Bram@vim.org> | 2019-01-26 16:21:07 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2019-01-26 16:21:07 +0100 |
commit | 3e460fd8b72db905fbf9f01b00371384ffc415b8 (patch) | |
tree | b02002682babdf9d7ef513fb3b33b06fcf585c70 /src/fileio.c | |
parent | 1ecc5e4a995ade68ae216bb56f6ac9bd5c0b7e4b (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.c | 2588 |
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 - |