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