diff options
Diffstat (limited to 'src/nvim/fileio.c')
-rw-r--r-- | src/nvim/fileio.c | 617 |
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; } |