summaryrefslogtreecommitdiffstats
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)
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 d3fcba6356..1eb9aa2cb0 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 9760d8f7b2..d0d31d4eb9 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 7aef61ac33..8d8666ffbe 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 2daa8d72bf..83614ca911 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 784cab9312..0459984dab 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 04560d4034..b71b923ded 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 70ab5377a3..ae490416f4 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 a3c66819a5..3f8c8a0384 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 a16908e9c1..c621360eab 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 18c68142d8..6a3508980f 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 23f01c38bc..2c409f0a62 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 6e7094cec0..e1dc36f69c 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 0000000000..55650b46f7
--- /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;
+