]> granicus.if.org Git - vim/commitdiff
patch 8.1.0825: code for autocommands is mixed with file I/O code v8.1.0825
authorBram Moolenaar <Bram@vim.org>
Sat, 26 Jan 2019 15:21:07 +0000 (16:21 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 26 Jan 2019 15:21:07 +0000 (16:21 +0100)
Problem:    Code for autocommands is mixed with file I/O code.
Solution:   Move autocommand code to a separate file. (Yegappan Lakshmanan,
            closes #3863)

19 files changed:
Filelist
src/Make_bc5.mak
src/Make_cyg_ming.mak
src/Make_dice.mak
src/Make_ivc.mak
src/Make_manx.mak
src/Make_morph.mak
src/Make_mvc.mak
src/Make_sas.mak
src/Make_vms.mms
src/Makefile
src/README.txt
src/autocmd.c [new file with mode: 0644]
src/fileio.c
src/globals.h
src/proto.h
src/proto/autocmd.pro [new file with mode: 0644]
src/proto/fileio.pro
src/version.c

index d3fcba635640d6f1b9d180596e6dab69e9ce35ef..1eb9aa2cb0766fb756e3c15cdcc5cb0e46a93509 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -14,6 +14,7 @@ SRC_ALL =     \
                src/arabic.c \
                src/arabic.h \
                src/ascii.h \
+               src/autocmd.c \
                src/beval.c \
                src/beval.h \
                src/blob.c \
@@ -146,6 +147,7 @@ SRC_ALL =   \
                src/proto.h \
                src/protodef.h \
                src/proto/arabic.pro \
+               src/proto/autocmd.pro \
                src/proto/beval.pro \
                src/proto/blob.pro \
                src/proto/blowfish.pro \
index 9760d8f7b25038ab7b38f42398eff72e4e2febcb..d0d31d4eb94ecade06b3475b981aab5edb7f0541 100644 (file)
@@ -525,6 +525,7 @@ vimwinmain = \
 
 vimobj =  \
        $(OBJDIR)\arabic.obj \
+       $(OBJDIR)\autocmd.obj \
        $(OBJDIR)\blowfish.obj \
        $(OBJDIR)\buffer.obj \
        $(OBJDIR)\charset.obj \
index 7aef61ac33610061f3bffb16929620540b0e0a7d..8d8666ffbec6ca80c09f2e29f495816d19f23322 100644 (file)
@@ -695,6 +695,7 @@ GUIOBJ =  $(OUTDIR)/gui.o $(OUTDIR)/gui_w32.o $(OUTDIR)/gui_beval.o $(OUTDIR)/os
 CUIOBJ = $(OUTDIR)/iscygpty.o
 OBJ = \
        $(OUTDIR)/arabic.o \
+       $(OUTDIR)/autocmd.o \
        $(OUTDIR)/beval.o \
        $(OUTDIR)/blob.o \
        $(OUTDIR)/blowfish.o \
index 2daa8d72bf15fd5fb2252072ee49af77d6bb6ce5..83614ca91151f7ae66439ac1b827f1fb61316a57 100644 (file)
@@ -27,6 +27,7 @@ LD = dcc
 
 SRC = \
        arabic.c \
+       autocmd.c \
        blowfish.c \
        buffer.c \
        charset.c \
@@ -84,6 +85,7 @@ SRC = \
        version.c
 
 OBJ =  o/arabic.o \
+       o/autocmd.o \
        o/blowfish.o \
        o/buffer.o \
        o/charset.o \
@@ -161,6 +163,8 @@ $(SYMS)  : vim.h globals.h keymap.h macros.h ascii.h term.h os_amiga.h structs.h
 
 o/arabic.o:    arabic.c  $(SYMS)
 
+o/autocmd.o:   autocmd.c  $(SYMS)
+
 o/blowfish.o:  blowfish.c  $(SYMS)
 
 o/buffer.o:    buffer.c  $(SYMS)
index 784cab931202463858d35af7927e12a531f90725..0459984dab0336a9d555d95506e6fe931595db8c 100644 (file)
@@ -211,6 +211,7 @@ ALL : .\$(VIM).exe vimrun.exe install.exe uninstal.exe xxd/xxd.exe GvimExt/gvime
 LINK32_OBJS= \
        $(EXTRAS) \
        "$(INTDIR)/arabic.obj" \
+       "$(INTDIR)/autocmd.obj" \
        "$(INTDIR)/blowfish.obj" \
        "$(INTDIR)/buffer.obj" \
        "$(INTDIR)/charset.obj" \
@@ -341,6 +342,10 @@ GvimExt/gvimext.dll: GvimExt/gvimext.cpp GvimExt/gvimext.rc GvimExt/gvimext.h
 SOURCE=.\arabic.c
 # End Source File
 # Begin Source File
+#
+SOURCE=.\autocmd.c
+# End Source File
+# Begin Source File
 
 SOURCE=.\blowfish.c
 # End Source File
index 04560d4034ef86a5b1e879b7696106382d8c8caa..b71b923ded6203bfca623fff4ea55ffba4da1f8b 100644 (file)
@@ -37,6 +37,7 @@ REN = $(SHELL) -c mv -f
 DEL = $(SHELL) -c rm -f
 
 SRC =  arabic.c \
+       autocmd.c \
        blowfish.c \
        buffer.c \
        charset.c \
@@ -96,6 +97,7 @@ SRC = arabic.c \
 INCL = vim.h feature.h keymap.h macros.h ascii.h term.h structs.h os_amiga.h
 
 OBJ =  obj/arabic.o \
+       obj/autocmd.o \
        obj/blowfish.o \
        obj/buffer.o \
        obj/charset.o \
@@ -153,6 +155,7 @@ OBJ =       obj/arabic.o \
        $(TERMLIB)
 
 PRO =  proto/arabic.pro \
+       proto/autocmd.pro \
        proto/blowfish.pro \
        proto/buffer.pro \
        proto/charset.pro \
@@ -256,6 +259,9 @@ $(OBJ): $(SYMS)
 obj/arabic.o:  arabic.c
        $(CCSYM) $@ arabic.c
 
+obj/autocmd.o: autocmd.c
+       $(CCSYM) $@ autocmd.c
+
 obj/blowfish.o:        blowfish.c
        $(CCSYM) $@ blowfish.c
 
index 70ab5377a33c892dc2fb9bbe77278bc2e859ccb3..ae490416f4ff975565558f6180acbb435db71a1c 100644 (file)
@@ -25,6 +25,7 @@ RM =          rm
        ${CC} ${CFLAGS} $< -o $@
 
 SRC =  arabic.c                                                \
+       autocmd.c                                               \
        blowfish.c                                              \
        buffer.c                                                \
        charset.c                                               \
index a3c66819a5f8150214afb4582b52372e407f6c02..3f8c8a03846b88a25fec00d8ca3ed53181a4a332 100644 (file)
@@ -700,6 +700,7 @@ INCL =      vim.h alloc.h arabic.h ascii.h ex_cmds.h farsi.h feature.h globals.h \
 
 OBJ = \
        $(OUTDIR)\arabic.obj \
+       $(OUTDIR)\autocmd.obj \
        $(OUTDIR)\beval.obj \
        $(OUTDIR)\blob.obj \
        $(OUTDIR)\blowfish.obj \
@@ -1345,6 +1346,8 @@ $(NEW_TESTS):
 
 $(OUTDIR)/arabic.obj:  $(OUTDIR) arabic.c  $(INCL)
 
+$(OUTDIR)/autocmd.obj: $(OUTDIR) autocmd.c  $(INCL)
+
 $(OUTDIR)/beval.obj:   $(OUTDIR) beval.c  $(INCL)
 
 $(OUTDIR)/blob.obj:    $(OUTDIR) blob.c  $(INCL)
@@ -1619,6 +1622,7 @@ auto:
 # End Custom Build
 proto.h: \
        proto/arabic.pro \
+       proto/autocmd.pro \
        proto/blob.pro \
        proto/blowfish.pro \
        proto/buffer.pro \
index a16908e9c1af2c80d076dcea8c23ffc979a78489..c621360eab36aef61e82487ce2ec997d3d6b6892 100644 (file)
@@ -90,6 +90,7 @@ PROPT = DEF=PROTO GPROTO GPPARM MAXIMUMERRORS=999 GENPROTOSTATICS GENPROTOPARAME
 
 SRC = \
        arabic.c \
+       autocmd.c \
        blowfish.c \
        buffer.c \
        charset.c \
@@ -148,6 +149,7 @@ SRC = \
 
 OBJ = \
        arabic.o \
+       autocmd.o \
        blowfish.o \
        buffer.o \
        charset.o \
@@ -206,6 +208,7 @@ OBJ = \
 
 PRO = \
        proto/arabic.pro \
+       proto/autocmd.pro \
        proto/blowfish.pro \
        proto/buffer.pro \
        proto/charset.pro \
@@ -319,6 +322,8 @@ $(PRO): $(GST) vim.h
 # dependencies
 arabic.o:              arabic.c
 proto/arabic.pro:      arabic.c
+autocmd.o:             autocmd.c
+proto/autocmd.pro:     autocmd.c
 blowfish.o:            blowfish.c
 proto/blowfish.pro:    blowfish.c
 buffer.o:              buffer.c
index 18c68142d8cf609e36f2c815e5ad615333c144af..6a3508980ff0998bcd76f222984e763193aa2ace 100644 (file)
@@ -312,7 +312,7 @@ ALL_CFLAGS_VER = /def=($(MODEL_DEF)$(DEFS)$(DEBUG_DEF)$(PERL_DEF)$(PYTHON_DEF) -
 ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_LIB) \
           $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB)
 
-SRC =  arabic.c beval.obj blob.c blowfish.c buffer.c charset.c crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c \
+SRC =  arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c charset.c crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c \
        evalfunc.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c if_cscope.c if_xcmdsrv.c farsi.c fileio.c fold.c \
        getchar.c hardcopy.c hashtab.c json.c list.c main.c mark.c menu.c mbyte.c memfile.c memline.c message.c misc1.c \
        misc2.c move.c normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c sha256.c sign.c \
@@ -321,7 +321,7 @@ SRC =       arabic.c beval.obj blob.c blowfish.c buffer.c charset.c crypt.c crypt_zip.
        $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \
        $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC)
 
-OBJ =  arabic.obj beval.obj blob.obj blowfish.obj buffer.obj charset.obj crypt.obj crypt_zip.obj dict.obj diff.obj digraph.obj \
+OBJ =  arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj charset.obj crypt.obj crypt_zip.obj dict.obj diff.obj digraph.obj \
        edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj ex_eval.obj ex_getln.obj if_cscope.obj \
        if_xcmdsrv.obj farsi.obj fileio.obj fold.obj getchar.obj hardcopy.obj hashtab.obj json.obj list.obj main.obj mark.obj \
        menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \
@@ -500,6 +500,7 @@ ruby_env :
 .ENDIF
 
 arabic.obj : arabic.c vim.h
+autocmd.obj : autocmd.c vim.h [.auto]config.h feature.h os_unix.h
 blowfish.obj : blowfish.c vim.h [.auto]config.h feature.h os_unix.h
 blob.obj : blob.c vim.h [.auto]config.h feature.h os_unix.h    
 buffer.obj : buffer.c vim.h [.auto]config.h feature.h os_unix.h \
index 23f01c38bc5352940e13dc352df830da494c3865..2c409f0a62755737820cf0fae943f960e1d36822 100644 (file)
@@ -1572,6 +1572,7 @@ include testdir/Make_all.mak
 
 BASIC_SRC = \
        arabic.c \
+       autocmd.c \
        beval.c \
        blob.c \
        blowfish.c \
@@ -1684,6 +1685,7 @@ LINT_SRC = $(BASIC_SRC) $(GUI_SRC) $(HANGULIN_SRC) \
 
 OBJ_COMMON = \
        objects/arabic.o \
+       objects/autocmd.o \
        objects/beval.o \
        objects/buffer.o \
        objects/blob.o \
@@ -1809,6 +1811,7 @@ ALL_OBJ = $(OBJ_COMMON) \
 
 PRO_AUTO = \
        arabic.pro \
+       autocmd.pro \
        blowfish.pro \
        buffer.pro \
        charset.pro \
@@ -2934,6 +2937,9 @@ $(ALL_OBJ): objects/.dirstamp
 objects/arabic.o: arabic.c
        $(CCC) -o $@ arabic.c
 
+objects/autocmd.o: autocmd.c
+       $(CCC) -o $@ autocmd.c
+
 objects/blob.o: blob.c
        $(CCC) -o $@ blob.c
 
@@ -3376,6 +3382,10 @@ objects/arabic.o: arabic.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
  proto.h globals.h farsi.h arabic.h
+objects/autocmd.o: autocmd.c vim.h protodef.h auto/config.h feature.h \
+ os_unix.h os_mac.h ascii.h keymap.h term.h macros.h option.h beval.h \
+ structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h proto.h globals.h \
+ farsi.h arabic.h
 objects/beval.o: beval.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
index 6e7094cec0909dfb44dc7a9a1ec3d69b942a3d5f..e1dc36f69c9d18d21557020541fd98a1524f37bf 100644 (file)
@@ -17,6 +17,7 @@ use the CTRL-] command.  Use CTRL-T or CTRL-O to jump back.
 To jump to a file, move the cursor on its name and use the "gf" command.
 
 Most code can be found in a file with an obvious name (incomplete list):
+       autocmd.c       autocommands
        buffer.c        manipulating buffers (loaded files)
        diff.c          diff mode (vimdiff)
        eval.c          expression evaluation
diff --git a/src/autocmd.c b/src/autocmd.c
new file mode 100644 (file)
index 0000000..55650b4
--- /dev/null
@@ -0,0 +1,2579 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * autocmd.c: Autocommand related functions
+ */
+
+#include "vim.h"
+
+/*
+ * 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
+};
+
+#define AUGROUP_DEFAULT    -1      // default autocmd group
+#define AUGROUP_ERROR     -2       // erroneous autocmd group
+#define AUGROUP_ALL       -3       // all autocmd groups
+
+/*
+ * 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;
+
+/*
+ * 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.
+ */
+int au_did_filetype INIT(= FALSE);
+
+/*
+ * 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 au_find_group(char_u *name);
+
+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
+            * pattern at the end of the list.
+            */
+           if (ap == NULL)
+           {
+               /* refuse to add buffer-local ap if buffer number is invalid */
+               if (is_buflocal && (buflocal_nr == 0
+                                     || buflist_findnr(buflocal_nr) == NULL))
+               {
+                   semsg(_("E680: <buffer=%d>: invalid buffer number "),
+                                                                buflocal_nr);
+                   return FAIL;
+               }
+
+               ap = (AutoPat *)alloc((unsigned)sizeof(AutoPat));
+               if (ap == NULL)
+                   return FAIL;
+               ap->pat = vim_strnsave(pat, patlen);
+               ap->patlen = patlen;
+               if (ap->pat == NULL)
+               {
+                   vim_free(ap);
+                   return FAIL;
+               }
+
+               if (is_buflocal)
+               {
+                   ap->buflocal_nr = buflocal_nr;
+                   ap->reg_prog = NULL;
+               }
+               else
+               {
+                   char_u      *reg_pat;
+
+                   ap->buflocal_nr = 0;
+                   reg_pat = file_pat_to_reg_pat(pat, endpat,
+                                                        &ap->allow_dirs, TRUE);
+                   if (reg_pat != NULL)
+                       ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
+                   vim_free(reg_pat);
+                   if (reg_pat == NULL || ap->reg_prog == NULL)
+                   {
+                       vim_free(ap->pat);
+                       vim_free(ap);
+                       return FAIL;
+                   }
+               }
+               ap->cmds = NULL;
+               *prev_ap = ap;
+               last_autopat[(int)event] = ap;
+               ap->next = NULL;
+               if (group == AUGROUP_ALL)
+                   ap->group = current_augroup;
+               else
+                   ap->group = group;
+           }
+
+           /*
+            * Add the autocmd at the end of the AutoCmd list.
+            */
+           prev_ac = &(ap->cmds);
+           while ((ac = *prev_ac) != NULL)
+               prev_ac = &ac->next;
+           ac = (AutoCmd *)alloc((unsigned)sizeof(AutoCmd));
+           if (ac == NULL)
+               return FAIL;
+           ac->cmd = vim_strsave(cmd);
+#ifdef FEAT_EVAL
+           ac->script_ctx = current_sctx;
+           ac->script_ctx.sc_lnum += sourcing_lnum;
+#endif
+           if (ac->cmd == NULL)
+           {
+               vim_free(ac);
+               return FAIL;
+           }
+           ac->next = NULL;
+           *prev_ac = ac;
+           ac->nested = nested;
+       }
+    }
+
+    au_cleanup();      // may really delete removed patterns/commands now
+    return OK;
+}
+
+/*
+ * Implementation of ":doautocmd [group] event [fname]".
+ * Return OK for success, FAIL for failure;
+ */
+    int
+do_doautocmd(
+    char_u     *arg,
+    int                do_msg,     // give message for no matching autocmds?
+    int                *did_something)
+{
+    char_u     *fname;
+    int                nothing_done = TRUE;
+    int                group;
+
+    if (did_something != NULL)
+       *did_something = FALSE;
+
+    /*
+     * Check for a legal group name.  If not, use AUGROUP_ALL.
+     */
+    group = au_get_grouparg(&arg);
+    if (arg == NULL)       // out of memory
+       return FAIL;
+
+    if (*arg == '*')
+    {
+       emsg(_("E217: Can't execute autocommands for ALL events"));
+       return FAIL;
+    }
+
+    /*
+     * Scan over the events.
+     * If we find an illegal name, return here, don't do anything.
+     */
+    fname = find_end_event(arg, group != AUGROUP_ALL);
+    if (fname == NULL)
+       return FAIL;
+
+    fname = skipwhite(fname);
+
+    /*
+     * Loop over the events.
+     */
+    while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
+       if (apply_autocmds_group(event_name2nr(arg, &arg),
+                                     fname, NULL, TRUE, group, curbuf, NULL))
+           nothing_done = FALSE;
+
+    if (nothing_done && do_msg)
+       msg(_("No matching autocommands"));
+    if (did_something != NULL)
+       *did_something = !nothing_done;
+
+#ifdef FEAT_EVAL
+    return aborting() ? FAIL : OK;
+#else
+    return OK;
+#endif
+}
+
+/*
+ * ":doautoall": execute autocommands for each loaded buffer.
+ */
+    void
+ex_doautoall(exarg_T *eap)
+{
+    int                retval;
+    aco_save_T aco;
+    buf_T      *buf;
+    bufref_T   bufref;
+    char_u     *arg = eap->arg;
+    int                call_do_modelines = check_nomodeline(&arg);
+    int                did_aucmd;
+
+    /*
+     * This is a bit tricky: For some commands curwin->w_buffer needs to be
+     * equal to curbuf, but for some buffers there may not be a window.
+     * So we change the buffer for the current window for a moment.  This
+     * gives problems when the autocommands make changes to the list of
+     * buffers or windows...
+     */
+    FOR_ALL_BUFFERS(buf)
+    {
+       if (buf->b_ml.ml_mfp != NULL)
+       {
+           // find a window for this buffer and save some values
+           aucmd_prepbuf(&aco, buf);
+           set_bufref(&bufref, buf);
+
+           // execute the autocommands for this buffer
+           retval = do_doautocmd(arg, FALSE, &did_aucmd);
+
+           if (call_do_modelines && did_aucmd)
+           {
+               // Execute the modeline settings, but don't set window-local
+               // options if we are using the current window for another
+               // buffer.
+               do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
+           }
+
+           // restore the current window
+           aucmd_restbuf(&aco);
+
+           // stop if there is some error or buffer was deleted
+           if (retval == FAIL || !bufref_valid(&bufref))
+               break;
+       }
+    }
+
+    check_cursor();        // just in case lines got deleted
+}
+
+/*
+ * Check *argp for <nomodeline>.  When it is present return FALSE, otherwise
+ * return TRUE and advance *argp to after it.
+ * Thus return TRUE when do_modelines() should be called.
+ */
+    int
+check_nomodeline(char_u **argp)
+{
+    if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
+    {
+       *argp = skipwhite(*argp + 12);
+       return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ * Prepare for executing autocommands for (hidden) buffer "buf".
+ * Search for a visible window containing the current buffer.  If there isn't
+ * one then use "aucmd_win".
+ * Set "curbuf" and "curwin" to match "buf".
+ */
+    void
+aucmd_prepbuf(
+    aco_save_T *aco,           // structure to save values in
+    buf_T      *buf)           // new curbuf
+{
+    win_T      *win;
+    int                save_ea;
+#ifdef FEAT_AUTOCHDIR
+    int                save_acd;
+#endif
+
+    // Find a window that is for the new buffer
+    if (buf == curbuf)         // be quick when buf is curbuf
+       win = curwin;
+    else
+       FOR_ALL_WINDOWS(win)
+           if (win->w_buffer == buf)
+               break;
+
+    // Allocate "aucmd_win" when needed.  If this fails (out of memory) fall
+    // back to using the current window.
+    if (win == NULL && aucmd_win == NULL)
+    {
+       win_alloc_aucmd_win();
+       if (aucmd_win == NULL)
+           win = curwin;
+    }
+    if (win == NULL && aucmd_win_used)
+       // Strange recursive autocommand, fall back to using the current
+       // window.  Expect a few side effects...
+       win = curwin;
+
+    aco->save_curwin = curwin;
+    aco->save_curbuf = curbuf;
+    aco->save_prevwin = prevwin;
+    if (win != NULL)
+    {
+       // There is a window for "buf" in the current tab page, make it the
+       // curwin.  This is preferred, it has the least side effects (esp. if
+       // "buf" is curbuf).
+       aco->use_aucmd_win = FALSE;
+       curwin = win;
+    }
+    else
+    {
+       // There is no window for "buf", use "aucmd_win".  To minimize the side
+       // effects, insert it in the current tab page.
+       // Anything related to a window (e.g., setting folds) may have
+       // unexpected results.
+       aco->use_aucmd_win = TRUE;
+       aucmd_win_used = TRUE;
+       aucmd_win->w_buffer = buf;
+#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
+       aucmd_win->w_s = &buf->b_s;
+#endif
+       ++buf->b_nwindows;
+       win_init_empty(aucmd_win); // set cursor and topline to safe values
+
+       // Make sure w_localdir and globaldir are NULL to avoid a chdir() in
+       // win_enter_ext().
+       VIM_CLEAR(aucmd_win->w_localdir);
+       aco->globaldir = globaldir;
+       globaldir = NULL;
+
+
+       // Split the current window, put the aucmd_win in the upper half.
+       // We don't want the BufEnter or WinEnter autocommands.
+       block_autocmds();
+       make_snapshot(SNAP_AUCMD_IDX);
+       save_ea = p_ea;
+       p_ea = FALSE;
+
+#ifdef FEAT_AUTOCHDIR
+       // Prevent chdir() call in win_enter_ext(), through do_autochdir().
+       save_acd = p_acd;
+       p_acd = FALSE;
+#endif
+
+       (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
+       (void)win_comp_pos();   // recompute window positions
+       p_ea = save_ea;
+#ifdef FEAT_AUTOCHDIR
+       p_acd = save_acd;
+#endif
+       unblock_autocmds();
+       curwin = aucmd_win;
+    }
+    curbuf = buf;
+    aco->new_curwin = curwin;
+    set_bufref(&aco->new_curbuf, curbuf);
+}
+
+/*
+ * Cleanup after executing autocommands for a (hidden) buffer.
+ * Restore the window as it was (if possible).
+ */
+    void
+aucmd_restbuf(
+    aco_save_T *aco)           // structure holding saved values
+{
+    int dummy;
+
+    if (aco->use_aucmd_win)
+    {
+       --curbuf->b_nwindows;
+       // Find "aucmd_win", it can't be closed, but it may be in another tab
+       // page. Do not trigger autocommands here.
+       block_autocmds();
+       if (curwin != aucmd_win)
+       {
+           tabpage_T   *tp;
+           win_T       *wp;
+
+           FOR_ALL_TAB_WINDOWS(tp, wp)
+           {
+               if (wp == aucmd_win)
+               {
+                   if (tp != curtab)
+                       goto_tabpage_tp(tp, TRUE, TRUE);
+                   win_goto(aucmd_win);
+                   goto win_found;
+               }
+           }
+       }
+win_found:
+
+       // Remove the window and frame from the tree of frames.
+       (void)winframe_remove(curwin, &dummy, NULL);
+       win_remove(curwin, NULL);
+       aucmd_win_used = FALSE;
+       last_status(FALSE);         // may need to remove last status line
+
+       if (!valid_tabpage_win(curtab))
+           // no valid window in current tabpage
+           close_tabpage(curtab);
+
+       restore_snapshot(SNAP_AUCMD_IDX, FALSE);
+       (void)win_comp_pos();   // recompute window positions
+       unblock_autocmds();
+
+       if (win_valid(aco->save_curwin))
+           curwin = aco->save_curwin;
+       else
+           // Hmm, original window disappeared.  Just use the first one.
+           curwin = firstwin;
+       if (win_valid(aco->save_prevwin))
+           prevwin = aco->save_prevwin;
+#ifdef FEAT_EVAL
+       vars_clear(&aucmd_win->w_vars->dv_hashtab);  // free all w: variables
+       hash_init(&aucmd_win->w_vars->dv_hashtab);   // re-use the hashtab
+#endif
+       curbuf = curwin->w_buffer;
+
+       vim_free(globaldir);
+       globaldir = aco->globaldir;
+
+       // the buffer contents may have changed
+       check_cursor();
+       if (curwin->w_topline > curbuf->b_ml.ml_line_count)
+       {
+           curwin->w_topline = curbuf->b_ml.ml_line_count;
+#ifdef FEAT_DIFF
+           curwin->w_topfill = 0;
+#endif
+       }
+#if defined(FEAT_GUI)
+       // Hide the scrollbars from the aucmd_win and update.
+       gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
+       gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
+       gui_may_update_scrollbars();
+#endif
+    }
+    else
+    {
+       // restore curwin
+       if (win_valid(aco->save_curwin))
+       {
+           // Restore the buffer which was previously edited by curwin, if
+           // it was changed, we are still the same window and the buffer is
+           // valid.
+           if (curwin == aco->new_curwin
+                   && curbuf != aco->new_curbuf.br_buf
+                   && bufref_valid(&aco->new_curbuf)
+                   && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
+           {
+# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
+               if (curwin->w_s == &curbuf->b_s)
+                   curwin->w_s = &aco->new_curbuf.br_buf->b_s;
+# endif
+               --curbuf->b_nwindows;
+               curbuf = aco->new_curbuf.br_buf;
+               curwin->w_buffer = curbuf;
+               ++curbuf->b_nwindows;
+           }
+
+           curwin = aco->save_curwin;
+           curbuf = curwin->w_buffer;
+           if (win_valid(aco->save_prevwin))
+               prevwin = aco->save_prevwin;
+           // In case the autocommand move the cursor to a position that that
+           // not exist in curbuf.
+           check_cursor();
+       }
+    }
+}
+
+static int     autocmd_nested = FALSE;
+
+/*
+ * Execute autocommands for "event" and file name "fname".
+ * Return TRUE if some commands were executed.
+ */
+    int
+apply_autocmds(
+    event_T    event,
+    char_u     *fname,     // NULL or empty means use actual file name
+    char_u     *fname_io,  // fname to use for <afile> on cmdline
+    int                force,      // when TRUE, ignore autocmd_busy
+    buf_T      *buf)       // buffer for <abuf>
+{
+    return apply_autocmds_group(event, fname, fname_io, force,
+                                                     AUGROUP_ALL, buf, NULL);
+}
+
+/*
+ * Like apply_autocmds(), but with extra "eap" argument.  This takes care of
+ * setting v:filearg.
+ */
+    int
+apply_autocmds_exarg(
+    event_T    event,
+    char_u     *fname,
+    char_u     *fname_io,
+    int                force,
+    buf_T      *buf,
+    exarg_T    *eap)
+{
+    return apply_autocmds_group(event, fname, fname_io, force,
+                                                      AUGROUP_ALL, buf, eap);
+}
+
+/*
+ * Like apply_autocmds(), but handles the caller's retval.  If the script
+ * processing is being aborted or if retval is FAIL when inside a try
+ * conditional, no autocommands are executed.  If otherwise the autocommands
+ * cause the script to be aborted, retval is set to FAIL.
+ */
+    int
+apply_autocmds_retval(
+    event_T    event,
+    char_u     *fname,     // NULL or empty means use actual file name
+    char_u     *fname_io,  // fname to use for <afile> on cmdline
+    int                force,      // when TRUE, ignore autocmd_busy
+    buf_T      *buf,       // buffer for <abuf>
+    int                *retval)    // pointer to caller's retval
+{
+    int                did_cmd;
+
+#ifdef FEAT_EVAL
+    if (should_abort(*retval))
+       return FALSE;
+#endif
+
+    did_cmd = apply_autocmds_group(event, fname, fname_io, force,
+                                                     AUGROUP_ALL, buf, NULL);
+    if (did_cmd
+#ifdef FEAT_EVAL
+           && aborting()
+#endif
+           )
+       *retval = FAIL;
+    return did_cmd;
+}
+
+/*
+ * Return TRUE when there is a CursorHold autocommand defined.
+ */
+    int
+has_cursorhold(void)
+{
+    return (first_autopat[(int)(get_real_state() == NORMAL_BUSY
+                           ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
+}
+
+/*
+ * Return TRUE if the CursorHold event can be triggered.
+ */
+    int
+trigger_cursorhold(void)
+{
+    int                state;
+
+    if (!did_cursorhold
+           && has_cursorhold()
+           && reg_recording == 0
+           && typebuf.tb_len == 0
+#ifdef FEAT_INS_EXPAND
+           && !ins_compl_active()
+#endif
+           )
+    {
+       state = get_real_state();
+       if (state == NORMAL_BUSY || (state & INSERT) != 0)
+           return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ * Return TRUE when there is a CursorMoved autocommand defined.
+ */
+    int
+has_cursormoved(void)
+{
+    return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
+}
+
+#if defined(FEAT_CONCEAL) || defined(PROTO)
+/*
+ * Return TRUE when there is a CursorMovedI autocommand defined.
+ */
+    int
+has_cursormovedI(void)
+{
+    return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
+}
+#endif
+
+/*
+ * Return TRUE when there is a TextChanged autocommand defined.
+ */
+    int
+has_textchanged(void)
+{
+    return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
+}
+
+/*
+ * Return TRUE when there is a TextChangedI autocommand defined.
+ */
+    int
+has_textchangedI(void)
+{
+    return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
+}
+
+#if defined(FEAT_INS_EXPAND) || defined(PROTO)
+/*
+ * Return TRUE when there is a TextChangedP autocommand defined.
+ */
+    int
+has_textchangedP(void)
+{
+    return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
+}
+#endif
+
+/*
+ * Return TRUE when there is an InsertCharPre autocommand defined.
+ */
+    int
+has_insertcharpre(void)
+{
+    return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
+}
+
+/*
+ * Return TRUE when there is an CmdUndefined autocommand defined.
+ */
+    int
+has_cmdundefined(void)
+{
+    return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
+}
+
+/*
+ * Return TRUE when there is an FuncUndefined autocommand defined.
+ */
+    int
+has_funcundefined(void)
+{
+    return (first_autopat[(int)EVENT_FUNCUNDEFINED] != NULL);
+}
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Return TRUE when there is a TextYankPost autocommand defined.
+ */
+    int
+has_textyankpost(void)
+{
+    return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
+}
+#endif
+
+/*
+ * Execute autocommands for "event" and file name "fname".
+ * Return TRUE if some commands were executed.
+ */
+    static int
+apply_autocmds_group(
+    event_T    event,
+    char_u     *fname,      // NULL or empty means use actual file name
+    char_u     *fname_io,   // fname to use for <afile> on cmdline, NULL means
+                            // use fname
+    int                force,       // when TRUE, ignore autocmd_busy
+    int                group,       // group ID, or AUGROUP_ALL
+    buf_T      *buf,        // buffer for <abuf>
+    exarg_T    *eap UNUSED) // command arguments
+{
+    char_u     *sfname = NULL; // short file name
+    char_u     *tail;
+    int                save_changed;
+    buf_T      *old_curbuf;
+    int                retval = FALSE;
+    char_u     *save_sourcing_name;
+    linenr_T   save_sourcing_lnum;
+    char_u     *save_autocmd_fname;
+    int                save_autocmd_fname_full;
+    int                save_autocmd_bufnr;
+    char_u     *save_autocmd_match;
+    int                save_autocmd_busy;
+    int                save_autocmd_nested;
+    static int nesting = 0;
+    AutoPatCmd patcmd;
+    AutoPat    *ap;
+#ifdef FEAT_EVAL
+    sctx_T     save_current_sctx;
+    funccal_entry_T funccal_entry;
+    char_u     *save_cmdarg;
+    long       save_cmdbang;
+#endif
+    static int filechangeshell_busy = FALSE;
+#ifdef FEAT_PROFILE
+    proftime_T wait_time;
+#endif
+    int                did_save_redobuff = FALSE;
+    save_redo_T        save_redo;
+    int                save_KeyTyped = KeyTyped;
+
+    /*
+     * Quickly return if there are no autocommands for this event or
+     * autocommands are blocked.
+     */
+    if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
+           || autocmd_blocked > 0)
+       goto BYPASS_AU;
+
+    /*
+     * When autocommands are busy, new autocommands are only executed when
+     * explicitly enabled with the "nested" flag.
+     */
+    if (autocmd_busy && !(force || autocmd_nested))
+       goto BYPASS_AU;
+
+#ifdef FEAT_EVAL
+    /*
+     * Quickly return when immediately aborting on error, or when an interrupt
+     * occurred or an exception was thrown but not caught.
+     */
+    if (aborting())
+       goto BYPASS_AU;
+#endif
+
+    /*
+     * FileChangedShell never nests, because it can create an endless loop.
+     */
+    if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
+                                     || event == EVENT_FILECHANGEDSHELLPOST))
+       goto BYPASS_AU;
+
+    /*
+     * Ignore events in 'eventignore'.
+     */
+    if (event_ignored(event))
+       goto BYPASS_AU;
+
+    /*
+     * Allow nesting of autocommands, but restrict the depth, because it's
+     * possible to create an endless loop.
+     */
+    if (nesting == 10)
+    {
+       emsg(_("E218: autocommand nesting too deep"));
+       goto BYPASS_AU;
+    }
+
+    /*
+     * Check if these autocommands are disabled.  Used when doing ":all" or
+     * ":ball".
+     */
+    if (       (autocmd_no_enter
+               && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
+           || (autocmd_no_leave
+               && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
+       goto BYPASS_AU;
+
+    /*
+     * Save the autocmd_* variables and info about the current buffer.
+     */
+    save_autocmd_fname = autocmd_fname;
+    save_autocmd_fname_full = autocmd_fname_full;
+    save_autocmd_bufnr = autocmd_bufnr;
+    save_autocmd_match = autocmd_match;
+    save_autocmd_busy = autocmd_busy;
+    save_autocmd_nested = autocmd_nested;
+    save_changed = curbuf->b_changed;
+    old_curbuf = curbuf;
+
+    /*
+     * Set the file name to be used for <afile>.
+     * Make a copy to avoid that changing a buffer name or directory makes it
+     * invalid.
+     */
+    if (fname_io == NULL)
+    {
+       if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
+                                                  || event == EVENT_OPTIONSET)
+           autocmd_fname = NULL;
+       else if (fname != NULL && !ends_excmd(*fname))
+           autocmd_fname = fname;
+       else if (buf != NULL)
+           autocmd_fname = buf->b_ffname;
+       else
+           autocmd_fname = NULL;
+    }
+    else
+       autocmd_fname = fname_io;
+    if (autocmd_fname != NULL)
+       autocmd_fname = vim_strsave(autocmd_fname);
+    autocmd_fname_full = FALSE; // call FullName_save() later
+
+    /*
+     * Set the buffer number to be used for <abuf>.
+     */
+    if (buf == NULL)
+       autocmd_bufnr = 0;
+    else
+       autocmd_bufnr = buf->b_fnum;
+
+    /*
+     * When the file name is NULL or empty, use the file name of buffer "buf".
+     * Always use the full path of the file name to match with, in case
+     * "allow_dirs" is set.
+     */
+    if (fname == NULL || *fname == NUL)
+    {
+       if (buf == NULL)
+           fname = NULL;
+       else
+       {
+#ifdef FEAT_SYN_HL
+           if (event == EVENT_SYNTAX)
+               fname = buf->b_p_syn;
+           else
+#endif
+               if (event == EVENT_FILETYPE)
+                   fname = buf->b_p_ft;
+               else
+               {
+                   if (buf->b_sfname != NULL)
+                       sfname = vim_strsave(buf->b_sfname);
+                   fname = buf->b_ffname;
+               }
+       }
+       if (fname == NULL)
+           fname = (char_u *)"";
+       fname = vim_strsave(fname);     // make a copy, so we can change it
+    }
+    else
+    {
+       sfname = vim_strsave(fname);
+       // Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
+       // ColorScheme, QuickFixCmd* or DirChanged
+       if (event == EVENT_FILETYPE
+               || event == EVENT_SYNTAX
+               || event == EVENT_CMDLINECHANGED
+               || event == EVENT_CMDLINEENTER
+               || event == EVENT_CMDLINELEAVE
+               || event == EVENT_CMDWINENTER
+               || event == EVENT_CMDWINLEAVE
+               || event == EVENT_CMDUNDEFINED
+               || event == EVENT_FUNCUNDEFINED
+               || event == EVENT_REMOTEREPLY
+               || event == EVENT_SPELLFILEMISSING
+               || event == EVENT_QUICKFIXCMDPRE
+               || event == EVENT_COLORSCHEME
+               || event == EVENT_COLORSCHEMEPRE
+               || event == EVENT_OPTIONSET
+               || event == EVENT_QUICKFIXCMDPOST
+               || event == EVENT_DIRCHANGED)
+       {
+           fname = vim_strsave(fname);
+           autocmd_fname_full = TRUE; // don't expand it later
+       }
+       else
+           fname = FullName_save(fname, FALSE);
+    }
+    if (fname == NULL)     // out of memory
+    {
+       vim_free(sfname);
+       retval = FALSE;
+       goto BYPASS_AU;
+    }
+
+#ifdef BACKSLASH_IN_FILENAME
+    /*
+     * Replace all backslashes with forward slashes.  This makes the
+     * autocommand patterns portable between Unix and MS-DOS.
+     */
+    if (sfname != NULL)
+       forward_slash(sfname);
+    forward_slash(fname);
+#endif
+
+#ifdef VMS
+    // remove version for correct match
+    if (sfname != NULL)
+       vms_remove_version(sfname);
+    vms_remove_version(fname);
+#endif
+
+    /*
+     * Set the name to be used for <amatch>.
+     */
+    autocmd_match = fname;
+
+
+    // Don't redraw while doing autocommands.
+    ++RedrawingDisabled;
+    save_sourcing_name = sourcing_name;
+    sourcing_name = NULL;      // don't free this one
+    save_sourcing_lnum = sourcing_lnum;
+    sourcing_lnum = 0;         // no line number here
+
+#ifdef FEAT_EVAL
+    save_current_sctx = current_sctx;
+
+# ifdef FEAT_PROFILE
+    if (do_profiling == PROF_YES)
+       prof_child_enter(&wait_time); // doesn't count for the caller itself
+# endif
+
+    // Don't use local function variables, if called from a function.
+    save_funccal(&funccal_entry);
+#endif
+
+    /*
+     * When starting to execute autocommands, save the search patterns.
+     */
+    if (!autocmd_busy)
+    {
+       save_search_patterns();
+#ifdef FEAT_INS_EXPAND
+       if (!ins_compl_active())
+#endif
+       {
+           saveRedobuff(&save_redo);
+           did_save_redobuff = TRUE;
+       }
+       did_filetype = keep_filetype;
+    }
+
+    /*
+     * Note that we are applying autocmds.  Some commands need to know.
+     */
+    autocmd_busy = TRUE;
+    filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
+    ++nesting;         // see matching decrement below
+
+    // Remember that FileType was triggered.  Used for did_filetype().
+    if (event == EVENT_FILETYPE)
+       did_filetype = TRUE;
+
+    tail = gettail(fname);
+
+    // Find first autocommand that matches
+    patcmd.curpat = first_autopat[(int)event];
+    patcmd.nextcmd = NULL;
+    patcmd.group = group;
+    patcmd.fname = fname;
+    patcmd.sfname = sfname;
+    patcmd.tail = tail;
+    patcmd.event = event;
+    patcmd.arg_bufnr = autocmd_bufnr;
+    patcmd.next = NULL;
+    auto_next_pat(&patcmd, FALSE);
+
+    // found one, start executing the autocommands
+    if (patcmd.curpat != NULL)
+    {
+       // add to active_apc_list
+       patcmd.next = active_apc_list;
+       active_apc_list = &patcmd;
+
+#ifdef FEAT_EVAL
+       // set v:cmdarg (only when there is a matching pattern)
+       save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
+       if (eap != NULL)
+       {
+           save_cmdarg = set_cmdarg(eap, NULL);
+           set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
+       }
+       else
+           save_cmdarg = NULL; // avoid gcc warning
+#endif
+       retval = TRUE;
+       // mark the last pattern, to avoid an endless loop when more patterns
+       // are added when executing autocommands
+       for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
+           ap->last = FALSE;
+       ap->last = TRUE;
+       check_lnums(TRUE);      // make sure cursor and topline are valid
+       do_cmdline(NULL, getnextac, (void *)&patcmd,
+                                    DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
+#ifdef FEAT_EVAL
+       if (eap != NULL)
+       {
+           (void)set_cmdarg(NULL, save_cmdarg);
+           set_vim_var_nr(VV_CMDBANG, save_cmdbang);
+       }
+#endif
+       // delete from active_apc_list
+       if (active_apc_list == &patcmd)     // just in case
+           active_apc_list = patcmd.next;
+    }
+
+    --RedrawingDisabled;
+    autocmd_busy = save_autocmd_busy;
+    filechangeshell_busy = FALSE;
+    autocmd_nested = save_autocmd_nested;
+    vim_free(sourcing_name);
+    sourcing_name = save_sourcing_name;
+    sourcing_lnum = save_sourcing_lnum;
+    vim_free(autocmd_fname);
+    autocmd_fname = save_autocmd_fname;
+    autocmd_fname_full = save_autocmd_fname_full;
+    autocmd_bufnr = save_autocmd_bufnr;
+    autocmd_match = save_autocmd_match;
+#ifdef FEAT_EVAL
+    current_sctx = save_current_sctx;
+    restore_funccal();
+# ifdef FEAT_PROFILE
+    if (do_profiling == PROF_YES)
+       prof_child_exit(&wait_time);
+# endif
+#endif
+    KeyTyped = save_KeyTyped;
+    vim_free(fname);
+    vim_free(sfname);
+    --nesting;         // see matching increment above
+
+    /*
+     * When stopping to execute autocommands, restore the search patterns and
+     * the redo buffer.  Free any buffers in the au_pending_free_buf list and
+     * free any windows in the au_pending_free_win list.
+     */
+    if (!autocmd_busy)
+    {
+       restore_search_patterns();
+       if (did_save_redobuff)
+           restoreRedobuff(&save_redo);
+       did_filetype = FALSE;
+       while (au_pending_free_buf != NULL)
+       {
+           buf_T *b = au_pending_free_buf->b_next;
+           vim_free(au_pending_free_buf);
+           au_pending_free_buf = b;
+       }
+       while (au_pending_free_win != NULL)
+       {
+           win_T *w = au_pending_free_win->w_next;
+           vim_free(au_pending_free_win);
+           au_pending_free_win = w;
+       }
+    }
+
+    /*
+     * Some events don't set or reset the Changed flag.
+     * Check if still in the same buffer!
+     */
+    if (curbuf == old_curbuf
+           && (event == EVENT_BUFREADPOST
+               || event == EVENT_BUFWRITEPOST
+               || event == EVENT_FILEAPPENDPOST
+               || event == EVENT_VIMLEAVE
+               || event == EVENT_VIMLEAVEPRE))
+    {
+#ifdef FEAT_TITLE
+       if (curbuf->b_changed != save_changed)
+           need_maketitle = TRUE;
+#endif
+       curbuf->b_changed = save_changed;
+    }
+
+    au_cleanup();      // may really delete removed patterns/commands now
+
+BYPASS_AU:
+    // When wiping out a buffer make sure all its buffer-local autocommands
+    // are deleted.
+    if (event == EVENT_BUFWIPEOUT && buf != NULL)
+       aubuflocal_remove(buf);
+
+    if (retval == OK && event == EVENT_FILETYPE)
+       au_did_filetype = TRUE;
+
+    return retval;
+}
+
+# ifdef FEAT_EVAL
+static char_u  *old_termresponse = NULL;
+# endif
+
+/*
+ * Block triggering autocommands until unblock_autocmd() is called.
+ * Can be used recursively, so long as it's symmetric.
+ */
+    void
+block_autocmds(void)
+{
+# ifdef FEAT_EVAL
+    // Remember the value of v:termresponse.
+    if (autocmd_blocked == 0)
+       old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
+# endif
+    ++autocmd_blocked;
+}
+
+    void
+unblock_autocmds(void)
+{
+    --autocmd_blocked;
+
+# ifdef FEAT_EVAL
+    // When v:termresponse was set while autocommands were blocked, trigger
+    // the autocommands now.  Esp. useful when executing a shell command
+    // during startup (vimdiff).
+    if (autocmd_blocked == 0
+                     && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
+       apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
+# endif
+}
+
+#if defined(FEAT_EVAL) && (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM)) \
+       || defined(PROTO)
+    int
+is_autocmd_blocked(void)
+{
+    return autocmd_blocked != 0;
+}
+#endif
+
+/*
+ * Find next autocommand pattern that matches.
+ */
+    static void
+auto_next_pat(
+    AutoPatCmd *apc,
+    int                stop_at_last)       // stop when 'last' flag is set
+{
+    AutoPat    *ap;
+    AutoCmd    *cp;
+    char_u     *name;
+    char       *s;
+
+    VIM_CLEAR(sourcing_name);
+
+    for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
+    {
+       apc->curpat = NULL;
+
+       // Only use a pattern when it has not been removed, has commands and
+       // the group matches. For buffer-local autocommands only check the
+       // buffer number.
+       if (ap->pat != NULL && ap->cmds != NULL
+               && (apc->group == AUGROUP_ALL || apc->group == ap->group))
+       {
+           // execution-condition
+           if (ap->buflocal_nr == 0
+                   ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
+                                     apc->sfname, apc->tail, ap->allow_dirs))
+                   : ap->buflocal_nr == apc->arg_bufnr)
+           {
+               name = event_nr2name(apc->event);
+               s = _("%s Autocommands for \"%s\"");
+               sourcing_name = alloc((unsigned)(STRLEN(s)
+                                           + STRLEN(name) + ap->patlen + 1));
+               if (sourcing_name != NULL)
+               {
+                   sprintf((char *)sourcing_name, s,
+                                              (char *)name, (char *)ap->pat);
+                   if (p_verbose >= 8)
+                   {
+                       verbose_enter();
+                       smsg(_("Executing %s"), sourcing_name);
+                       verbose_leave();
+                   }
+               }
+
+               apc->curpat = ap;
+               apc->nextcmd = ap->cmds;
+               // mark last command
+               for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
+                   cp->last = FALSE;
+               cp->last = TRUE;
+           }
+           line_breakcheck();
+           if (apc->curpat != NULL)        // found a match
+               break;
+       }
+       if (stop_at_last && ap->last)
+           break;
+    }
+}
+
+/*
+ * Get next autocommand command.
+ * Called by do_cmdline() to get the next line for ":if".
+ * Returns allocated string, or NULL for end of autocommands.
+ */
+    char_u *
+getnextac(int c UNUSED, void *cookie, int indent UNUSED)
+{
+    AutoPatCmd     *acp = (AutoPatCmd *)cookie;
+    char_u         *retval;
+    AutoCmd        *ac;
+
+    // Can be called again after returning the last line.
+    if (acp->curpat == NULL)
+       return NULL;
+
+    // repeat until we find an autocommand to execute
+    for (;;)
+    {
+       // skip removed commands
+       while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
+           if (acp->nextcmd->last)
+               acp->nextcmd = NULL;
+           else
+               acp->nextcmd = acp->nextcmd->next;
+
+       if (acp->nextcmd != NULL)
+           break;
+
+       // at end of commands, find next pattern that matches
+       if (acp->curpat->last)
+           acp->curpat = NULL;
+       else
+           acp->curpat = acp->curpat->next;
+       if (acp->curpat != NULL)
+           auto_next_pat(acp, TRUE);
+       if (acp->curpat == NULL)
+           return NULL;
+    }
+
+    ac = acp->nextcmd;
+
+    if (p_verbose >= 9)
+    {
+       verbose_enter_scroll();
+       smsg(_("autocommand %s"), ac->cmd);
+       msg_puts("\n");   // don't overwrite this either
+       verbose_leave_scroll();
+    }
+    retval = vim_strsave(ac->cmd);
+    autocmd_nested = ac->nested;
+#ifdef FEAT_EVAL
+    current_sctx = ac->script_ctx;
+#endif
+    if (ac->last)
+       acp->nextcmd = NULL;
+    else
+       acp->nextcmd = ac->next;
+    return retval;
+}
+
+/*
+ * Return TRUE if there is a matching autocommand for "fname".
+ * To account for buffer-local autocommands, function needs to know
+ * in which buffer the file will be opened.
+ */
+    int
+has_autocmd(event_T event, char_u *sfname, buf_T *buf)
+{
+    AutoPat    *ap;
+    char_u     *fname;
+    char_u     *tail = gettail(sfname);
+    int                retval = FALSE;
+
+    fname = FullName_save(sfname, FALSE);
+    if (fname == NULL)
+       return FALSE;
+
+#ifdef BACKSLASH_IN_FILENAME
+    /*
+     * Replace all backslashes with forward slashes.  This makes the
+     * autocommand patterns portable between Unix and MS-DOS.
+     */
+    sfname = vim_strsave(sfname);
+    if (sfname != NULL)
+       forward_slash(sfname);
+    forward_slash(fname);
+#endif
+
+    for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
+       if (ap->pat != NULL && ap->cmds != NULL
+             && (ap->buflocal_nr == 0
+               ? match_file_pat(NULL, &ap->reg_prog,
+                                         fname, sfname, tail, ap->allow_dirs)
+               : buf != NULL && ap->buflocal_nr == buf->b_fnum
+          ))
+       {
+           retval = TRUE;
+           break;
+       }
+
+    vim_free(fname);
+#ifdef BACKSLASH_IN_FILENAME
+    vim_free(sfname);
+#endif
+
+    return retval;
+}
+
+#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
+/*
+ * Function given to ExpandGeneric() to obtain the list of autocommand group
+ * names.
+ */
+    char_u *
+get_augroup_name(expand_T *xp UNUSED, int idx)
+{
+    if (idx == augroups.ga_len)                // add "END" add the end
+       return (char_u *)"END";
+    if (idx >= augroups.ga_len)                // end of list
+       return NULL;
+    if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
+       // skip deleted entries
+       return (char_u *)"";
+    return AUGROUP_NAME(idx);          // return a name
+}
+
+static int include_groups = FALSE;
+
+    char_u  *
+set_context_in_autocmd(
+    expand_T   *xp,
+    char_u     *arg,
+    int                doautocmd)      // TRUE for :doauto*, FALSE for :autocmd
+{
+    char_u     *p;
+    int                group;
+
+    // check for a group name, skip it if present
+    include_groups = FALSE;
+    p = arg;
+    group = au_get_grouparg(&arg);
+    if (group == AUGROUP_ERROR)
+       return NULL;
+    // If there only is a group name that's what we expand.
+    if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
+    {
+       arg = p;
+       group = AUGROUP_ALL;
+    }
+
+    // skip over event name
+    for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
+       if (*p == ',')
+           arg = p + 1;
+    if (*p == NUL)
+    {
+       if (group == AUGROUP_ALL)
+           include_groups = TRUE;
+       xp->xp_context = EXPAND_EVENTS;     // expand event name
+       xp->xp_pattern = arg;
+       return NULL;
+    }
+
+    // skip over pattern
+    arg = skipwhite(p);
+    while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
+       arg++;
+    if (*arg)
+       return arg;                         // expand (next) command
+
+    if (doautocmd)
+       xp->xp_context = EXPAND_FILES;      // expand file names
+    else
+       xp->xp_context = EXPAND_NOTHING;    // pattern is not expanded
+    return NULL;
+}
+
+/*
+ * Function given to ExpandGeneric() to obtain the list of event names.
+ */
+    char_u *
+get_event_name(expand_T *xp UNUSED, int idx)
+{
+    if (idx < augroups.ga_len)         // First list group names, if wanted
+    {
+       if (!include_groups || AUGROUP_NAME(idx) == NULL
+                                || AUGROUP_NAME(idx) == get_deleted_augroup())
+           return (char_u *)"";        // skip deleted entries
+       return AUGROUP_NAME(idx);       // return a name
+    }
+    return (char_u *)event_names[idx - augroups.ga_len].name;
+}
+
+#endif // FEAT_CMDL_COMPL
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Return TRUE if autocmd is supported.
+ */
+    int
+autocmd_supported(char_u *name)
+{
+    char_u *p;
+
+    return (event_name2nr(name, &p) != NUM_EVENTS);
+}
+
+/*
+ * Return TRUE if an autocommand is defined for a group, event and
+ * pattern:  The group can be omitted to accept any group. "event" and "pattern"
+ * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
+ * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
+ * Used for:
+ *     exists("#Group") or
+ *     exists("#Group#Event") or
+ *     exists("#Group#Event#pat") or
+ *     exists("#Event") or
+ *     exists("#Event#pat")
+ */
+    int
+au_exists(char_u *arg)
+{
+    char_u     *arg_save;
+    char_u     *pattern = NULL;
+    char_u     *event_name;
+    char_u     *p;
+    event_T    event;
+    AutoPat    *ap;
+    buf_T      *buflocal_buf = NULL;
+    int                group;
+    int                retval = FALSE;
+
+    // Make a copy so that we can change the '#' chars to a NUL.
+    arg_save = vim_strsave(arg);
+    if (arg_save == NULL)
+       return FALSE;
+    p = vim_strchr(arg_save, '#');
+    if (p != NULL)
+       *p++ = NUL;
+
+    // First, look for an autocmd group name
+    group = au_find_group(arg_save);
+    if (group == AUGROUP_ERROR)
+    {
+       // Didn't match a group name, assume the first argument is an event.
+       group = AUGROUP_ALL;
+       event_name = arg_save;
+    }
+    else
+    {
+       if (p == NULL)
+       {
+           // "Group": group name is present and it's recognized
+           retval = TRUE;
+           goto theend;
+       }
+
+       // Must be "Group#Event" or "Group#Event#pat".
+       event_name = p;
+       p = vim_strchr(event_name, '#');
+       if (p != NULL)
+           *p++ = NUL;     // "Group#Event#pat"
+    }
+
+    pattern = p;           // "pattern" is NULL when there is no pattern
+
+    // find the index (enum) for the event name
+    event = event_name2nr(event_name, &p);
+
+    // return FALSE if the event name is not recognized
+    if (event == NUM_EVENTS)
+       goto theend;
+
+    // Find the first autocommand for this event.
+    // If there isn't any, return FALSE;
+    // If there is one and no pattern given, return TRUE;
+    ap = first_autopat[(int)event];
+    if (ap == NULL)
+       goto theend;
+
+    // if pattern is "<buffer>", special handling is needed which uses curbuf
+    // for pattern "<buffer=N>, fnamecmp() will work fine
+    if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
+       buflocal_buf = curbuf;
+
+    // Check if there is an autocommand with the given pattern.
+    for ( ; ap != NULL; ap = ap->next)
+       // only use a pattern when it has not been removed and has commands.
+       // For buffer-local autocommands, fnamecmp() works fine.
+       if (ap->pat != NULL && ap->cmds != NULL
+           && (group == AUGROUP_ALL || ap->group == group)
+           && (pattern == NULL
+               || (buflocal_buf == NULL
+                   ? fnamecmp(ap->pat, pattern) == 0
+                   : ap->buflocal_nr == buflocal_buf->b_fnum)))
+       {
+           retval = TRUE;
+           break;
+       }
+
+theend:
+    vim_free(arg_save);
+    return retval;
+}
+#endif
index 4cb13f27a5e904733ab8fa67d47b8e703f8bf10b..bf724f642fcca5fcec01aab6796266d98917bb8d 100644 (file)
@@ -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,2573 +7535,14 @@ 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.
+ * Try matching a filename with a "pattern" ("prog" is NULL), or use the
+ * precompiled regprog "prog" ("pattern" is NULL).  That avoids calling
+ * vim_regcomp() often.
+ * Used for autocommands and 'wildignore'.
+ * Returns TRUE if there is a match, FALSE otherwise.
  */
     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
-            * pattern at the end of the list.
-            */
-           if (ap == NULL)
-           {
-               /* refuse to add buffer-local ap if buffer number is invalid */
-               if (is_buflocal && (buflocal_nr == 0
-                                     || buflist_findnr(buflocal_nr) == NULL))
-               {
-                   semsg(_("E680: <buffer=%d>: invalid buffer number "),
-                                                                buflocal_nr);
-                   return FAIL;
-               }
-
-               ap = (AutoPat *)alloc((unsigned)sizeof(AutoPat));
-               if (ap == NULL)
-                   return FAIL;
-               ap->pat = vim_strnsave(pat, patlen);
-               ap->patlen = patlen;
-               if (ap->pat == NULL)
-               {
-                   vim_free(ap);
-                   return FAIL;
-               }
-
-               if (is_buflocal)
-               {
-                   ap->buflocal_nr = buflocal_nr;
-                   ap->reg_prog = NULL;
-               }
-               else
-               {
-                   char_u      *reg_pat;
-
-                   ap->buflocal_nr = 0;
-                   reg_pat = file_pat_to_reg_pat(pat, endpat,
-                                                        &ap->allow_dirs, TRUE);
-                   if (reg_pat != NULL)
-                       ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
-                   vim_free(reg_pat);
-                   if (reg_pat == NULL || ap->reg_prog == NULL)
-                   {
-                       vim_free(ap->pat);
-                       vim_free(ap);
-                       return FAIL;
-                   }
-               }
-               ap->cmds = NULL;
-               *prev_ap = ap;
-               last_autopat[(int)event] = ap;
-               ap->next = NULL;
-               if (group == AUGROUP_ALL)
-                   ap->group = current_augroup;
-               else
-                   ap->group = group;
-           }
-
-           /*
-            * Add the autocmd at the end of the AutoCmd list.
-            */
-           prev_ac = &(ap->cmds);
-           while ((ac = *prev_ac) != NULL)
-               prev_ac = &ac->next;
-           ac = (AutoCmd *)alloc((unsigned)sizeof(AutoCmd));
-           if (ac == NULL)
-               return FAIL;
-           ac->cmd = vim_strsave(cmd);
-#ifdef FEAT_EVAL
-           ac->script_ctx = current_sctx;
-           ac->script_ctx.sc_lnum += sourcing_lnum;
-#endif
-           if (ac->cmd == NULL)
-           {
-               vim_free(ac);
-               return FAIL;
-           }
-           ac->next = NULL;
-           *prev_ac = ac;
-           ac->nested = nested;
-       }
-    }
-
-    au_cleanup();      /* may really delete removed patterns/commands now */
-    return OK;
-}
-
-/*
- * Implementation of ":doautocmd [group] event [fname]".
- * Return OK for success, FAIL for failure;
- */
-    int
-do_doautocmd(
-    char_u     *arg,
-    int                do_msg,     /* give message for no matching autocmds? */
-    int                *did_something)
-{
-    char_u     *fname;
-    int                nothing_done = TRUE;
-    int                group;
-
-    if (did_something != NULL)
-       *did_something = FALSE;
-
-    /*
-     * Check for a legal group name.  If not, use AUGROUP_ALL.
-     */
-    group = au_get_grouparg(&arg);
-    if (arg == NULL)       /* out of memory */
-       return FAIL;
-
-    if (*arg == '*')
-    {
-       emsg(_("E217: Can't execute autocommands for ALL events"));
-       return FAIL;
-    }
-
-    /*
-     * Scan over the events.
-     * If we find an illegal name, return here, don't do anything.
-     */
-    fname = find_end_event(arg, group != AUGROUP_ALL);
-    if (fname == NULL)
-       return FAIL;
-
-    fname = skipwhite(fname);
-
-    /*
-     * Loop over the events.
-     */
-    while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
-       if (apply_autocmds_group(event_name2nr(arg, &arg),
-                                     fname, NULL, TRUE, group, curbuf, NULL))
-           nothing_done = FALSE;
-
-    if (nothing_done && do_msg)
-       msg(_("No matching autocommands"));
-    if (did_something != NULL)
-       *did_something = !nothing_done;
-
-#ifdef FEAT_EVAL
-    return aborting() ? FAIL : OK;
-#else
-    return OK;
-#endif
-}
-
-/*
- * ":doautoall": execute autocommands for each loaded buffer.
- */
-    void
-ex_doautoall(exarg_T *eap)
-{
-    int                retval;
-    aco_save_T aco;
-    buf_T      *buf;
-    bufref_T   bufref;
-    char_u     *arg = eap->arg;
-    int                call_do_modelines = check_nomodeline(&arg);
-    int                did_aucmd;
-
-    /*
-     * This is a bit tricky: For some commands curwin->w_buffer needs to be
-     * equal to curbuf, but for some buffers there may not be a window.
-     * So we change the buffer for the current window for a moment.  This
-     * gives problems when the autocommands make changes to the list of
-     * buffers or windows...
-     */
-    FOR_ALL_BUFFERS(buf)
-    {
-       if (buf->b_ml.ml_mfp != NULL)
-       {
-           /* find a window for this buffer and save some values */
-           aucmd_prepbuf(&aco, buf);
-           set_bufref(&bufref, buf);
-
-           /* execute the autocommands for this buffer */
-           retval = do_doautocmd(arg, FALSE, &did_aucmd);
-
-           if (call_do_modelines && did_aucmd)
-           {
-               /* Execute the modeline settings, but don't set window-local
-                * options if we are using the current window for another
-                * buffer. */
-               do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
-           }
-
-           /* restore the current window */
-           aucmd_restbuf(&aco);
-
-           /* stop if there is some error or buffer was deleted */
-           if (retval == FAIL || !bufref_valid(&bufref))
-               break;
-       }
-    }
-
-    check_cursor();        /* just in case lines got deleted */
-}
-
-/*
- * Check *argp for <nomodeline>.  When it is present return FALSE, otherwise
- * return TRUE and advance *argp to after it.
- * Thus return TRUE when do_modelines() should be called.
- */
-    int
-check_nomodeline(char_u **argp)
-{
-    if (STRNCMP(*argp, "<nomodeline>", 12) == 0)
-    {
-       *argp = skipwhite(*argp + 12);
-       return FALSE;
-    }
-    return TRUE;
-}
-
-/*
- * Prepare for executing autocommands for (hidden) buffer "buf".
- * Search for a visible window containing the current buffer.  If there isn't
- * one then use "aucmd_win".
- * Set "curbuf" and "curwin" to match "buf".
- */
-    void
-aucmd_prepbuf(
-    aco_save_T *aco,           /* structure to save values in */
-    buf_T      *buf)           /* new curbuf */
-{
-    win_T      *win;
-    int                save_ea;
-#ifdef FEAT_AUTOCHDIR
-    int                save_acd;
-#endif
-
-    /* Find a window that is for the new buffer */
-    if (buf == curbuf)         /* be quick when buf is curbuf */
-       win = curwin;
-    else
-       FOR_ALL_WINDOWS(win)
-           if (win->w_buffer == buf)
-               break;
-
-    /* Allocate "aucmd_win" when needed.  If this fails (out of memory) fall
-     * back to using the current window. */
-    if (win == NULL && aucmd_win == NULL)
-    {
-       win_alloc_aucmd_win();
-       if (aucmd_win == NULL)
-           win = curwin;
-    }
-    if (win == NULL && aucmd_win_used)
-       /* Strange recursive autocommand, fall back to using the current
-        * window.  Expect a few side effects... */
-       win = curwin;
-
-    aco->save_curwin = curwin;
-    aco->save_curbuf = curbuf;
-    aco->save_prevwin = prevwin;
-    if (win != NULL)
-    {
-       /* There is a window for "buf" in the current tab page, make it the
-        * curwin.  This is preferred, it has the least side effects (esp. if
-        * "buf" is curbuf). */
-       aco->use_aucmd_win = FALSE;
-       curwin = win;
-    }
-    else
-    {
-       /* There is no window for "buf", use "aucmd_win".  To minimize the side
-        * effects, insert it in the current tab page.
-        * Anything related to a window (e.g., setting folds) may have
-        * unexpected results. */
-       aco->use_aucmd_win = TRUE;
-       aucmd_win_used = TRUE;
-       aucmd_win->w_buffer = buf;
-#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
-       aucmd_win->w_s = &buf->b_s;
-#endif
-       ++buf->b_nwindows;
-       win_init_empty(aucmd_win); /* set cursor and topline to safe values */
-
-       /* Make sure w_localdir and globaldir are NULL to avoid a chdir() in
-        * win_enter_ext(). */
-       VIM_CLEAR(aucmd_win->w_localdir);
-       aco->globaldir = globaldir;
-       globaldir = NULL;
-
-
-       /* Split the current window, put the aucmd_win in the upper half.
-        * We don't want the BufEnter or WinEnter autocommands. */
-       block_autocmds();
-       make_snapshot(SNAP_AUCMD_IDX);
-       save_ea = p_ea;
-       p_ea = FALSE;
-
-#ifdef FEAT_AUTOCHDIR
-       /* Prevent chdir() call in win_enter_ext(), through do_autochdir(). */
-       save_acd = p_acd;
-       p_acd = FALSE;
-#endif
-
-       (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
-       (void)win_comp_pos();   /* recompute window positions */
-       p_ea = save_ea;
-#ifdef FEAT_AUTOCHDIR
-       p_acd = save_acd;
-#endif
-       unblock_autocmds();
-       curwin = aucmd_win;
-    }
-    curbuf = buf;
-    aco->new_curwin = curwin;
-    set_bufref(&aco->new_curbuf, curbuf);
-}
-
-/*
- * Cleanup after executing autocommands for a (hidden) buffer.
- * Restore the window as it was (if possible).
- */
-    void
-aucmd_restbuf(
-    aco_save_T *aco)           /* structure holding saved values */
-{
-    int dummy;
-
-    if (aco->use_aucmd_win)
-    {
-       --curbuf->b_nwindows;
-       /* Find "aucmd_win", it can't be closed, but it may be in another tab
-        * page. Do not trigger autocommands here. */
-       block_autocmds();
-       if (curwin != aucmd_win)
-       {
-           tabpage_T   *tp;
-           win_T       *wp;
-
-           FOR_ALL_TAB_WINDOWS(tp, wp)
-           {
-               if (wp == aucmd_win)
-               {
-                   if (tp != curtab)
-                       goto_tabpage_tp(tp, TRUE, TRUE);
-                   win_goto(aucmd_win);
-                   goto win_found;
-               }
-           }
-       }
-win_found:
-
-       /* Remove the window and frame from the tree of frames. */
-       (void)winframe_remove(curwin, &dummy, NULL);
-       win_remove(curwin, NULL);
-       aucmd_win_used = FALSE;
-       last_status(FALSE);         /* may need to remove last status line */
-
-       if (!valid_tabpage_win(curtab))
-           /* no valid window in current tabpage */
-           close_tabpage(curtab);
-
-       restore_snapshot(SNAP_AUCMD_IDX, FALSE);
-       (void)win_comp_pos();   /* recompute window positions */
-       unblock_autocmds();
-
-       if (win_valid(aco->save_curwin))
-           curwin = aco->save_curwin;
-       else
-           /* Hmm, original window disappeared.  Just use the first one. */
-           curwin = firstwin;
-       if (win_valid(aco->save_prevwin))
-           prevwin = aco->save_prevwin;
-#ifdef FEAT_EVAL
-       vars_clear(&aucmd_win->w_vars->dv_hashtab);  /* free all w: variables */
-       hash_init(&aucmd_win->w_vars->dv_hashtab);   /* re-use the hashtab */
-#endif
-       curbuf = curwin->w_buffer;
-
-       vim_free(globaldir);
-       globaldir = aco->globaldir;
-
-       /* the buffer contents may have changed */
-       check_cursor();
-       if (curwin->w_topline > curbuf->b_ml.ml_line_count)
-       {
-           curwin->w_topline = curbuf->b_ml.ml_line_count;
-#ifdef FEAT_DIFF
-           curwin->w_topfill = 0;
-#endif
-       }
-#if defined(FEAT_GUI)
-       /* Hide the scrollbars from the aucmd_win and update. */
-       gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_LEFT], FALSE);
-       gui_mch_enable_scrollbar(&aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE);
-       gui_may_update_scrollbars();
-#endif
-    }
-    else
-    {
-       /* restore curwin */
-       if (win_valid(aco->save_curwin))
-       {
-           /* Restore the buffer which was previously edited by curwin, if
-            * it was changed, we are still the same window and the buffer is
-            * valid. */
-           if (curwin == aco->new_curwin
-                   && curbuf != aco->new_curbuf.br_buf
-                   && bufref_valid(&aco->new_curbuf)
-                   && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL)
-           {
-# if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
-               if (curwin->w_s == &curbuf->b_s)
-                   curwin->w_s = &aco->new_curbuf.br_buf->b_s;
-# endif
-               --curbuf->b_nwindows;
-               curbuf = aco->new_curbuf.br_buf;
-               curwin->w_buffer = curbuf;
-               ++curbuf->b_nwindows;
-           }
-
-           curwin = aco->save_curwin;
-           curbuf = curwin->w_buffer;
-           if (win_valid(aco->save_prevwin))
-               prevwin = aco->save_prevwin;
-           /* In case the autocommand move the cursor to a position that that
-            * not exist in curbuf. */
-           check_cursor();
-       }
-    }
-}
-
-static int     autocmd_nested = FALSE;
-
-/*
- * Execute autocommands for "event" and file name "fname".
- * Return TRUE if some commands were executed.
- */
-    int
-apply_autocmds(
-    event_T    event,
-    char_u     *fname,     /* NULL or empty means use actual file name */
-    char_u     *fname_io,  /* fname to use for <afile> on cmdline */
-    int                force,      /* when TRUE, ignore autocmd_busy */
-    buf_T      *buf)       /* buffer for <abuf> */
-{
-    return apply_autocmds_group(event, fname, fname_io, force,
-                                                     AUGROUP_ALL, buf, NULL);
-}
-
-/*
- * Like apply_autocmds(), but with extra "eap" argument.  This takes care of
- * setting v:filearg.
- */
-    static int
-apply_autocmds_exarg(
-    event_T    event,
-    char_u     *fname,
-    char_u     *fname_io,
-    int                force,
-    buf_T      *buf,
-    exarg_T    *eap)
-{
-    return apply_autocmds_group(event, fname, fname_io, force,
-                                                      AUGROUP_ALL, buf, eap);
-}
-
-/*
- * Like apply_autocmds(), but handles the caller's retval.  If the script
- * processing is being aborted or if retval is FAIL when inside a try
- * conditional, no autocommands are executed.  If otherwise the autocommands
- * cause the script to be aborted, retval is set to FAIL.
- */
-    int
-apply_autocmds_retval(
-    event_T    event,
-    char_u     *fname,     /* NULL or empty means use actual file name */
-    char_u     *fname_io,  /* fname to use for <afile> on cmdline */
-    int                force,      /* when TRUE, ignore autocmd_busy */
-    buf_T      *buf,       /* buffer for <abuf> */
-    int                *retval)    /* pointer to caller's retval */
-{
-    int                did_cmd;
-
-#ifdef FEAT_EVAL
-    if (should_abort(*retval))
-       return FALSE;
-#endif
-
-    did_cmd = apply_autocmds_group(event, fname, fname_io, force,
-                                                     AUGROUP_ALL, buf, NULL);
-    if (did_cmd
-#ifdef FEAT_EVAL
-           && aborting()
-#endif
-           )
-       *retval = FAIL;
-    return did_cmd;
-}
-
-/*
- * Return TRUE when there is a CursorHold autocommand defined.
- */
-    int
-has_cursorhold(void)
-{
-    return (first_autopat[(int)(get_real_state() == NORMAL_BUSY
-                           ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL);
-}
-
-/*
- * Return TRUE if the CursorHold event can be triggered.
- */
-    int
-trigger_cursorhold(void)
-{
-    int                state;
-
-    if (!did_cursorhold
-           && has_cursorhold()
-           && reg_recording == 0
-           && typebuf.tb_len == 0
-#ifdef FEAT_INS_EXPAND
-           && !ins_compl_active()
-#endif
-           )
-    {
-       state = get_real_state();
-       if (state == NORMAL_BUSY || (state & INSERT) != 0)
-           return TRUE;
-    }
-    return FALSE;
-}
-
-/*
- * Return TRUE when there is a CursorMoved autocommand defined.
- */
-    int
-has_cursormoved(void)
-{
-    return (first_autopat[(int)EVENT_CURSORMOVED] != NULL);
-}
-
-#if defined(FEAT_CONCEAL) || defined(PROTO)
-/*
- * Return TRUE when there is a CursorMovedI autocommand defined.
- */
-    int
-has_cursormovedI(void)
-{
-    return (first_autopat[(int)EVENT_CURSORMOVEDI] != NULL);
-}
-#endif
-
-/*
- * Return TRUE when there is a TextChanged autocommand defined.
- */
-    int
-has_textchanged(void)
-{
-    return (first_autopat[(int)EVENT_TEXTCHANGED] != NULL);
-}
-
-/*
- * Return TRUE when there is a TextChangedI autocommand defined.
- */
-    int
-has_textchangedI(void)
-{
-    return (first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL);
-}
-
-#if defined(FEAT_INS_EXPAND) || defined(PROTO)
-/*
- * Return TRUE when there is a TextChangedP autocommand defined.
- */
-    int
-has_textchangedP(void)
-{
-    return (first_autopat[(int)EVENT_TEXTCHANGEDP] != NULL);
-}
-#endif
-
-/*
- * Return TRUE when there is an InsertCharPre autocommand defined.
- */
-    int
-has_insertcharpre(void)
-{
-    return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
-}
-
-/*
- * Return TRUE when there is an CmdUndefined autocommand defined.
- */
-    int
-has_cmdundefined(void)
-{
-    return (first_autopat[(int)EVENT_CMDUNDEFINED] != NULL);
-}
-
-/*
- * Return TRUE when there is an FuncUndefined autocommand defined.
- */
-    int
-has_funcundefined(void)
-{
-    return (first_autopat[(int)EVENT_FUNCUNDEFINED] != NULL);
-}
-
-#if defined(FEAT_EVAL) || defined(PROTO)
-/*
- * Return TRUE when there is a TextYankPost autocommand defined.
- */
-    int
-has_textyankpost(void)
-{
-    return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
-}
-#endif
-
-/*
- * Execute autocommands for "event" and file name "fname".
- * Return TRUE if some commands were executed.
- */
-    static int
-apply_autocmds_group(
-    event_T    event,
-    char_u     *fname,      /* NULL or empty means use actual file name */
-    char_u     *fname_io,   /* fname to use for <afile> on cmdline, NULL means
-                              use fname */
-    int                force,       /* when TRUE, ignore autocmd_busy */
-    int                group,       /* group ID, or AUGROUP_ALL */
-    buf_T      *buf,        /* buffer for <abuf> */
-    exarg_T    *eap UNUSED) /* command arguments */
-{
-    char_u     *sfname = NULL; /* short file name */
-    char_u     *tail;
-    int                save_changed;
-    buf_T      *old_curbuf;
-    int                retval = FALSE;
-    char_u     *save_sourcing_name;
-    linenr_T   save_sourcing_lnum;
-    char_u     *save_autocmd_fname;
-    int                save_autocmd_fname_full;
-    int                save_autocmd_bufnr;
-    char_u     *save_autocmd_match;
-    int                save_autocmd_busy;
-    int                save_autocmd_nested;
-    static int nesting = 0;
-    AutoPatCmd patcmd;
-    AutoPat    *ap;
-#ifdef FEAT_EVAL
-    sctx_T     save_current_sctx;
-    funccal_entry_T funccal_entry;
-    char_u     *save_cmdarg;
-    long       save_cmdbang;
-#endif
-    static int filechangeshell_busy = FALSE;
-#ifdef FEAT_PROFILE
-    proftime_T wait_time;
-#endif
-    int                did_save_redobuff = FALSE;
-    save_redo_T        save_redo;
-    int                save_KeyTyped = KeyTyped;
-
-    /*
-     * Quickly return if there are no autocommands for this event or
-     * autocommands are blocked.
-     */
-    if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
-           || autocmd_blocked > 0)
-       goto BYPASS_AU;
-
-    /*
-     * When autocommands are busy, new autocommands are only executed when
-     * explicitly enabled with the "nested" flag.
-     */
-    if (autocmd_busy && !(force || autocmd_nested))
-       goto BYPASS_AU;
-
-#ifdef FEAT_EVAL
-    /*
-     * Quickly return when immediately aborting on error, or when an interrupt
-     * occurred or an exception was thrown but not caught.
-     */
-    if (aborting())
-       goto BYPASS_AU;
-#endif
-
-    /*
-     * FileChangedShell never nests, because it can create an endless loop.
-     */
-    if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL
-                                     || event == EVENT_FILECHANGEDSHELLPOST))
-       goto BYPASS_AU;
-
-    /*
-     * Ignore events in 'eventignore'.
-     */
-    if (event_ignored(event))
-       goto BYPASS_AU;
-
-    /*
-     * Allow nesting of autocommands, but restrict the depth, because it's
-     * possible to create an endless loop.
-     */
-    if (nesting == 10)
-    {
-       emsg(_("E218: autocommand nesting too deep"));
-       goto BYPASS_AU;
-    }
-
-    /*
-     * Check if these autocommands are disabled.  Used when doing ":all" or
-     * ":ball".
-     */
-    if (       (autocmd_no_enter
-               && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
-           || (autocmd_no_leave
-               && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
-       goto BYPASS_AU;
-
-    /*
-     * Save the autocmd_* variables and info about the current buffer.
-     */
-    save_autocmd_fname = autocmd_fname;
-    save_autocmd_fname_full = autocmd_fname_full;
-    save_autocmd_bufnr = autocmd_bufnr;
-    save_autocmd_match = autocmd_match;
-    save_autocmd_busy = autocmd_busy;
-    save_autocmd_nested = autocmd_nested;
-    save_changed = curbuf->b_changed;
-    old_curbuf = curbuf;
-
-    /*
-     * Set the file name to be used for <afile>.
-     * Make a copy to avoid that changing a buffer name or directory makes it
-     * invalid.
-     */
-    if (fname_io == NULL)
-    {
-       if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
-                                                  || event == EVENT_OPTIONSET)
-           autocmd_fname = NULL;
-       else if (fname != NULL && !ends_excmd(*fname))
-           autocmd_fname = fname;
-       else if (buf != NULL)
-           autocmd_fname = buf->b_ffname;
-       else
-           autocmd_fname = NULL;
-    }
-    else
-       autocmd_fname = fname_io;
-    if (autocmd_fname != NULL)
-       autocmd_fname = vim_strsave(autocmd_fname);
-    autocmd_fname_full = FALSE; /* call FullName_save() later */
-
-    /*
-     * Set the buffer number to be used for <abuf>.
-     */
-    if (buf == NULL)
-       autocmd_bufnr = 0;
-    else
-       autocmd_bufnr = buf->b_fnum;
-
-    /*
-     * When the file name is NULL or empty, use the file name of buffer "buf".
-     * Always use the full path of the file name to match with, in case
-     * "allow_dirs" is set.
-     */
-    if (fname == NULL || *fname == NUL)
-    {
-       if (buf == NULL)
-           fname = NULL;
-       else
-       {
-#ifdef FEAT_SYN_HL
-           if (event == EVENT_SYNTAX)
-               fname = buf->b_p_syn;
-           else
-#endif
-               if (event == EVENT_FILETYPE)
-                   fname = buf->b_p_ft;
-               else
-               {
-                   if (buf->b_sfname != NULL)
-                       sfname = vim_strsave(buf->b_sfname);
-                   fname = buf->b_ffname;
-               }
-       }
-       if (fname == NULL)
-           fname = (char_u *)"";
-       fname = vim_strsave(fname);     /* make a copy, so we can change it */
-    }
-    else
-    {
-       sfname = vim_strsave(fname);
-       /* Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
-        * ColorScheme, QuickFixCmd* or DirChanged */
-       if (event == EVENT_FILETYPE
-               || event == EVENT_SYNTAX
-               || event == EVENT_CMDLINECHANGED
-               || event == EVENT_CMDLINEENTER
-               || event == EVENT_CMDLINELEAVE
-               || event == EVENT_CMDWINENTER
-               || event == EVENT_CMDWINLEAVE
-               || event == EVENT_CMDUNDEFINED
-               || event == EVENT_FUNCUNDEFINED
-               || event == EVENT_REMOTEREPLY
-               || event == EVENT_SPELLFILEMISSING
-               || event == EVENT_QUICKFIXCMDPRE
-               || event == EVENT_COLORSCHEME
-               || event == EVENT_COLORSCHEMEPRE
-               || event == EVENT_OPTIONSET
-               || event == EVENT_QUICKFIXCMDPOST
-               || event == EVENT_DIRCHANGED)
-       {
-           fname = vim_strsave(fname);
-           autocmd_fname_full = TRUE; /* don't expand it later */
-       }
-       else
-           fname = FullName_save(fname, FALSE);
-    }
-    if (fname == NULL)     /* out of memory */
-    {
-       vim_free(sfname);
-       retval = FALSE;
-       goto BYPASS_AU;
-    }
-
-#ifdef BACKSLASH_IN_FILENAME
-    /*
-     * Replace all backslashes with forward slashes.  This makes the
-     * autocommand patterns portable between Unix and MS-DOS.
-     */
-    if (sfname != NULL)
-       forward_slash(sfname);
-    forward_slash(fname);
-#endif
-
-#ifdef VMS
-    /* remove version for correct match */
-    if (sfname != NULL)
-       vms_remove_version(sfname);
-    vms_remove_version(fname);
-#endif
-
-    /*
-     * Set the name to be used for <amatch>.
-     */
-    autocmd_match = fname;
-
-
-    /* Don't redraw while doing autocommands. */
-    ++RedrawingDisabled;
-    save_sourcing_name = sourcing_name;
-    sourcing_name = NULL;      /* don't free this one */
-    save_sourcing_lnum = sourcing_lnum;
-    sourcing_lnum = 0;         /* no line number here */
-
-#ifdef FEAT_EVAL
-    save_current_sctx = current_sctx;
-
-# ifdef FEAT_PROFILE
-    if (do_profiling == PROF_YES)
-       prof_child_enter(&wait_time); /* doesn't count for the caller itself */
-# endif
-
-    // Don't use local function variables, if called from a function.
-    save_funccal(&funccal_entry);
-#endif
-
-    /*
-     * When starting to execute autocommands, save the search patterns.
-     */
-    if (!autocmd_busy)
-    {
-       save_search_patterns();
-#ifdef FEAT_INS_EXPAND
-       if (!ins_compl_active())
-#endif
-       {
-           saveRedobuff(&save_redo);
-           did_save_redobuff = TRUE;
-       }
-       did_filetype = keep_filetype;
-    }
-
-    /*
-     * Note that we are applying autocmds.  Some commands need to know.
-     */
-    autocmd_busy = TRUE;
-    filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
-    ++nesting;         /* see matching decrement below */
-
-    /* Remember that FileType was triggered.  Used for did_filetype(). */
-    if (event == EVENT_FILETYPE)
-       did_filetype = TRUE;
-
-    tail = gettail(fname);
-
-    /* Find first autocommand that matches */
-    patcmd.curpat = first_autopat[(int)event];
-    patcmd.nextcmd = NULL;
-    patcmd.group = group;
-    patcmd.fname = fname;
-    patcmd.sfname = sfname;
-    patcmd.tail = tail;
-    patcmd.event = event;
-    patcmd.arg_bufnr = autocmd_bufnr;
-    patcmd.next = NULL;
-    auto_next_pat(&patcmd, FALSE);
-
-    /* found one, start executing the autocommands */
-    if (patcmd.curpat != NULL)
-    {
-       /* add to active_apc_list */
-       patcmd.next = active_apc_list;
-       active_apc_list = &patcmd;
-
-#ifdef FEAT_EVAL
-       /* set v:cmdarg (only when there is a matching pattern) */
-       save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
-       if (eap != NULL)
-       {
-           save_cmdarg = set_cmdarg(eap, NULL);
-           set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
-       }
-       else
-           save_cmdarg = NULL; /* avoid gcc warning */
-#endif
-       retval = TRUE;
-       /* mark the last pattern, to avoid an endless loop when more patterns
-        * are added when executing autocommands */
-       for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
-           ap->last = FALSE;
-       ap->last = TRUE;
-       check_lnums(TRUE);      /* make sure cursor and topline are valid */
-       do_cmdline(NULL, getnextac, (void *)&patcmd,
-                                    DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
-#ifdef FEAT_EVAL
-       if (eap != NULL)
-       {
-           (void)set_cmdarg(NULL, save_cmdarg);
-           set_vim_var_nr(VV_CMDBANG, save_cmdbang);
-       }
-#endif
-       /* delete from active_apc_list */
-       if (active_apc_list == &patcmd)     /* just in case */
-           active_apc_list = patcmd.next;
-    }
-
-    --RedrawingDisabled;
-    autocmd_busy = save_autocmd_busy;
-    filechangeshell_busy = FALSE;
-    autocmd_nested = save_autocmd_nested;
-    vim_free(sourcing_name);
-    sourcing_name = save_sourcing_name;
-    sourcing_lnum = save_sourcing_lnum;
-    vim_free(autocmd_fname);
-    autocmd_fname = save_autocmd_fname;
-    autocmd_fname_full = save_autocmd_fname_full;
-    autocmd_bufnr = save_autocmd_bufnr;
-    autocmd_match = save_autocmd_match;
-#ifdef FEAT_EVAL
-    current_sctx = save_current_sctx;
-    restore_funccal();
-# ifdef FEAT_PROFILE
-    if (do_profiling == PROF_YES)
-       prof_child_exit(&wait_time);
-# endif
-#endif
-    KeyTyped = save_KeyTyped;
-    vim_free(fname);
-    vim_free(sfname);
-    --nesting;         /* see matching increment above */
-
-    /*
-     * When stopping to execute autocommands, restore the search patterns and
-     * the redo buffer.  Free any buffers in the au_pending_free_buf list and
-     * free any windows in the au_pending_free_win list.
-     */
-    if (!autocmd_busy)
-    {
-       restore_search_patterns();
-       if (did_save_redobuff)
-           restoreRedobuff(&save_redo);
-       did_filetype = FALSE;
-       while (au_pending_free_buf != NULL)
-       {
-           buf_T *b = au_pending_free_buf->b_next;
-           vim_free(au_pending_free_buf);
-           au_pending_free_buf = b;
-       }
-       while (au_pending_free_win != NULL)
-       {
-           win_T *w = au_pending_free_win->w_next;
-           vim_free(au_pending_free_win);
-           au_pending_free_win = w;
-       }
-    }
-
-    /*
-     * Some events don't set or reset the Changed flag.
-     * Check if still in the same buffer!
-     */
-    if (curbuf == old_curbuf
-           && (event == EVENT_BUFREADPOST
-               || event == EVENT_BUFWRITEPOST
-               || event == EVENT_FILEAPPENDPOST
-               || event == EVENT_VIMLEAVE
-               || event == EVENT_VIMLEAVEPRE))
-    {
-#ifdef FEAT_TITLE
-       if (curbuf->b_changed != save_changed)
-           need_maketitle = TRUE;
-#endif
-       curbuf->b_changed = save_changed;
-    }
-
-    au_cleanup();      /* may really delete removed patterns/commands now */
-
-BYPASS_AU:
-    /* When wiping out a buffer make sure all its buffer-local autocommands
-     * are deleted. */
-    if (event == EVENT_BUFWIPEOUT && buf != NULL)
-       aubuflocal_remove(buf);
-
-    if (retval == OK && event == EVENT_FILETYPE)
-       au_did_filetype = TRUE;
-
-    return retval;
-}
-
-# ifdef FEAT_EVAL
-static char_u  *old_termresponse = NULL;
-# endif
-
-/*
- * Block triggering autocommands until unblock_autocmd() is called.
- * Can be used recursively, so long as it's symmetric.
- */
-    void
-block_autocmds(void)
-{
-# ifdef FEAT_EVAL
-    /* Remember the value of v:termresponse. */
-    if (autocmd_blocked == 0)
-       old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
-# endif
-    ++autocmd_blocked;
-}
-
-    void
-unblock_autocmds(void)
-{
-    --autocmd_blocked;
-
-# ifdef FEAT_EVAL
-    /* When v:termresponse was set while autocommands were blocked, trigger
-     * the autocommands now.  Esp. useful when executing a shell command
-     * during startup (vimdiff). */
-    if (autocmd_blocked == 0
-                     && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
-       apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
-# endif
-}
-
-#if defined(FEAT_EVAL) && (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM)) \
-       || defined(PROTO)
-    int
-is_autocmd_blocked(void)
-{
-    return autocmd_blocked != 0;
-}
-#endif
-
-/*
- * Find next autocommand pattern that matches.
- */
-    static void
-auto_next_pat(
-    AutoPatCmd *apc,
-    int                stop_at_last)       /* stop when 'last' flag is set */
-{
-    AutoPat    *ap;
-    AutoCmd    *cp;
-    char_u     *name;
-    char       *s;
-
-    VIM_CLEAR(sourcing_name);
-
-    for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
-    {
-       apc->curpat = NULL;
-
-       /* Only use a pattern when it has not been removed, has commands and
-        * the group matches. For buffer-local autocommands only check the
-        * buffer number. */
-       if (ap->pat != NULL && ap->cmds != NULL
-               && (apc->group == AUGROUP_ALL || apc->group == ap->group))
-       {
-           /* execution-condition */
-           if (ap->buflocal_nr == 0
-                   ? (match_file_pat(NULL, &ap->reg_prog, apc->fname,
-                                     apc->sfname, apc->tail, ap->allow_dirs))
-                   : ap->buflocal_nr == apc->arg_bufnr)
-           {
-               name = event_nr2name(apc->event);
-               s = _("%s Autocommands for \"%s\"");
-               sourcing_name = alloc((unsigned)(STRLEN(s)
-                                           + STRLEN(name) + ap->patlen + 1));
-               if (sourcing_name != NULL)
-               {
-                   sprintf((char *)sourcing_name, s,
-                                              (char *)name, (char *)ap->pat);
-                   if (p_verbose >= 8)
-                   {
-                       verbose_enter();
-                       smsg(_("Executing %s"), sourcing_name);
-                       verbose_leave();
-                   }
-               }
-
-               apc->curpat = ap;
-               apc->nextcmd = ap->cmds;
-               /* mark last command */
-               for (cp = ap->cmds; cp->next != NULL; cp = cp->next)
-                   cp->last = FALSE;
-               cp->last = TRUE;
-           }
-           line_breakcheck();
-           if (apc->curpat != NULL)        /* found a match */
-               break;
-       }
-       if (stop_at_last && ap->last)
-           break;
-    }
-}
-
-/*
- * Get next autocommand command.
- * Called by do_cmdline() to get the next line for ":if".
- * Returns allocated string, or NULL for end of autocommands.
- */
-    char_u *
-getnextac(int c UNUSED, void *cookie, int indent UNUSED)
-{
-    AutoPatCmd     *acp = (AutoPatCmd *)cookie;
-    char_u         *retval;
-    AutoCmd        *ac;
-
-    /* Can be called again after returning the last line. */
-    if (acp->curpat == NULL)
-       return NULL;
-
-    /* repeat until we find an autocommand to execute */
-    for (;;)
-    {
-       /* skip removed commands */
-       while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL)
-           if (acp->nextcmd->last)
-               acp->nextcmd = NULL;
-           else
-               acp->nextcmd = acp->nextcmd->next;
-
-       if (acp->nextcmd != NULL)
-           break;
-
-       /* at end of commands, find next pattern that matches */
-       if (acp->curpat->last)
-           acp->curpat = NULL;
-       else
-           acp->curpat = acp->curpat->next;
-       if (acp->curpat != NULL)
-           auto_next_pat(acp, TRUE);
-       if (acp->curpat == NULL)
-           return NULL;
-    }
-
-    ac = acp->nextcmd;
-
-    if (p_verbose >= 9)
-    {
-       verbose_enter_scroll();
-       smsg(_("autocommand %s"), ac->cmd);
-       msg_puts("\n");   /* don't overwrite this either */
-       verbose_leave_scroll();
-    }
-    retval = vim_strsave(ac->cmd);
-    autocmd_nested = ac->nested;
-#ifdef FEAT_EVAL
-    current_sctx = ac->script_ctx;
-#endif
-    if (ac->last)
-       acp->nextcmd = NULL;
-    else
-       acp->nextcmd = ac->next;
-    return retval;
-}
-
-/*
- * Return TRUE if there is a matching autocommand for "fname".
- * To account for buffer-local autocommands, function needs to know
- * in which buffer the file will be opened.
- */
-    int
-has_autocmd(event_T event, char_u *sfname, buf_T *buf)
-{
-    AutoPat    *ap;
-    char_u     *fname;
-    char_u     *tail = gettail(sfname);
-    int                retval = FALSE;
-
-    fname = FullName_save(sfname, FALSE);
-    if (fname == NULL)
-       return FALSE;
-
-#ifdef BACKSLASH_IN_FILENAME
-    /*
-     * Replace all backslashes with forward slashes.  This makes the
-     * autocommand patterns portable between Unix and MS-DOS.
-     */
-    sfname = vim_strsave(sfname);
-    if (sfname != NULL)
-       forward_slash(sfname);
-    forward_slash(fname);
-#endif
-
-    for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
-       if (ap->pat != NULL && ap->cmds != NULL
-             && (ap->buflocal_nr == 0
-               ? match_file_pat(NULL, &ap->reg_prog,
-                                         fname, sfname, tail, ap->allow_dirs)
-               : buf != NULL && ap->buflocal_nr == buf->b_fnum
-          ))
-       {
-           retval = TRUE;
-           break;
-       }
-
-    vim_free(fname);
-#ifdef BACKSLASH_IN_FILENAME
-    vim_free(sfname);
-#endif
-
-    return retval;
-}
-
-#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
-/*
- * Function given to ExpandGeneric() to obtain the list of autocommand group
- * names.
- */
-    char_u *
-get_augroup_name(expand_T *xp UNUSED, int idx)
-{
-    if (idx == augroups.ga_len)                /* add "END" add the end */
-       return (char_u *)"END";
-    if (idx >= augroups.ga_len)                /* end of list */
-       return NULL;
-    if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup())
-       /* skip deleted entries */
-       return (char_u *)"";
-    return AUGROUP_NAME(idx);          /* return a name */
-}
-
-static int include_groups = FALSE;
-
-    char_u  *
-set_context_in_autocmd(
-    expand_T   *xp,
-    char_u     *arg,
-    int                doautocmd)      /* TRUE for :doauto*, FALSE for :autocmd */
-{
-    char_u     *p;
-    int                group;
-
-    /* check for a group name, skip it if present */
-    include_groups = FALSE;
-    p = arg;
-    group = au_get_grouparg(&arg);
-    if (group == AUGROUP_ERROR)
-       return NULL;
-    /* If there only is a group name that's what we expand. */
-    if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
-    {
-       arg = p;
-       group = AUGROUP_ALL;
-    }
-
-    /* skip over event name */
-    for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
-       if (*p == ',')
-           arg = p + 1;
-    if (*p == NUL)
-    {
-       if (group == AUGROUP_ALL)
-           include_groups = TRUE;
-       xp->xp_context = EXPAND_EVENTS;     /* expand event name */
-       xp->xp_pattern = arg;
-       return NULL;
-    }
-
-    /* skip over pattern */
-    arg = skipwhite(p);
-    while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
-       arg++;
-    if (*arg)
-       return arg;                         /* expand (next) command */
-
-    if (doautocmd)
-       xp->xp_context = EXPAND_FILES;      /* expand file names */
-    else
-       xp->xp_context = EXPAND_NOTHING;    /* pattern is not expanded */
-    return NULL;
-}
-
-/*
- * Function given to ExpandGeneric() to obtain the list of event names.
- */
-    char_u *
-get_event_name(expand_T *xp UNUSED, int idx)
-{
-    if (idx < augroups.ga_len)         /* First list group names, if wanted */
-    {
-       if (!include_groups || AUGROUP_NAME(idx) == NULL
-                                || AUGROUP_NAME(idx) == get_deleted_augroup())
-           return (char_u *)"";        /* skip deleted entries */
-       return AUGROUP_NAME(idx);       /* return a name */
-    }
-    return (char_u *)event_names[idx - augroups.ga_len].name;
-}
-
-#endif /* FEAT_CMDL_COMPL */
-
-#if defined(FEAT_EVAL) || defined(PROTO)
-/*
- * Return TRUE if autocmd is supported.
- */
-    int
-autocmd_supported(char_u *name)
-{
-    char_u *p;
-
-    return (event_name2nr(name, &p) != NUM_EVENTS);
-}
-
-/*
- * Return TRUE if an autocommand is defined for a group, event and
- * pattern:  The group can be omitted to accept any group. "event" and "pattern"
- * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
- * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
- * Used for:
- *     exists("#Group") or
- *     exists("#Group#Event") or
- *     exists("#Group#Event#pat") or
- *     exists("#Event") or
- *     exists("#Event#pat")
- */
-    int
-au_exists(char_u *arg)
-{
-    char_u     *arg_save;
-    char_u     *pattern = NULL;
-    char_u     *event_name;
-    char_u     *p;
-    event_T    event;
-    AutoPat    *ap;
-    buf_T      *buflocal_buf = NULL;
-    int                group;
-    int                retval = FALSE;
-
-    /* Make a copy so that we can change the '#' chars to a NUL. */
-    arg_save = vim_strsave(arg);
-    if (arg_save == NULL)
-       return FALSE;
-    p = vim_strchr(arg_save, '#');
-    if (p != NULL)
-       *p++ = NUL;
-
-    /* First, look for an autocmd group name */
-    group = au_find_group(arg_save);
-    if (group == AUGROUP_ERROR)
-    {
-       /* Didn't match a group name, assume the first argument is an event. */
-       group = AUGROUP_ALL;
-       event_name = arg_save;
-    }
-    else
-    {
-       if (p == NULL)
-       {
-           /* "Group": group name is present and it's recognized */
-           retval = TRUE;
-           goto theend;
-       }
-
-       /* Must be "Group#Event" or "Group#Event#pat". */
-       event_name = p;
-       p = vim_strchr(event_name, '#');
-       if (p != NULL)
-           *p++ = NUL;     /* "Group#Event#pat" */
-    }
-
-    pattern = p;           /* "pattern" is NULL when there is no pattern */
-
-    /* find the index (enum) for the event name */
-    event = event_name2nr(event_name, &p);
-
-    /* return FALSE if the event name is not recognized */
-    if (event == NUM_EVENTS)
-       goto theend;
-
-    /* Find the first autocommand for this event.
-     * If there isn't any, return FALSE;
-     * If there is one and no pattern given, return TRUE; */
-    ap = first_autopat[(int)event];
-    if (ap == NULL)
-       goto theend;
-
-    /* if pattern is "<buffer>", special handling is needed which uses curbuf */
-    /* for pattern "<buffer=N>, fnamecmp() will work fine */
-    if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
-       buflocal_buf = curbuf;
-
-    /* Check if there is an autocommand with the given pattern. */
-    for ( ; ap != NULL; ap = ap->next)
-       /* only use a pattern when it has not been removed and has commands. */
-       /* For buffer-local autocommands, fnamecmp() works fine. */
-       if (ap->pat != NULL && ap->cmds != NULL
-           && (group == AUGROUP_ALL || ap->group == group)
-           && (pattern == NULL
-               || (buflocal_buf == NULL
-                   ? fnamecmp(ap->pat, pattern) == 0
-                   : ap->buflocal_nr == buflocal_buf->b_fnum)))
-       {
-           retval = TRUE;
-           break;
-       }
-
-theend:
-    vim_free(arg_save);
-    return retval;
-}
-#endif
-
-
-/*
- * Try matching a filename with a "pattern" ("prog" is NULL), or use the
- * precompiled regprog "prog" ("pattern" is NULL).  That avoids calling
- * vim_regcomp() often.
- * Used for autocommands and 'wildignore'.
- * Returns TRUE if there is a match, FALSE otherwise.
- */
-    static int
 match_file_pat(
     char_u     *pattern,               /* pattern to match with */
     regprog_T  **prog,                 /* pre-compiled regprog or NULL */
index 355b4bb349c6b48fc043e39d7658ab6c7f67588b..c6fd710f179c30025a879957f5db3f9de211e3fe 100644 (file)
@@ -397,6 +397,7 @@ EXTERN int  autocmd_no_enter INIT(= FALSE); /* *Enter autocmds disabled */
 EXTERN int     autocmd_no_leave INIT(= FALSE); /* *Leave autocmds disabled */
 EXTERN int     modified_was_set;               /* did ":set modified" */
 EXTERN int     did_filetype INIT(= FALSE);     /* FileType event found */
+EXTERN int     au_did_filetype INIT(= FALSE);
 EXTERN int     keep_filetype INIT(= FALSE);    /* value for did_filetype when
                                                   starting to execute
                                                   autocommands */
index cb4add0029487ffe0322fa20274e4f920dde36ff..e4153ebef22e17aaf6b2c08a4f96b8f2104cb65f 100644 (file)
@@ -62,6 +62,7 @@ extern int _stricoll(char *a, char *b);
 #  include "crypt.pro"
 #  include "crypt_zip.pro"
 # endif
+# include "autocmd.pro"
 # include "buffer.pro"
 # include "charset.pro"
 # ifdef FEAT_CSCOPE
diff --git a/src/proto/autocmd.pro b/src/proto/autocmd.pro
new file mode 100644 (file)
index 0000000..8c6d18f
--- /dev/null
@@ -0,0 +1,39 @@
+/* autocmd.c */
+void aubuflocal_remove(buf_T *buf);
+int au_has_group(char_u *name);
+void do_augroup(char_u *arg, int del_group);
+void free_all_autocmds(void);
+int check_ei(void);
+char_u *au_event_disable(char *what);
+void au_event_restore(char_u *old_ei);
+void do_autocmd(char_u *arg_in, int forceit);
+int do_doautocmd(char_u *arg, int do_msg, int *did_something);
+void ex_doautoall(exarg_T *eap);
+int check_nomodeline(char_u **argp);
+void aucmd_prepbuf(aco_save_T *aco, buf_T *buf);
+void aucmd_restbuf(aco_save_T *aco);
+int apply_autocmds(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf);
+int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap);
+int apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, int *retval);
+int has_cursorhold(void);
+int trigger_cursorhold(void);
+int has_cursormoved(void);
+int has_cursormovedI(void);
+int has_textchanged(void);
+int has_textchangedI(void);
+int has_textchangedP(void);
+int has_insertcharpre(void);
+int has_cmdundefined(void);
+int has_funcundefined(void);
+int has_textyankpost(void);
+void block_autocmds(void);
+void unblock_autocmds(void);
+int is_autocmd_blocked(void);
+char_u *getnextac(int c, void *cookie, int indent);
+int has_autocmd(event_T event, char_u *sfname, buf_T *buf);
+char_u *get_augroup_name(expand_T *xp, int idx);
+char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd);
+char_u *get_event_name(expand_T *xp, int idx);
+int autocmd_supported(char_u *name);
+int au_exists(char_u *arg);
+/* vim: set ft=c : */
index 990348143bdafaab2d323152c17ad313c74248f3..21ac55c9d1454d9c2c6f8a9f20febed948daeb65 100644 (file)
@@ -28,42 +28,7 @@ int delete_recursive(char_u *name);
 void vim_deltempdir(void);
 char_u *vim_tempname(int extra_char, int keep);
 void forward_slash(char_u *fname);
-void aubuflocal_remove(buf_T *buf);
-int au_has_group(char_u *name);
-void do_augroup(char_u *arg, int del_group);
-void free_all_autocmds(void);
-int check_ei(void);
-char_u *au_event_disable(char *what);
-void au_event_restore(char_u *old_ei);
-void do_autocmd(char_u *arg_in, int forceit);
-int do_doautocmd(char_u *arg, int do_msg, int *did_something);
-void ex_doautoall(exarg_T *eap);
-int check_nomodeline(char_u **argp);
-void aucmd_prepbuf(aco_save_T *aco, buf_T *buf);
-void aucmd_restbuf(aco_save_T *aco);
-int apply_autocmds(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf);
-int apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, int *retval);
-int has_cursorhold(void);
-int trigger_cursorhold(void);
-int has_cursormoved(void);
-int has_cursormovedI(void);
-int has_textchanged(void);
-int has_textchangedI(void);
-int has_textchangedP(void);
-int has_insertcharpre(void);
-int has_cmdundefined(void);
-int has_funcundefined(void);
-int has_textyankpost(void);
-void block_autocmds(void);
-void unblock_autocmds(void);
-int is_autocmd_blocked(void);
-char_u *getnextac(int c, void *cookie, int indent);
-int has_autocmd(event_T event, char_u *sfname, buf_T *buf);
-char_u *get_augroup_name(expand_T *xp, int idx);
-char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd);
-char_u *get_event_name(expand_T *xp, int idx);
-int autocmd_supported(char_u *name);
-int au_exists(char_u *arg);
+int match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname, char_u *sfname, char_u *tail, int allow_dirs);
 int match_file_list(char_u *list, char_u *sfname, char_u *ffname);
 char_u *file_pat_to_reg_pat(char_u *pat, char_u *pat_end, char *allow_dirs, int no_bslash);
 long read_eintr(int fd, void *buf, size_t bufsize);
index ddf9b1496c6a5c636fbad39ae02105a56bd54872..af0adc27602ec53546f32f8418bd35ac0b0197f8 100644 (file)
@@ -787,6 +787,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    825,
 /**/
     824,
 /**/