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.c617
1 files changed, 398 insertions, 219 deletions
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index db1469db97..d433afab3e 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -32,7 +32,6 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
-#include "nvim/misc2.h"
#include "nvim/garray.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -44,6 +43,7 @@
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sha256.h"
+#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
#include "nvim/types.h"
@@ -51,6 +51,7 @@
#include "nvim/window.h"
#include "nvim/shada.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/time.h"
#include "nvim/os/input.h"
@@ -187,6 +188,14 @@ 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;
@@ -240,7 +249,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
* READ_DUMMY read into a dummy buffer (to check if file contents changed)
* READ_KEEP_UNDO don't clear undo info or read it from a file
*
- * return FAIL for failure, OK otherwise
+ * return FAIL for failure, NOTDONE for directory (failure), or OK
*/
int
readfile (
@@ -249,7 +258,7 @@ readfile (
linenr_T from,
linenr_T lines_to_skip,
linenr_T lines_to_read,
- exarg_T *eap, /* can be NULL! */
+ exarg_T *eap, // can be NULL!
int flags
)
{
@@ -272,7 +281,7 @@ readfile (
int wasempty; /* buffer was empty before reading */
colnr_T len;
long size = 0;
- char_u *p;
+ char_u *p = NULL;
off_t filesize = 0;
int skip_read = FALSE;
context_sha256_T sha_ctx;
@@ -282,11 +291,9 @@ readfile (
int error = FALSE; /* errors encountered */
int ff_error = EOL_UNKNOWN; /* file format with errors */
long linerest = 0; /* remaining chars in line */
-#ifdef UNIX
int perm = 0;
+#ifdef UNIX
int swap_mode = -1; /* protection bits for swap file */
-#else
- int perm;
#endif
int fileformat = 0; /* end-of-line format */
int keep_fileformat = FALSE;
@@ -331,6 +338,8 @@ readfile (
int using_b_ffname;
int using_b_fname;
+ au_did_filetype = false; // reset before triggering any autocommands
+
curbuf->b_no_eol_lnum = 0; /* in case it was set by the previous read */
/*
@@ -418,32 +427,31 @@ readfile (
}
}
- if (!read_stdin && !read_buffer) {
-#ifdef UNIX
- /*
- * On Unix it is possible to read a directory, so we have to
- * check for it before os_open().
- */
+ if (!read_buffer && !read_stdin) {
perm = os_getperm(fname);
- if (perm >= 0 && !S_ISREG(perm) /* not a regular file ... */
+#ifdef UNIX
+ // On Unix it is possible to read a directory, so we have to
+ // check for it before os_open().
+ if (perm >= 0 && !S_ISREG(perm) // not a regular file ...
# ifdef S_ISFIFO
- && !S_ISFIFO(perm) /* ... or fifo */
+ && !S_ISFIFO(perm) // ... or fifo
# endif
# ifdef S_ISSOCK
- && !S_ISSOCK(perm) /* ... or socket */
+ && !S_ISSOCK(perm) // ... or socket
# endif
# ifdef OPEN_CHR_FILES
&& !(S_ISCHR(perm) && is_dev_fd_file(fname))
- /* ... or a character special file named /dev/fd/<n> */
+ // ... or a character special file named /dev/fd/<n>
# endif
) {
- if (S_ISDIR(perm))
+ if (S_ISDIR(perm)) {
filemess(curbuf, fname, (char_u *)_("is a directory"), 0);
- else
+ } else {
filemess(curbuf, fname, (char_u *)_("is not a file"), 0);
+ }
msg_end();
msg_scroll = msg_save;
- return FAIL;
+ return S_ISDIR(perm) ? NOTDONE : FAIL;
}
#endif
}
@@ -493,44 +501,31 @@ readfile (
curbuf->b_flags &= ~(BF_NEW | BF_NEW_W);
}
- /*
- * Check readonly by trying to open the file for writing.
- * If this fails, we know that the file is readonly.
- */
- file_readonly = FALSE;
+ // Check readonly.
+ file_readonly = false;
if (!read_buffer && !read_stdin) {
- if (!newfile || readonlymode) {
- file_readonly = TRUE;
- } else if ((fd = os_open((char *)fname, O_RDWR, 0)) < 0) {
- // opening in readwrite mode failed => file is readonly
- file_readonly = TRUE;
- }
- if (file_readonly == TRUE) {
- // try to open readonly
- fd = os_open((char *)fname, O_RDONLY, 0);
+ if (!newfile || readonlymode || !(perm & 0222)
+ || !os_file_is_writable((char *)fname)) {
+ file_readonly = true;
}
+ fd = os_open((char *)fname, O_RDONLY, 0);
}
- if (fd < 0) { /* cannot open at all */
+ if (fd < 0) { // cannot open at all
msg_scroll = msg_save;
#ifndef UNIX
- /*
- * On non-unix systems we can't open a directory, check here.
- */
- perm = os_getperm(fname); /* check if the file exists */
+ // On non-unix systems we can't open a directory, check here.
if (os_isdir(fname)) {
filemess(curbuf, sfname, (char_u *)_("is a directory"), 0);
- curbuf->b_p_ro = TRUE; /* must use "w!" now */
- } else
+ curbuf->b_p_ro = true; // must use "w!" now
+ } else {
#endif
if (!newfile) {
return FAIL;
}
- if (perm == UV_ENOENT) {
- /*
- * Set the 'new-file' flag, so that when the file has
- * been created by someone else, a ":w" will complain.
- */
+ if (perm == UV_ENOENT) { // check if the file exists
+ // Set the 'new-file' flag, so that when the file has
+ // been created by someone else, a ":w" will complain.
curbuf->b_flags |= BF_NEW;
/* Create a swap file now, so that other Vims are warned
@@ -581,6 +576,9 @@ readfile (
return FAIL;
}
+#ifndef UNIX
+ }
+#endif
/*
* Only set the 'ro' flag for readonly files the first time they are
@@ -1749,8 +1747,9 @@ failed:
#ifdef HAVE_FD_CLOEXEC
else {
int fdflags = fcntl(fd, F_GETFD);
- if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
- fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC);
+ if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) {
+ (void)fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC);
+ }
}
#endif
xfree(buffer);
@@ -1883,17 +1882,22 @@ failed:
xfree(keep_msg);
keep_msg = NULL;
+ p = NULL;
msg_scrolled_ign = TRUE;
- p = msg_trunc_attr(IObuff, FALSE, 0);
+
+ if (!read_stdin && !read_buffer) {
+ p = msg_trunc_attr(IObuff, FALSE, 0);
+ }
+
if (read_stdin || read_buffer || restart_edit != 0
- || (msg_scrolled != 0 && !need_wait_return))
- /* Need to repeat the message after redrawing when:
- * - When reading from stdin (the screen will be cleared next).
- * - When restart_edit is set (otherwise there will be a delay
- * before redrawing).
- * - When the screen was scrolled but there is no wait-return
- * prompt. */
+ || (msg_scrolled != 0 && !need_wait_return)) {
+ // Need to repeat the message after redrawing when:
+ // - When reading from stdin (the screen will be cleared next).
+ // - When restart_edit is set (otherwise there will be a delay before
+ // redrawing).
+ // - When the screen was scrolled but there is no wait-return prompt.
set_keep_msg(p, 0);
+ }
msg_scrolled_ign = FALSE;
}
@@ -1970,20 +1974,29 @@ failed:
* should not be overwritten: Set msg_scroll, restore its value if no
* output was done.
*/
- msg_scroll = TRUE;
- if (filtering)
+ msg_scroll = true;
+ if (filtering) {
apply_autocmds_exarg(EVENT_FILTERREADPOST, NULL, sfname,
- FALSE, curbuf, eap);
- else if (newfile)
+ false, curbuf, eap);
+ } else if (newfile) {
apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname,
- FALSE, curbuf, eap);
- else
+ false, curbuf, eap);
+ if (!au_did_filetype && *curbuf->b_p_ft != NUL) {
+ // EVENT_FILETYPE was not triggered but the buffer already has a
+ // filetype. Trigger EVENT_FILETYPE using the existing filetype.
+ apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname,
+ true, curbuf);
+ }
+ } else {
apply_autocmds_exarg(EVENT_FILEREADPOST, sfname, sfname,
- FALSE, NULL, eap);
- if (msg_scrolled == n)
+ false, NULL, eap);
+ }
+ if (msg_scrolled == n) {
msg_scroll = m;
- if (aborting()) /* autocmds may abort script processing */
+ }
+ if (aborting()) { // autocmds may abort script processing
return FAIL;
+ }
}
if (recoverymode && error)
@@ -2989,14 +3002,15 @@ nobackup:
* delete an existing one, try to use another name.
* Change one character, just before the extension.
*/
- if (!p_bk && os_file_exists(backup)) {
+ if (!p_bk && os_path_exists(backup)) {
p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
if (p < backup) /* empty file name ??? */
p = backup;
*p = 'z';
- while (*p > 'a' && os_file_exists(backup))
- --*p;
- /* They all exist??? Must be something wrong! */
+ while (*p > 'a' && os_path_exists(backup)) {
+ (*p)--;
+ }
+ // They all exist??? Must be something wrong!
if (*p == 'a') {
xfree(backup);
backup = NULL;
@@ -3223,12 +3237,12 @@ restore_backup:
* This may not work if the vim_rename() fails.
* In that case we leave the copy around.
*/
- /* If file does not exist, put the copy in its place */
- if (!os_file_exists(fname)) {
+ // If file does not exist, put the copy in its place
+ if (!os_path_exists(fname)) {
vim_rename(backup, fname);
}
- /* if original file does exist throw away the copy */
- if (os_file_exists(fname)) {
+ // if original file does exist throw away the copy
+ if (os_path_exists(fname)) {
os_remove((char *)backup);
}
} else {
@@ -3237,8 +3251,8 @@ restore_backup:
}
}
- /* if original file no longer exists give an extra warning */
- if (!newfile && !os_file_exists(fname)) {
+ // if original file no longer exists give an extra warning
+ if (!newfile && !os_path_exists(fname)) {
end = 0;
}
}
@@ -3596,9 +3610,9 @@ restore_backup:
* If the original file does not exist yet
* the current backup file becomes the original file
*/
- if (org == NULL)
+ if (org == NULL) {
EMSG(_("E205: Patchmode: can't save original file"));
- else if (!os_file_exists((char_u *)org)) {
+ } else if (!os_path_exists((char_u *)org)) {
vim_rename(backup, (char_u *)org);
xfree(backup); /* don't delete the file */
backup = NULL;
@@ -3785,8 +3799,9 @@ static int set_rw_fname(char_u *fname, char_u *sfname)
/* Do filetype detection now if 'filetype' is empty. */
if (*curbuf->b_p_ft == NUL) {
- if (au_has_group((char_u *)"filetypedetect"))
- (void)do_doautocmd((char_u *)"filetypedetect BufRead", FALSE);
+ if (au_has_group((char_u *)"filetypedetect")) {
+ (void)do_doautocmd((char_u *)"filetypedetect BufRead", false, NULL);
+ }
do_modelines(0);
}
@@ -4176,9 +4191,8 @@ static bool need_conversion(const char_u *fenc)
same_encoding = (enc_flags != 0 && fenc_flags == enc_flags);
}
if (same_encoding) {
- /* Specified encoding matches with 'encoding'. This requires
- * conversion when 'encoding' is Unicode but not UTF-8. */
- return enc_unicode != 0;
+ // Specified file encoding matches UTF-8.
+ return false;
}
/* Encodings differ. However, conversion is not needed when 'enc' is any
@@ -4458,11 +4472,95 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
return eof == NULL;
}
-/*
- * os_rename() only works if both files are on the same file system, this
- * function will (attempts to?) copy the file across if rename fails -- webb
- * Return -1 for failure, 0 for success.
- */
+/// Read 2 bytes from "fd" and turn them into an int, MSB first.
+int get2c(FILE *fd)
+{
+ int n;
+
+ n = getc(fd);
+ n = (n << 8) + getc(fd);
+ return n;
+}
+
+/// Read 3 bytes from "fd" and turn them into an int, MSB first.
+int get3c(FILE *fd)
+{
+ int n;
+
+ n = getc(fd);
+ n = (n << 8) + getc(fd);
+ n = (n << 8) + getc(fd);
+ return n;
+}
+
+/// Read 4 bytes from "fd" and turn them into an int, MSB first.
+int get4c(FILE *fd)
+{
+ // Use unsigned rather than int otherwise result is undefined
+ // when left-shift sets the MSB.
+ unsigned n;
+
+ n = (unsigned)getc(fd);
+ n = (n << 8) + (unsigned)getc(fd);
+ n = (n << 8) + (unsigned)getc(fd);
+ n = (n << 8) + (unsigned)getc(fd);
+ return (int)n;
+}
+
+/// Read 8 bytes from `fd` and turn them into a time_t, MSB first.
+time_t get8ctime(FILE *fd)
+{
+ time_t n = 0;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ n = (n << 8) + getc(fd);
+ }
+ return n;
+}
+
+/// Reads a string of length "cnt" from "fd" into allocated memory.
+/// @return pointer to the string or NULL when unable to read that many bytes.
+char *read_string(FILE *fd, size_t cnt)
+{
+ char *str = xmallocz(cnt);
+ for (size_t i = 0; i < cnt; i++) {
+ int c = getc(fd);
+ if (c == EOF) {
+ xfree(str);
+ return NULL;
+ }
+ str[i] = (char)c;
+ }
+ return str;
+}
+
+/// Writes a number to file "fd", most significant bit first, in "len" bytes.
+/// @returns false in case of an error.
+bool put_bytes(FILE *fd, uintmax_t number, size_t len)
+{
+ assert(len > 0);
+ for (size_t i = len - 1; i < len; i--) {
+ if (putc((int)(number >> (i * 8)), fd) == EOF) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/// Writes time_t to file "fd" in 8 bytes.
+/// @returns FAIL when the write failed.
+int put_time(FILE *fd, time_t time_)
+{
+ uint8_t buf[8];
+ time_to_bytes(time_, buf);
+ return fwrite(buf, sizeof(uint8_t), ARRAY_SIZE(buf), fd) == 1 ? OK : FAIL;
+}
+
+/// os_rename() only works if both files are on the same file system, this
+/// function will (attempts to?) copy the file across if rename fails -- webb
+//
+/// @return -1 for failure, 0 for success
int vim_rename(char_u *from, char_u *to)
{
int fd_in;
@@ -4513,9 +4611,11 @@ int vim_rename(char_u *from, char_u *to)
if (STRLEN(from) >= MAXPATHL - 5)
return -1;
STRCPY(tempname, from);
- for (n = 123; n < 99999; ++n) {
- sprintf((char *)path_tail(tempname), "%d", n);
- if (!os_file_exists(tempname)) {
+ for (n = 123; n < 99999; n++) {
+ char * tail = (char *)path_tail(tempname);
+ snprintf(tail, (MAXPATHL + 1) - (tail - (char *)tempname - 1), "%d", n);
+
+ if (!os_path_exists(tempname)) {
if (os_rename(from, tempname) == OK) {
if (os_rename(tempname, to) == OK)
return 0;
@@ -4862,7 +4962,7 @@ buf_check_timestamp (
}
} else if ((buf->b_flags & BF_NEW) && !(buf->b_flags & BF_NEW_W)
- && os_file_exists(buf->b_ffname)) {
+ && os_path_exists(buf->b_ffname)) {
retval = 1;
mesg = _("W13: Warning: File \"%s\" has been created after editing started");
buf->b_flags |= BF_NEW_W;
@@ -4882,8 +4982,8 @@ buf_check_timestamp (
set_vim_var_string(VV_WARNINGMSG, tbuf, -1);
if (can_reload) {
if (*mesg2 != NUL) {
- strncat(tbuf, "\n", tbuf_len);
- strncat(tbuf, mesg2, tbuf_len);
+ xstrlcat(tbuf, "\n", tbuf_len - 1);
+ xstrlcat(tbuf, mesg2, tbuf_len - 1);
}
if (do_dialog(VIM_WARNING, (char_u *) _("Warning"), (char_u *) tbuf,
(char_u *) _("&OK\n&Load File"), 1, NULL, true) == 2) {
@@ -4891,8 +4991,8 @@ buf_check_timestamp (
}
} else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) {
if (*mesg2 != NUL) {
- strncat(tbuf, "; ", tbuf_len);
- strncat(tbuf, mesg2, tbuf_len);
+ xstrlcat(tbuf, "; ", tbuf_len - 1);
+ xstrlcat(tbuf, mesg2, tbuf_len - 1);
}
EMSG(tbuf);
retval = 2;
@@ -4988,10 +5088,10 @@ void buf_reload(buf_T *buf, int orig_mode)
* the old contents. Can't use memory only, the file might be
* too big. Use a hidden buffer to move the buffer contents to.
*/
- if (bufempty() || saved == FAIL)
+ if (bufempty() || saved == FAIL) {
savebuf = NULL;
- else {
- /* Allocate a buffer without putting it in the buffer list. */
+ } else {
+ // Allocate a buffer without putting it in the buffer list.
savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
if (savebuf != NULL && buf == curbuf) {
/* Open the memline. */
@@ -5010,13 +5110,13 @@ void buf_reload(buf_T *buf, int orig_mode)
}
if (saved == OK) {
- curbuf->b_flags |= BF_CHECK_RO; /* check for RO again */
- keep_filetype = TRUE; /* don't detect 'filetype' */
- if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0,
- (linenr_T)0,
- (linenr_T)MAXLNUM, &ea, flags) == FAIL) {
- if (!aborting())
+ curbuf->b_flags |= BF_CHECK_RO; // check for RO again
+ keep_filetype = true; // don't detect 'filetype'
+ if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, (linenr_T)0,
+ (linenr_T)MAXLNUM, &ea, flags) != OK) {
+ if (!aborting()) {
EMSG2(_("E321: Could not reload \"%s\""), buf->b_fname);
+ }
if (savebuf != NULL && buf_valid(savebuf) && buf == curbuf) {
/* Put the text back from the save buffer. First
* delete any lines that readfile() added. */
@@ -5131,6 +5231,10 @@ static void vim_maketempdir(void)
// Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
char_u template[TEMP_FILE_PATH_MAXLEN];
char_u path[TEMP_FILE_PATH_MAXLEN];
+
+ // Make sure the umask doesn't remove the executable bit.
+ // "repl" has been reported to use "0177".
+ mode_t umask_save = umask(0077);
for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
// Expand environment variables, leave room for "/nvimXXXXXX/999999999"
expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
@@ -5154,6 +5258,7 @@ static void vim_maketempdir(void)
os_rmdir((char *)path);
}
}
+ (void)umask(umask_save);
}
/// Delete "name" and everything in it, recursively.
@@ -5273,6 +5378,8 @@ static AutoPatCmd *active_apc_list = NULL; /* stack of active autocommands */
*/
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.
@@ -5287,6 +5394,14 @@ 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.
*/
@@ -5306,10 +5421,11 @@ static void show_autocmd(AutoPat *ap, event_T event)
return;
if (event != last_event || ap->group != last_group) {
if (ap->group != AUGROUP_DEFAULT) {
- if (AUGROUP_NAME(ap->group) == NULL)
- msg_puts_attr((char_u *)_("--Deleted--"), hl_attr(HLF_E));
- else
+ 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((char_u *)" ");
}
msg_puts_attr(event_nr2name(event), hl_attr(HLF_T));
@@ -5475,11 +5591,33 @@ static void au_del_group(char_u *name)
int i;
i = au_find_group(name);
- if (i == AUGROUP_ERROR) /* the group doesn't exist */
+ if (i == AUGROUP_ERROR) { // the group doesn't exist
EMSG2(_("E367: No such group: \"%s\""), name);
- else {
+ } 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));
- AUGROUP_NAME(i) = NULL;
+ if (in_use) {
+ AUGROUP_NAME(i) = get_deleted_augroup();
+ } else {
+ AUGROUP_NAME(i) = NULL;
+ }
}
}
@@ -5491,8 +5629,9 @@ static void au_del_group(char_u *name)
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 && STRCMP(AUGROUP_NAME(i), name) == 0) {
+ 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;
}
}
@@ -5540,10 +5679,21 @@ void do_augroup(char_u *arg, int del_group)
#if defined(EXITFREE)
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);
- ga_clear_strings(&augroups);
+ 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()) {
+ xfree(s);
+ }
+ }
+ ga_clear(&augroups);
}
#endif
@@ -5559,13 +5709,14 @@ static event_T event_name2nr(char_u *start, char_u **end)
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)
- ;
- 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)
+ // 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;
@@ -5607,7 +5758,7 @@ find_end_event (
}
pat = arg + 1;
} else {
- for (pat = arg; *pat && !ascii_iswhite(*pat); pat = p) {
+ 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);
@@ -5722,8 +5873,9 @@ void au_event_restore(char_u *old_ei)
*
* Mostly a {group} argument can optionally appear before <event>.
*/
-void do_autocmd(char_u *arg, int forceit)
+void do_autocmd(char_u *arg_in, int forceit)
{
+ char_u *arg = arg_in;
char_u *pat;
char_u *envpat = NULL;
char_u *cmd;
@@ -5732,10 +5884,13 @@ void do_autocmd(char_u *arg, int forceit)
int nested = FALSE;
int group;
- /*
- * Check for a legal group name. If not, use AUGROUP_ALL.
- */
- group = au_get_grouparg(&arg);
+ 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.
@@ -5745,50 +5900,54 @@ void do_autocmd(char_u *arg, int forceit)
if (pat == NULL)
return;
- /*
- * Scan over the pattern. Put a NUL at the end.
- */
pat = skipwhite(pat);
- 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) {
+ 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;
+ int p_ssl_save = p_ssl;
- p_ssl = TRUE;
+ p_ssl = true;
#endif
- envpat = expand_env_save(pat);
+ envpat = expand_env_save(pat);
#ifdef BACKSLASH_IN_FILENAME
- p_ssl = p_ssl_save;
+ p_ssl = p_ssl_save;
#endif
- if (envpat != NULL)
- pat = envpat;
- }
+ if (envpat != NULL) {
+ pat = envpat;
+ }
+ }
- /*
- * Check for "nested" flag.
- */
- cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) {
- nested = TRUE;
- cmd = skipwhite(cmd + 6);
- }
+ // Check for "nested" flag.
+ cmd = skipwhite(cmd);
+ if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0
+ && ascii_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;
+ // 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;
+ }
}
/*
@@ -5802,16 +5961,17 @@ void do_autocmd(char_u *arg, int forceit)
/*
* 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) {
+ 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)
+ event = (event_T)((int)event + 1)) {
+ if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
break;
+ }
+ }
} else {
- while (*arg && !ascii_iswhite(*arg)) {
+ while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
event_T event = event_name2nr(arg, &arg);
assert(event < NUM_EVENTS);
if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
@@ -5838,7 +5998,8 @@ static int au_get_grouparg(char_u **argp)
char_u *arg = *argp;
int group = AUGROUP_ALL;
- p = skiptowhite(arg);
+ 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);
@@ -6068,13 +6229,18 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd,
int
do_doautocmd (
char_u *arg,
- int do_msg /* give message for no matching autocmds? */
+ 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.
*/
@@ -6103,8 +6269,12 @@ do_doautocmd (
fname, NULL, TRUE, group, curbuf, NULL))
nothing_done = FALSE;
- if (nothing_done && do_msg)
+ if (nothing_done && do_msg) {
MSG(_("No matching autocommands"));
+ }
+ if (did_something != NULL) {
+ *did_something = !nothing_done;
+ }
return aborting() ? FAIL : OK;
}
@@ -6133,13 +6303,14 @@ void ex_doautoall(exarg_T *eap)
/* find a window for this buffer and save some values */
aucmd_prepbuf(&aco, buf);
- /* execute the autocommands for this buffer */
- retval = do_doautocmd(arg, FALSE);
+ bool did_aucmd;
+ // execute the autocommands for this buffer
+ retval = do_doautocmd(arg, false, &did_aucmd);
- if (call_do_modelines) {
- /* Execute the modeline settings, but don't set window-local
- * options if we are using the current window for another
- * buffer. */
+ 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);
}
@@ -6258,27 +6429,24 @@ aucmd_prepbuf (
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 */
-)
+/// 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)
{
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. */
+ 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);
+ goto_tabpage_tp(tp, true, true);
}
win_goto(aucmd_win);
goto win_found;
@@ -6287,49 +6455,50 @@ aucmd_restbuf (
}
win_found:
- /* Remove the window and frame from the tree of frames. */
+ // 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 */
- restore_snapshot(SNAP_AUCMD_IDX, FALSE);
- (void)win_comp_pos(); /* recompute window positions */
+ aucmd_win_used = false;
+ last_status(false); // may need to remove last status line
+ restore_snapshot(SNAP_AUCMD_IDX, false);
+ (void)win_comp_pos(); // recompute window positions
unblock_autocmds();
- if (win_valid(aco->save_curwin))
+ if (win_valid(aco->save_curwin)) {
curwin = aco->save_curwin;
- else
- /* Hmm, original window disappeared. Just use the first one. */
+ } else {
+ // Hmm, original window disappeared. Just use the first one.
curwin = firstwin;
- vars_clear(&aucmd_win->w_vars->dv_hashtab); /* free all w: variables */
- hash_init(&aucmd_win->w_vars->dv_hashtab); /* re-use the hashtab */
+ }
+ 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 */
+ // 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 */
+ // 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. */
+ // 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
&& buf_valid(aco->new_curbuf)
&& aco->new_curbuf->b_ml.ml_mfp != NULL) {
- if (curwin->w_s == &curbuf->b_s)
+ if (curwin->w_s == &curbuf->b_s) {
curwin->w_s = &aco->new_curbuf->b_s;
- --curbuf->b_nwindows;
+ }
+ curbuf->b_nwindows--;
curbuf = aco->new_curbuf;
curwin->w_buffer = curbuf;
- ++curbuf->b_nwindows;
+ curbuf->b_nwindows++;
}
curwin = aco->save_curwin;
@@ -6597,8 +6766,9 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
fname = vim_strsave(fname); /* make a copy, so we can change it */
} else {
sfname = vim_strsave(fname);
- // don't try expanding the following events
+ // Don't try expanding the following events.
if (event == EVENT_COLORSCHEME
+ || event == EVENT_DIRCHANGED
|| event == EVENT_FILETYPE
|| event == EVENT_FUNCUNDEFINED
|| event == EVENT_OPTIONSET
@@ -6607,10 +6777,11 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|| event == EVENT_REMOTEREPLY
|| event == EVENT_SPELLFILEMISSING
|| event == EVENT_SYNTAX
- || event == EVENT_TABCLOSED)
+ || event == EVENT_TABCLOSED) {
fname = vim_strsave(fname);
- else
- fname = (char_u *)FullName_save((char *)fname, FALSE);
+ } else {
+ fname = (char_u *)FullName_save((char *)fname, false);
+ }
}
if (fname == NULL) { /* out of memory */
xfree(sfname);
@@ -6783,6 +6954,10 @@ BYPASS_AU:
if (event == EVENT_BUFWIPEOUT && buf != NULL)
aubuflocal_remove(buf);
+ if (retval == OK && event == EVENT_FILETYPE) {
+ au_did_filetype = true;
+ }
+
return retval;
}
@@ -6982,9 +7157,11 @@ char_u *get_augroup_name(expand_T *xp, int idx)
return (char_u *)"END";
if (idx >= augroups.ga_len) /* end of list */
return NULL;
- if (AUGROUP_NAME(idx) == NULL) /* skip deleted entries */
+ if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup()) {
+ // skip deleted entries
return (char_u *)"";
- return AUGROUP_NAME(idx); /* return a name */
+ }
+ return AUGROUP_NAME(idx); // return a name
}
static int include_groups = FALSE;
@@ -7041,10 +7218,12 @@ set_context_in_autocmd (
*/
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)
- return (char_u *)""; /* skip deleted entries */
- return AUGROUP_NAME(idx); /* return a name */
+ 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;
}