aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/fileio.c')
-rw-r--r--src/nvim/fileio.c2147
1 files changed, 2 insertions, 2145 deletions
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 6ee3a3f579..ab8072ae1d 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -67,85 +67,6 @@
#define UV_FS_COPYFILE_FICLONE 0
#endif
-//
-// 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; // Command to be executed (NULL when
- // command has been removed)
- bool once; // "One shot": removed after execution
- char nested; // If autocommands nest here
- char last; // last command in list
- sctx_T script_ctx; // script context where defined
- 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;
-
-///
-/// 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;
-
-#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 */
#define FIO_UTF8 0x02 /* convert UTF-8 */
@@ -195,14 +116,6 @@ struct bw_info {
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 bool au_did_filetype INIT(= false);
-
void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
{
int msg_scroll_save;
@@ -231,14 +144,6 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
msg_scrolled_ign = FALSE;
}
-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
-};
/*
* Read lines from file "fname" into the buffer after line "from".
@@ -5404,2054 +5309,6 @@ char_u *vim_tempname(void)
}
-/*
- * Code for automatic commands.
- */
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "auevents_name_map.generated.h"
-#endif
-
-static AutoPatCmd *active_apc_list = NULL; /* stack of active autocommands */
-
-/// List of autocmd group names
-static garray_T augroups = { 0, 0, sizeof(char_u *), 10, NULL };
-#define AUGROUP_NAME(i) (((char **)augroups.ga_data)[i])
-
-/*
- * 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 event_T last_event;
-static int last_group;
-static int autocmd_blocked = 0; /* block all autocmds */
-
-// use get_deleted_augroup() to get this
-static const char *deleted_augroup = NULL;
-
-static inline const char *get_deleted_augroup(void)
- FUNC_ATTR_ALWAYS_INLINE
-{
- if (deleted_augroup == NULL) {
- deleted_augroup = _("--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(get_deleted_augroup(), HL_ATTR(HLF_E));
- } else {
- msg_puts_attr(AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
- }
- msg_puts(" ");
- }
- msg_puts_attr(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 */
- continue;
- }
- if (msg_col >= 14) {
- msg_putchar('\n');
- }
- msg_col = 14;
- if (got_int) {
- return;
- }
- msg_outtrans(ac->cmd);
- if (p_verbose > 0) {
- last_set_msg(ac->script_ctx);
- }
- if (got_int) {
- return;
- }
- if (ac->next != NULL) {
- msg_putchar('\n');
- if (got_int) {
- return;
- }
- }
- }
-}
-
-// Mark an autocommand handler for deletion.
-static void au_remove_pat(AutoPat *ap)
-{
- XFREE_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)
-{
- for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
- XFREE_CLEAR(ac->cmd);
- }
- au_need_clean = true;
-}
-
-// Delete one command from an autocmd pattern.
-static void au_del_cmd(AutoCmd *ac)
-{
- XFREE_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;
- 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) {
- bool has_cmd = false;
-
- // Loop over all commands for this pattern.
- AutoCmd **prev_ac = &(ap->cmds);
- for (AutoCmd *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;
- xfree(ac->cmd);
- xfree(ac);
- } else {
- has_cmd = true;
- prev_ac = &(ac->next);
- }
- }
-
- if (ap->pat != NULL && !has_cmd) {
- // Pattern was not marked for deletion, but all of its commands were.
- // So mark the pattern for deletion.
- au_remove_pat(ap);
- }
-
- // 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);
- xfree(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 = 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);
- }
-
- AUGROUP_NAME(i) = xstrdup((char *)name);
- if (i == augroups.ga_len) {
- augroups.ga_len++;
- }
- }
-
- return i;
-}
-
-static void au_del_group(char_u *name)
-{
- int i = au_find_group(name);
- if (i == AUGROUP_ERROR) { // the group doesn't exist
- EMSG2(_("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;
- }
- }
- }
- xfree(AUGROUP_NAME(i));
- if (in_use) {
- AUGROUP_NAME(i) = (char *)get_deleted_augroup();
- } else {
- AUGROUP_NAME(i) = NULL;
- }
- }
-}
-
-/// Find the ID of an autocmd group name.
-///
-/// @param name augroup name
-///
-/// @return the ID or AUGROUP_ERROR (< 0) for error.
-static int au_find_group(const char_u *name)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- for (int 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.
-///
-/// @param name augroup name
-bool au_has_group(const char_u *name)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return au_find_group(name) != AUGROUP_ERROR;
-}
-
-/// ":augroup {name}".
-void do_augroup(char_u *arg, int del_group)
-{
- 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
- int i = au_new_group(arg);
- if (i != AUGROUP_ERROR)
- current_augroup = i;
- } else { // ":aug": list the group names
- msg_start();
- for (int i = 0; i < augroups.ga_len; ++i) {
- if (AUGROUP_NAME(i) != NULL) {
- msg_puts(AUGROUP_NAME(i));
- msg_puts(" ");
- }
- }
- msg_clr_eos();
- msg_end();
- }
-}
-
-#if defined(EXITFREE)
-void free_all_autocmds(void)
-{
- for (current_augroup = -1; current_augroup < augroups.ga_len;
- current_augroup++) {
- do_autocmd((char_u *)"", true);
- }
-
- for (int i = 0; i < augroups.ga_len; i++) {
- char *const s = ((char **)(augroups.ga_data))[i];
- if ((const char *)s != get_deleted_augroup()) {
- xfree(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(const char_u *start, char_u **end)
-{
- const char_u *p;
- int i;
- int len;
-
- // the event name ends with end of line, '|', a blank or a comma
- for (p = start; *p && !ascii_iswhite(*p) && *p != ',' && *p != '|'; p++) {
- }
- for (i = 0; event_names[i].name != NULL; i++) {
- len = (int)event_names[i].len;
- if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) {
- break;
- }
- }
- if (*p == ',') {
- p++;
- }
- *end = (char_u *)p;
- if (event_names[i].name == NULL) {
- return NUM_EVENTS;
- }
- return event_names[i].event;
-}
-
-/// Return the name for event
-///
-/// @param[in] event Event to return name for.
-///
-/// @return Event name, static string. Returns "Unknown" for unknown events.
-static const char *event_nr2name(event_T event)
- FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST
-{
- int i;
-
- for (i = 0; event_names[i].name != NULL; i++) {
- if (event_names[i].event == event) {
- return event_names[i].name;
- }
- }
- return "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] && !ascii_iswhite(arg[1])) {
- EMSG2(_("E215: Illegal character after *: %s"), arg);
- return NULL;
- }
- pat = arg + 1;
- } else {
- for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) {
- if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS) {
- if (have_group)
- EMSG2(_("E216: No such event: %s"), pat);
- else
- EMSG2(_("E216: No such group or event: %s"), pat);
- return NULL;
- }
- }
- }
- return pat;
-}
-
-/// Return true if "event" is included in 'eventignore'.
-///
-/// @param event event to check
-static bool event_ignored(event_T event)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- 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;
-}
-
-/*
- * 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);
- new_ei = vim_strnsave(p_ei, (int)(STRLEN(p_ei) + STRLEN(what)));
- 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);
- xfree(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);
- xfree(old_ei);
- }
-}
-
-// Implements :autocmd.
-// Defines an autocmd (does not execute; cf. apply_autocmds_group).
-//
-// 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;
- int need_free = false;
- int nested = false;
- bool once = 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);
- }
-
- /*
- * 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 && (!ascii_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;
- }
- }
-
- cmd = skipwhite(cmd);
- for (size_t i = 0; i < 2; i++) {
- if (*cmd != NUL) {
- // Check for "++once" flag.
- if (STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) {
- if (once) {
- EMSG2(_(e_duparg2), "++once");
- }
- once = true;
- cmd = skipwhite(cmd + 6);
- }
-
- // Check for "++nested" flag.
- if ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8]))) {
- if (nested) {
- EMSG2(_(e_duparg2), "++nested");
- }
- nested = true;
- cmd = skipwhite(cmd + 8);
- }
-
- // Check for the old (deprecated) "nested" flag.
- if (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) {
- if (nested) {
- EMSG2(_(e_duparg2), "nested");
- }
- 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_T event = (event_T)0; (int)event < (int)NUM_EVENTS;
- event = (event_T)((int)event + 1)) {
- if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group)
- == FAIL) {
- break;
- }
- }
- } else {
- while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
- event_T event = event_name2nr(arg, &arg);
- assert(event < NUM_EVENTS);
- if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group)
- == FAIL) {
- break;
- }
- }
- }
-
- if (need_free)
- xfree(cmd);
- xfree(envpat);
-}
-
-/*
- * Find the group ID in a ":autocmd" or ":doautocmd" argument.
- * The "argp" argument is advanced to the following argument.
- *
- * Returns the group ID or AUGROUP_ALL.
- */
-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 && !ascii_iswhite(*p) && *p != '|'; p++) {
- }
- if (p > arg) {
- group_name = vim_strnsave(arg, (int)(p - arg));
- 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 */
- xfree(group_name);
- }
- return group;
-}
-
-// do_autocmd() for one event.
-// Defines an autocmd (does not execute; cf. apply_autocmds_group).
-//
-// 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, bool once, 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\}".
- */
- endpat = pat;
- // ignore single comma
- if (*endpat == ',') {
- continue;
- }
- brace_level = 0;
- for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\');
- ++endpat) {
- if (*endpat == '{')
- brace_level++;
- else if (*endpat == '}')
- brace_level--;
- }
- 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)) {
- emsgf(_("E680: <buffer=%d>: invalid buffer number "),
- buflocal_nr);
- return FAIL;
- }
-
- ap = xmalloc(sizeof(AutoPat));
- ap->pat = vim_strnsave(pat, patlen);
- ap->patlen = patlen;
-
- 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);
- xfree(reg_pat);
- if (reg_pat == NULL || ap->reg_prog == NULL) {
- xfree(ap->pat);
- xfree(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 = xmalloc(sizeof(AutoCmd));
- ac->cmd = vim_strsave(cmd);
- ac->script_ctx = current_sctx;
- ac->script_ctx.sc_lnum += sourcing_lnum;
- ac->next = NULL;
- *prev_ac = ac;
- ac->once = once;
- 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?
- bool *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 == '*') {
- 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) && !ascii_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;
- }
-
- return aborting() ? FAIL : OK;
-}
-
-/*
- * ":doautoall": execute autocommands for each loaded buffer.
- */
-void ex_doautoall(exarg_T *eap)
-{
- int retval;
- aco_save_T aco;
- char_u *arg = eap->arg;
- int call_do_modelines = check_nomodeline(&arg);
- bufref_T bufref;
-
- /*
- * 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) {
- continue;
- }
- // Find a window for this buffer and save some values.
- aucmd_prepbuf(&aco, buf);
- set_bufref(&bufref, buf);
-
- bool did_aucmd;
- // 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 do_modelines() should be
-/// called when true is returned.
-///
-/// @param[in,out] argp argument string
-bool check_nomodeline(char_u **argp)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (STRNCMP(*argp, "<nomodeline>", 12) == 0) {
- *argp = skipwhite(*argp + 12);
- return false;
- }
- return true;
-}
-
-/// Prepare for executing autocommands for (hidden) buffer `buf`.
-/// If the current buffer is not in any visible window, put it in a temporary
-/// floating window `aucmd_win`.
-/// Set `curbuf` and `curwin` to match `buf`.
-///
-/// @param aco structure to save values in
-/// @param buf new curbuf
-void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
-{
- win_T *win;
- bool need_append = true; // Append `aucmd_win` to the window list.
-
- /* Find a window that is for the new buffer */
- if (buf == curbuf) { /* be quick when buf is curbuf */
- win = curwin;
- } else {
- win = NULL;
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf) {
- win = wp;
- break;
- }
- }
- }
-
- // Allocate the `aucmd_win` dummy floating window.
- if (win == NULL && aucmd_win == NULL) {
- win_alloc_aucmd_win();
- need_append = false;
- }
- 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_prevwin = prevwin;
- aco->save_curbuf = curbuf;
- 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;
- aucmd_win->w_s = &buf->b_s;
- ++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(). */
- XFREE_CLEAR(aucmd_win->w_localdir);
- aco->globaldir = globaldir;
- globaldir = NULL;
-
- block_autocmds(); // We don't want BufEnter/WinEnter autocommands.
- if (need_append) {
- win_append(lastwin, aucmd_win);
- handle_register_window(aucmd_win);
- win_config_float(aucmd_win, aucmd_win->w_float_config);
- }
- // Prevent chdir() call in win_enter_ext(), through do_autochdir()
- int save_acd = p_acd;
- p_acd = false;
- win_enter(aucmd_win, false);
- p_acd = save_acd;
- 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).
-///
-/// @param aco structure holding saved values
-void aucmd_restbuf(aco_save_T *aco)
-{
- 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) {
- 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:
-
- win_remove(curwin, NULL);
- handle_unregister_window(curwin);
- if (curwin->w_grid.chars != NULL) {
- ui_comp_remove_grid(&curwin->w_grid);
- ui_call_win_hide(curwin->w_grid.handle);
- grid_free(&curwin->w_grid);
- }
-
- 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);
- }
-
- unblock_autocmds();
-
- if (win_valid(aco->save_curwin)) {
- curwin = aco->save_curwin;
- } else {
- // Hmm, original window disappeared. Just use the first one.
- curwin = firstwin;
- }
- prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin
- : firstwin; // window disappeared?
- vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
- hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
- curbuf = curwin->w_buffer;
-
- xfree(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;
- curwin->w_topfill = 0;
- }
- } 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 (curwin->w_s == &curbuf->b_s) {
- curwin->w_s = &aco->new_curbuf.br_buf->b_s;
- }
- curbuf->b_nwindows--;
- curbuf = aco->new_curbuf.br_buf;
- curwin->w_buffer = curbuf;
- curbuf->b_nwindows++;
- }
-
- curwin = aco->save_curwin;
- prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin
- : firstwin; // window disappeared?
- curbuf = curwin->w_buffer;
- // In case the autocommand moves the cursor to a position that does not
- // exist in curbuf
- check_cursor();
- }
- }
-}
-
-static int autocmd_nested = FALSE;
-
-/// Execute autocommands for "event" and file name "fname".
-///
-/// @param event event that occurred
-/// @param fname filename, NULL or empty means use actual file name
-/// @param fname_io filename to use for <afile> on cmdline
-/// @param force When true, ignore autocmd_busy
-/// @param buf Buffer for <abuf>
-///
-/// @return true if some commands were executed.
-bool apply_autocmds(event_T event, char_u *fname, char_u *fname_io, bool force,
- buf_T *buf)
-{
- 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.
-///
-/// @param event event that occurred
-/// @param fname NULL or empty means use actual file name
-/// @param fname_io fname to use for <afile> on cmdline
-/// @param force When true, ignore autocmd_busy
-/// @param buf Buffer for <abuf>
-/// @param exarg Ex command arguments
-///
-/// @return true if some commands were executed.
-static bool apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io,
- bool 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.
-///
-/// @param event event that occurred
-/// @param fname NULL or empty means use actual file name
-/// @param fname_io fname to use for <afile> on cmdline
-/// @param force When true, ignore autocmd_busy
-/// @param buf Buffer for <abuf>
-/// @param[in,out] retval caller's retval
-///
-/// @return true if some autocommands were executed
-bool apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io,
- bool force, buf_T *buf, int *retval)
-{
- if (should_abort(*retval)) {
- return false;
- }
-
- bool did_cmd = apply_autocmds_group(event, fname, fname_io, force,
- AUGROUP_ALL, buf, NULL);
- if (did_cmd && aborting()) {
- *retval = FAIL;
- }
- return did_cmd;
-}
-
-/// Return true when there is a CursorHold/CursorHoldI autocommand defined for
-/// the current mode.
-bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return first_autopat[(int)(get_real_state() == NORMAL_BUSY
- ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL;
-}
-
-/// Return true if the CursorHold/CursorHoldI event can be triggered.
-bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- int state;
-
- if (!did_cursorhold
- && has_cursorhold()
- && reg_recording == 0
- && typebuf.tb_len == 0
- && !ins_compl_active()
- ) {
- state = get_real_state();
- if (state == NORMAL_BUSY || (state & INSERT) != 0) {
- return true;
- }
- }
- return false;
-}
-
-/// Return true if "event" autocommand is defined.
-///
-/// @param event the autocommand to check
-bool has_event(event_T event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return first_autopat[event] != NULL;
-}
-
-/// Execute autocommands for "event" and file name "fname".
-///
-/// @param event event that occurred
-/// @param fname filename, NULL or empty means use actual file name
-/// @param fname_io filename to use for <afile> on cmdline,
-/// NULL means use `fname`.
-/// @param force When true, ignore autocmd_busy
-/// @param group autocmd group ID or AUGROUP_ALL
-/// @param buf Buffer for <abuf>
-/// @param eap Ex command arguments
-///
-/// @return true if some commands were executed.
-static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
- bool force, int group, buf_T *buf,
- exarg_T *eap)
-{
- char_u *sfname = NULL; /* short file name */
- char_u *tail;
- bool save_changed;
- buf_T *old_curbuf;
- bool retval = false;
- char_u *save_sourcing_name;
- linenr_T save_sourcing_lnum;
- char_u *save_autocmd_fname;
- 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;
- char_u *save_cmdarg;
- long save_cmdbang;
- static int filechangeshell_busy = FALSE;
- proftime_T wait_time;
- bool did_save_redobuff = false;
- save_redo_T save_redo;
- const bool 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;
-
- /*
- * 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;
-
- /*
- * 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_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) {
- // Allocate MAXPATHL for when eval_vars() resolves the fullpath.
- autocmd_fname = vim_strnsave(autocmd_fname, MAXPATHL);
- }
-
- /*
- * 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 {
- if (event == EVENT_SYNTAX)
- fname = buf->b_p_syn;
- else 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 the following events.
- if (event == EVENT_CMDLINECHANGED
- || event == EVENT_CMDLINEENTER
- || event == EVENT_CMDLINELEAVE
- || event == EVENT_CMDWINENTER
- || event == EVENT_CMDWINLEAVE
- || event == EVENT_CMDUNDEFINED
- || event == EVENT_COLORSCHEME
- || event == EVENT_COLORSCHEMEPRE
- || event == EVENT_DIRCHANGED
- || event == EVENT_FILETYPE
- || event == EVENT_FUNCUNDEFINED
- || event == EVENT_OPTIONSET
- || event == EVENT_QUICKFIXCMDPOST
- || event == EVENT_QUICKFIXCMDPRE
- || event == EVENT_REMOTEREPLY
- || event == EVENT_SPELLFILEMISSING
- || event == EVENT_SYNTAX
- || event == EVENT_SIGNAL
- || event == EVENT_TABCLOSED
- || event == EVENT_WINCLOSED) {
- fname = vim_strsave(fname);
- } else {
- fname = (char_u *)FullName_save((char *)fname, false);
- }
- }
- if (fname == NULL) { /* out of memory */
- xfree(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 Windows.
- if (sfname != NULL) {
- forward_slash(sfname);
- }
- forward_slash(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 */
-
- const sctx_T save_current_sctx = current_sctx;
-
- if (do_profiling == PROF_YES)
- prof_child_enter(&wait_time); /* doesn't count for the caller itself */
-
- // Don't use local function variables, if called from a function.
- funccal_entry_T funccal_entry;
- save_funccal(&funccal_entry);
-
- /*
- * When starting to execute autocommands, save the search patterns.
- */
- if (!autocmd_busy) {
- save_search_patterns();
- if (!ins_compl_active()) {
- 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 = path_tail(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;
-
- // 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
- }
- 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
-
- // Execute the autocmd. The `getnextac` callback handles iteration.
- do_cmdline(NULL, getnextac, (void *)&patcmd,
- DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
-
- reset_lnums(); // restore cursor and topline, unless they were changed
-
- if (eap != NULL) {
- (void)set_cmdarg(NULL, save_cmdarg);
- set_vim_var_nr(VV_CMDBANG, save_cmdbang);
- }
- /* 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;
- xfree(sourcing_name);
- sourcing_name = save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
- xfree(autocmd_fname);
- autocmd_fname = save_autocmd_fname;
- autocmd_bufnr = save_autocmd_bufnr;
- autocmd_match = save_autocmd_match;
- current_sctx = save_current_sctx;
- restore_funccal();
- if (do_profiling == PROF_YES) {
- prof_child_exit(&wait_time);
- }
- KeyTyped = save_KeyTyped;
- xfree(fname);
- xfree(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;
- xfree(au_pending_free_buf);
- au_pending_free_buf = b;
- }
- while (au_pending_free_win != NULL) {
- win_T *w = au_pending_free_win->w_next;
- xfree(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)) {
- if (curbuf->b_changed != save_changed)
- need_maketitle = TRUE;
- 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;
-}
-
-static char_u *old_termresponse = NULL;
-
-/*
- * Block triggering autocommands until unblock_autocmd() is called.
- * Can be used recursively, so long as it's symmetric.
- */
-void block_autocmds(void)
-{
- /* Remember the value of v:termresponse. */
- if (autocmd_blocked == 0)
- old_termresponse = get_vim_var_str(VV_TERMRESPONSE);
- ++autocmd_blocked;
-}
-
-void unblock_autocmds(void)
-{
- --autocmd_blocked;
-
- /* When v:termresponse was set while autocommands were blocked, trigger
- * the autocommands now. Esp. useful when executing a shell command
- * during startup (nvim -d). */
- if (autocmd_blocked == 0
- && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse)
- apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf);
-}
-
-// 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 *s;
-
- XFREE_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) {
- const char *const name = event_nr2name(apc->event);
- s = _("%s Autocommands for \"%s\"");
- const size_t sourcing_name_len = (STRLEN(s) + strlen(name) + ap->patlen
- + 1);
- sourcing_name = xmalloc(sourcing_name_len);
- snprintf((char *)sourcing_name, sourcing_name_len, s, 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".
-/// @return allocated string, or NULL for end of autocommands.
-char_u *getnextac(int c, void *cookie, int indent, bool do_concat)
-{
- 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);
- // Remove one-shot ("once") autocmd in anticipation of its execution.
- if (ac->once) {
- au_del_cmd(ac);
- }
- autocmd_nested = ac->nested;
- current_sctx = ac->script_ctx;
- 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.
-///
-/// @param event event that occurred.
-/// @param sfname filename the event occurred in.
-/// @param buf buffer the file is open in
-bool has_autocmd(event_T event, char_u *sfname, buf_T *buf)
- FUNC_ATTR_WARN_UNUSED_RESULT
-{
- AutoPat *ap;
- char_u *fname;
- char_u *tail = path_tail(sfname);
- bool retval = false;
-
- fname = (char_u *)FullName_save((char *)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 Windows.
- sfname = vim_strsave(sfname);
- 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;
- }
- }
-
- xfree(fname);
-#ifdef BACKSLASH_IN_FILENAME
- xfree(sfname);
-#endif
-
- return retval;
-}
-
-/*
- * Function given to ExpandGeneric() to obtain the list of autocommand group
- * names.
- */
-char_u *get_augroup_name(expand_T *xp, 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 (char_u *)AUGROUP_NAME(idx);
-}
-
-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 there only is a group name that's what we expand. */
- if (*arg == NUL && group != AUGROUP_ALL && !ascii_iswhite(arg[-1])) {
- arg = p;
- group = AUGROUP_ALL;
- }
-
- /* skip over event name */
- for (p = arg; *p != NUL && !ascii_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 && (!ascii_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, 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 (char_u *)AUGROUP_NAME(idx);
- }
- return (char_u *)event_names[idx - augroups.ga_len].name;
-}
-
-
-/// Check whether given autocommand is supported
-///
-/// @param[in] event Event to check.
-///
-/// @return True if it is, false otherwise.
-bool autocmd_supported(const char *const event)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- char_u *p;
- return event_name2nr((const char_u *)event, &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 omitted to accept any event and 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")
-///
-/// @param arg autocommand string
-bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
-{
- event_T event;
- AutoPat *ap;
- buf_T *buflocal_buf = NULL;
- int group;
- bool retval = false;
-
- // Make a copy so that we can change the '#' chars to a NUL.
- char *const arg_save = xstrdup(arg);
- char *p = strchr(arg_save, '#');
- if (p != NULL) {
- *p++ = NUL;
- }
-
- // First, look for an autocmd group name.
- group = au_find_group((char_u *)arg_save);
- char *event_name;
- 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 = strchr(event_name, '#');
- if (p != NULL) {
- *p++ = NUL; // "Group#Event#pat"
- }
- }
-
- char *pattern = p; // "pattern" is NULL when there is no pattern.
-
- // Find the index (enum) for the event name.
- event = event_name2nr((char_u *)event_name, (char_u **)&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, (char_u *)pattern) == 0
- : ap->buflocal_nr == buflocal_buf->b_fnum))) {
- retval = true;
- break;
- }
-
-theend:
- xfree(arg_save);
- return retval;
-}
/// Tries matching a filename with a "pattern" ("prog" is NULL), or use the
/// precompiled regprog "prog" ("pattern" is NULL). That avoids calling
@@ -7467,8 +5324,8 @@ theend:
/// @param allow_dirs Allow matching with dir
///
/// @return true if there is a match, false otherwise
-static bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname,
- char_u *sfname, char_u *tail, int allow_dirs)
+bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname,
+ char_u *sfname, char_u *tail, int allow_dirs)
{
regmatch_T regmatch;
bool result = false;