diff options
Diffstat (limited to 'src/nvim/fileio.c')
| -rw-r--r-- | src/nvim/fileio.c | 443 | 
1 files changed, 231 insertions, 212 deletions
| diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index c1b8203ed1..efeee1ba2b 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1,6 +1,7 @@ -/* - * fileio.c: read from and write to a file - */ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// fileio.c: read from and write to a file  #include <assert.h>  #include <errno.h> @@ -62,57 +63,62 @@  #define BUFSIZE         8192    /* size of normal write buffer */  #define SMBUFSIZE       256     /* size of emergency write buffer */ -/* - * 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: - * - * 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 - * - * 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. - */ +// +// The autocommands are stored in a list for each event. +// Autocommands for the same pattern, that are consecutive, are joined +// together, to avoid having to match the pattern too often. +// The result is an array of Autopat lists, which point to AutoCmd lists: +// +// last_autopat[0]  -----------------------------+ +//                                               V +// first_autopat[0] --> Autopat.next  -->  Autopat.next -->  NULL +//                      Autopat.cmds       Autopat.cmds +//                          |                    | +//                          V                    V +//                      AutoCmd.next       AutoCmd.next +//                          |                    | +//                          V                    V +//                      AutoCmd.next            NULL +//                          | +//                          V +//                         NULL +// +// last_autopat[1]  --------+ +//                          V +// first_autopat[1] --> Autopat.next  -->  NULL +//                      Autopat.cmds +//                          | +//                          V +//                      AutoCmd.next +//                          | +//                          V +//                         NULL +//   etc. +// +//   The order of AutoCmds is important, this is the order in which they were +//   defined and will have to be executed. +//  typedef struct AutoCmd { -  char_u          *cmd;                 /* The command to be executed (NULL -                                           when command has been removed) */ -  char nested;                          /* If autocommands nest here */ -  char last;                            /* last command in list */ -  scid_T scriptID;                      /* script ID where defined */ -  struct AutoCmd  *next;                /* Next AutoCmd in list */ +  char_u          *cmd;                 // The command to be executed (NULL +                                        // when command has been removed) +  char nested;                          // If autocommands nest here +  char last;                            // last command in list +  scid_T scriptID;                      // script ID where defined +  struct AutoCmd  *next;                // Next AutoCmd in list  } AutoCmd;  typedef struct AutoPat { -  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 */ -  struct AutoPat  *next;                /* next AutoPat in AutoPat list */ -  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() */ +  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;  /* @@ -223,6 +229,15 @@ 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".   * @@ -244,6 +259,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)   *		stdin)   * 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 + * READ_FIFO	read from fifo/socket instead of a file   *   * return FAIL for failure, NOTDONE for directory (failure), or OK   */ @@ -264,6 +280,7 @@ readfile (    int filtering = (flags & READ_FILTER);    int read_stdin = (flags & READ_STDIN);    int read_buffer = (flags & READ_BUFFER); +  int read_fifo = (flags & READ_FIFO);    int set_options = newfile || read_buffer                      || (eap != NULL && eap->read_edit);    linenr_T read_buf_lnum = 1;           /* next line to read from curbuf */ @@ -278,11 +295,10 @@ readfile (    colnr_T len;    long size = 0;    char_u      *p = NULL; -  off_t filesize = 0; -  int skip_read = FALSE; +  off_T filesize = 0; +  int skip_read = false;    context_sha256_T sha_ctx; -  int read_undo_file = FALSE; -  int split = 0;                        /* number of split lines */ +  int read_undo_file = false;    linenr_T linecnt;    int error = FALSE;                    /* errors encountered */    int ff_error = EOL_UNKNOWN;           /* file format with errors */ @@ -297,12 +313,9 @@ readfile (    linenr_T skip_count = 0;    linenr_T read_count = 0;    int msg_save = msg_scroll; -  linenr_T read_no_eol_lnum = 0;        /* non-zero lnum when last line of -                                        * last read was missing the eol */ -  int try_mac = (vim_strchr(p_ffs, 'm') != NULL); -  int try_dos = (vim_strchr(p_ffs, 'd') != NULL); -  int try_unix = (vim_strchr(p_ffs, 'x') != NULL); -  int file_rewind = FALSE; +  linenr_T read_no_eol_lnum = 0;        // non-zero lnum when last line of +                                        // last read was missing the eol +  int file_rewind = false;    int can_retry;    linenr_T conv_error = 0;              /* line nr with conversion error */    linenr_T illegal_byte = 0;            /* line nr with illegal byte */ @@ -423,7 +436,7 @@ readfile (      }    } -  if (!read_buffer && !read_stdin) { +  if (!read_buffer && !read_stdin && !read_fifo) {      perm = os_getperm((const char *)fname);  #ifdef UNIX      // On Unix it is possible to read a directory, so we have to @@ -465,8 +478,8 @@ readfile (    if (check_readonly && !readonlymode)      curbuf->b_p_ro = FALSE; -  if (newfile && !read_stdin && !read_buffer) { -    /* Remember time of file. */ +  if (newfile && !read_stdin && !read_buffer && !read_fifo) { +    // Remember time of file.      FileInfo file_info;      if (os_fileinfo((char *)fname, &file_info)) {        buf_store_file_info(curbuf, &file_info); @@ -634,37 +647,46 @@ readfile (    curbuf->b_op_start.lnum = ((from == 0) ? 1 : from);    curbuf->b_op_start.col = 0; +  int try_mac = (vim_strchr(p_ffs, 'm') != NULL); +  int try_dos = (vim_strchr(p_ffs, 'd') != NULL); +  int try_unix = (vim_strchr(p_ffs, 'x') != NULL); +    if (!read_buffer) {      int m = msg_scroll;      int n = msg_scrolled; -    /* -     * The file must be closed again, the autocommands may want to change -     * the file before reading it. -     */ -    if (!read_stdin) -      close(fd);                /* ignore errors */ +    // The file must be closed again, the autocommands may want to change +    // the file before reading it. +    if (!read_stdin) { +      close(fd);                // ignore errors +    } -    /* -     * The output from the autocommands should not overwrite anything and -     * should not be overwritten: Set msg_scroll, restore its value if no -     * output was done. -     */ -    msg_scroll = TRUE; -    if (filtering) +    // The output from the autocommands should not overwrite anything and +    // should not be overwritten: Set msg_scroll, restore its value if no +    // output was done. +    msg_scroll = true; +    if (filtering) {        apply_autocmds_exarg(EVENT_FILTERREADPRE, NULL, sfname, -          FALSE, curbuf, eap); -    else if (read_stdin) +                           false, curbuf, eap); +    } else if (read_stdin) {        apply_autocmds_exarg(EVENT_STDINREADPRE, NULL, sfname, -          FALSE, curbuf, eap); -    else if (newfile) +                           false, curbuf, eap); +    } else if (newfile) {        apply_autocmds_exarg(EVENT_BUFREADPRE, NULL, sfname, -          FALSE, curbuf, eap); -    else +                           false, curbuf, eap); +    } else {        apply_autocmds_exarg(EVENT_FILEREADPRE, sfname, sfname, -          FALSE, NULL, eap); -    if (msg_scrolled == n) +                           false, NULL, eap); +    } + +    // autocommands may have changed it +    try_mac = (vim_strchr(p_ffs, 'm') != NULL); +    try_dos = (vim_strchr(p_ffs, 'd') != NULL); +    try_unix = (vim_strchr(p_ffs, 'x') != NULL); + +    if (msg_scrolled == n) {        msg_scroll = m; +    }      if (aborting()) {       /* autocmds may abort script processing */        --no_wait_return; @@ -730,43 +752,16 @@ readfile (      fenc = (char_u *)"";                /* binary: don't convert */      fenc_alloced = FALSE;    } else if (curbuf->b_help) { -    char_u firstline[80]; -    int fc; - -    /* Help files are either utf-8 or latin1.  Try utf-8 first, if this -     * fails it must be latin1. -     * Always do this when 'encoding' is "utf-8".  Otherwise only do -     * this when needed to avoid [converted] remarks all the time. -     * It is needed when the first line contains non-ASCII characters. -     * That is only in *.??x files. */ -    fenc = (char_u *)"latin1"; -    c = enc_utf8; -    if (!c && !read_stdin) { -      fc = fname[STRLEN(fname) - 1]; -      if (TOLOWER_ASC(fc) == 'x') { -        /* Read the first line (and a bit more).  Immediately rewind to -         * the start of the file.  If the read() fails "len" is -1. */ -        len = read_eintr(fd, firstline, 80); -        lseek(fd, (off_t)0L, SEEK_SET); -        for (p = firstline; p < firstline + len; ++p) -          if (*p >= 0x80) { -            c = TRUE; -            break; -          } -      } -    } +    // Help files are either utf-8 or latin1.  Try utf-8 first, if this +    // fails it must be latin1. +    // It is needed when the first line contains non-ASCII characters. +    // That is only in *.??x files. +    fenc_next = (char_u *)"latin1"; +    fenc = (char_u *)"utf-8"; -    if (c) { -      fenc_next = fenc; -      fenc = (char_u *)"utf-8"; +    fenc_alloced = false; -      /* When the file is utf-8 but a character doesn't fit in -       * 'encoding' don't retry.  In help text editing utf-8 bytes -       * doesn't make sense. */ -      if (!enc_utf8) -        keep_dest_enc = TRUE; -    } -    fenc_alloced = FALSE; +    c = 1;    } else if (*p_fencs == NUL) {      fenc = curbuf->b_p_fenc;            /* use format from buffer */      fenc_alloced = FALSE; @@ -801,9 +796,9 @@ retry:      if (read_buffer) {        read_buf_lnum = 1;        read_buf_col = 0; -    } else if (read_stdin || lseek(fd, (off_t)0L, SEEK_SET) != 0) { -      /* Can't rewind the file, give up. */ -      error = TRUE; +    } else if (read_stdin || vim_lseek(fd, (off_T)0L, SEEK_SET) != 0) { +      // Can't rewind the file, give up. +      error = true;        goto failed;      }      /* Delete the previously read lines. */ @@ -919,6 +914,7 @@ retry:       * and we can't do it internally or with iconv().       */      if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL +        && !read_fifo  #  ifdef USE_ICONV          && iconv_fd == (iconv_t)-1  #  endif @@ -959,7 +955,7 @@ retry:    /* Set "can_retry" when it's possible to rewind the file and try with     * another "fenc" value.  It's FALSE when no other "fenc" to try, reading     * stdin or fixed at a specific encoding. */ -  can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc); +  can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc && !read_fifo);    if (!skip_read) {      linerest = 0; @@ -971,6 +967,7 @@ retry:                        && curbuf->b_ffname != NULL                        && curbuf->b_p_udf                        && !filtering +                      && !read_fifo                        && !read_stdin                        && !read_buffer);      if (read_undo_file) @@ -1031,8 +1028,8 @@ retry:            size = size / ICONV_MULT;             /* worst case */          if (conv_restlen > 0) { -          /* Insert unconverted bytes from previous line. */ -          memmove(ptr, conv_rest, conv_restlen); +          // Insert unconverted bytes from previous line. +          memmove(ptr, conv_rest, conv_restlen);  // -V614            ptr += conv_restlen;            size -= conv_restlen;          } @@ -1636,21 +1633,19 @@ rewind_retry:              *ptr = NUL;                         /* end of line */              len = (colnr_T)(ptr - line_start + 1);              if (fileformat == EOL_DOS) { -              if (ptr[-1] == CAR) {             /* remove CR */ +              if (ptr > line_start && ptr[-1] == CAR) { +                // remove CR before NL                  ptr[-1] = NUL; -                --len; -              } -              /* -               * Reading in Dos format, but no CR-LF found! -               * When 'fileformats' includes "unix", delete all -               * the lines read so far and start all over again. -               * Otherwise give an error message later. -               */ -              else if (ff_error != EOL_DOS) { -                if (   try_unix -                       && !read_stdin -                       && (read_buffer -                           || lseek(fd, (off_t)0L, SEEK_SET) == 0)) { +                len--; +              } else if (ff_error != EOL_DOS) { +                // Reading in Dos format, but no CR-LF found! +                // When 'fileformats' includes "unix", delete all +                // the lines read so far and start all over again. +                // Otherwise give an error message later. +                if (try_unix +                    && !read_stdin +                    && (read_buffer +                        || vim_lseek(fd, (off_T)0L, SEEK_SET) == 0)) {                    fileformat = EOL_UNIX;                    if (set_options)                      set_fileformat(EOL_UNIX, OPT_LOCAL); @@ -1741,9 +1736,17 @@ failed:    xfree(buffer);    if (read_stdin) { -    /* Use stderr for stdin, makes shell commands work. */      close(0); +#ifndef WIN32 +    // On Unix, use stderr for stdin, makes shell commands work.      ignored = dup(2); +#else +    // On Windows, use the console input handle for stdin. +    HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, +                              FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, +                              OPEN_EXISTING, 0, (HANDLE)NULL); +    ignored = _open_osfhandle(conin, _O_RDONLY); +#endif    }    if (tmpname != NULL) { @@ -1838,10 +1841,6 @@ failed:          STRCAT(IObuff, _("[CR missing]"));          c = TRUE;        } -      if (split) { -        STRCAT(IObuff, _("[long lines split]")); -        c = TRUE; -      }        if (notconverted) {          STRCAT(IObuff, _("[NOT converted]"));          c = TRUE; @@ -1946,7 +1945,7 @@ failed:      u_read_undo(NULL, hash, fname);    } -  if (!read_stdin && !read_buffer) { +  if (!read_stdin && !read_fifo && (!read_buffer || sfname != NULL)) {      int m = msg_scroll;      int n = msg_scrolled; @@ -1964,7 +1963,7 @@ failed:      if (filtering) {        apply_autocmds_exarg(EVENT_FILTERREADPOST, NULL, sfname,                             false, curbuf, eap); -    } else if (newfile) { +    } else if (newfile || (read_buffer && sfname != NULL)) {        apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname,                             false, curbuf, eap);        if (!au_did_filetype && *curbuf->b_p_ft != NUL) { @@ -1997,7 +1996,7 @@ failed:  /// Do not accept "/dev/fd/[012]", opening these may hang Vim.  ///  /// @param fname file name to check -static bool is_dev_fd_file(char_u *fname) +bool is_dev_fd_file(char_u *fname)    FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT  {    return STRNCMP(fname, "/dev/fd/", 8) == 0 @@ -2593,11 +2592,9 @@ buf_write (        perm = -1;      }    } -#else /* win32 */ -      /* -       * Check for a writable device name. -       */ -  c = os_nodetype((char *)fname); +#else  // win32 +  // Check for a writable device name. +  c = fname == NULL ? NODE_OTHER : os_nodetype((char *)fname);    if (c == NODE_OTHER) {      SET_ERRMSG_NUM("E503", _("is not a file or writable device"));      goto fail; @@ -2617,9 +2614,8 @@ buf_write (      if (overwriting) {        os_fileinfo((char *)fname, &file_info_old);      } -    } -#endif /* !UNIX */ +#endif  // !UNIX    if (!device && !newfile) {      /* @@ -3067,7 +3063,7 @@ nobackup:     */    if (reset_changed && !newfile && overwriting        && !(exiting && backup != NULL)) { -    ml_preserve(buf, FALSE); +    ml_preserve(buf, false, !!p_fs);      if (got_int) {        SET_ERRMSG(_(e_interr));        goto restore_backup; @@ -3185,8 +3181,8 @@ nobackup:  #ifdef UNIX        FileInfo file_info; -      /* Don't delete the file when it's a hard or symbolic link. */ -      if ((!newfile && os_fileinfo_hardlinks(&file_info) > 1) +      // Don't delete the file when it's a hard or symbolic link. +      if ((!newfile && os_fileinfo_hardlinks(&file_info_old) > 1)            || (os_fileinfo_link((char *)fname, &file_info)                && !os_fileinfo_id_equal(&file_info, &file_info_old))) {          SET_ERRMSG(_("E166: Can't open linked file for writing")); @@ -3351,9 +3347,9 @@ restore_backup:          *s++ = NL;        }      } -    if (++len == bufsize && end) { +    if (++len == bufsize) {        if (buf_write_bytes(&write_info) == FAIL) { -        end = 0;                        /* write error: break loop */ +        end = 0;  // Write error: break loop.          break;        }        nchars += bufsize; @@ -3362,7 +3358,7 @@ restore_backup:        os_breakcheck();        if (got_int) { -        end = 0;                        /* Interrupted, break loop */ +        end = 0;  // Interrupted, break loop.          break;        }      } @@ -3857,7 +3853,7 @@ static bool msg_add_fileformat(int eol_type)  /*   * Append line and character count to IObuff.   */ -void msg_add_lines(int insert_space, long lnum, off_t nchars) +void msg_add_lines(int insert_space, long lnum, off_T nchars)  {    char_u  *p; @@ -4337,7 +4333,7 @@ void shorten_fnames(int force)          && !path_with_url((char *)buf->b_fname)          && (force              || buf->b_sfname == NULL -            || path_is_absolute_path(buf->b_sfname))) { +            || path_is_absolute(buf->b_sfname))) {        xfree(buf->b_sfname);        buf->b_sfname = NULL;        p = path_shorten_fname(buf->b_ffname, dirname); @@ -4459,25 +4455,35 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)  /// @param size size of the buffer  /// @param fp file to read from  /// -/// @return true for end-of-file. +/// @return true for EOF or error  bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL  { -  char        *eof; -#define FGETS_SIZE 200 -  char tbuf[FGETS_SIZE]; +  char *retval; +  assert(size > 0);    buf[size - 2] = NUL; -  eof = fgets((char *)buf, size, fp); + +  do { +    errno = 0; +    retval = fgets((char *)buf, size, fp); +  } while (retval == NULL && errno == EINTR && ferror(fp)); +    if (buf[size - 2] != NUL && buf[size - 2] != '\n') { -    buf[size - 1] = NUL;            /* Truncate the line */ +    char tbuf[200]; + +    buf[size - 1] = NUL;  // Truncate the line. -    /* Now throw away the rest of the line: */ +    // Now throw away the rest of the line:      do { -      tbuf[FGETS_SIZE - 2] = NUL; -      ignoredp = fgets((char *)tbuf, FGETS_SIZE, fp); -    } while (tbuf[FGETS_SIZE - 2] != NUL && tbuf[FGETS_SIZE - 2] != '\n'); +      tbuf[sizeof(tbuf) - 2] = NUL; +      errno = 0; +      retval = fgets((char *)tbuf, sizeof(tbuf), fp); +      if (retval == NULL && (feof(fp) || errno != EINTR)) { +        break; +      } +    } while (tbuf[sizeof(tbuf) - 2] != NUL && tbuf[sizeof(tbuf) - 2] != '\n');    } -  return eof == NULL; +  return retval == NULL;  }  /// Read 2 bytes from "fd" and turn them into an int, MSB first. @@ -4570,6 +4576,7 @@ int put_time(FILE *fd, time_t time_)  ///  /// @return -1 for failure, 0 for success  int vim_rename(const char_u *from, const char_u *to) +  FUNC_ATTR_NONNULL_ALL  {    int fd_in;    int fd_out; @@ -4683,7 +4690,7 @@ int vim_rename(const char_u *from, const char_u *to)      return -1;    } -  // Avoid xmalloc() here as vim_rename() is called by buf_write() when neovim +  // Avoid xmalloc() here as vim_rename() is called by buf_write() when nvim    // is `preserve_exit()`ing.    buffer = try_malloc(BUFSIZE);    if (buffer == NULL) { @@ -4845,6 +4852,7 @@ buf_check_timestamp (      buf_T *buf,      int focus               /* called for GUI focus event */  ) +  FUNC_ATTR_NONNULL_ALL  {    int retval = 0;    char_u      *path; @@ -5095,14 +5103,12 @@ void buf_reload(buf_T *buf, int orig_mode)      flags |= READ_KEEP_UNDO;    } -  /* -   * To behave like when a new file is edited (matters for -   * BufReadPost autocommands) we first need to delete the current -   * buffer contents.  But if reading the file fails we should keep -   * 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) { +  // To behave like when a new file is edited (matters for +  // BufReadPost autocommands) we first need to delete the current +  // buffer contents.  But if reading the file fails we should keep +  // 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) {      savebuf = NULL;    } else {      // Allocate a buffer without putting it in the buffer list. @@ -5135,7 +5141,7 @@ void buf_reload(buf_T *buf, int orig_mode)        if (savebuf != NULL && bufref_valid(&bufref) && buf == curbuf) {          // Put the text back from the save buffer.  First          // delete any lines that readfile() added. -        while (!bufempty()) { +        while (!BUFEMPTY()) {            if (ml_delete(buf->b_ml.ml_line_count, false) == FAIL) {              break;            } @@ -5537,6 +5543,15 @@ static void au_cleanup(void)        /* 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); @@ -6129,10 +6144,13 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd,        patlen = (int)STRLEN(buflocal_pat);       /*   but not endpat */      } -    /* -     * Find AutoPat entries with this pattern. -     */ -    prev_ap = &first_autopat[(int)event]; +    // 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: @@ -6218,6 +6236,7 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd,          }          ap->cmds = NULL;          *prev_ap = ap; +        last_autopat[(int)event] = ap;          ap->next = NULL;          if (group == AUGROUP_ALL)            ap->group = current_augroup; @@ -6283,13 +6302,13 @@ do_doautocmd (    fname = skipwhite(fname); -  /* -   * Loop over the events. -   */ -  while (*arg && !ascii_iswhite(*arg)) -    if (apply_autocmds_group(event_name2nr(arg, &arg), -            fname, NULL, TRUE, group, curbuf, NULL)) -      nothing_done = FALSE; +  // 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")); @@ -6664,7 +6683,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,    char_u      *save_sourcing_name;    linenr_T save_sourcing_lnum;    char_u      *save_autocmd_fname; -  int save_autocmd_fname_full;    int save_autocmd_bufnr;    char_u      *save_autocmd_match;    int save_autocmd_busy; @@ -6680,12 +6698,12 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,    proftime_T wait_time;    bool did_save_redobuff = false; -  /* -   * Quickly return if there are no autocommands for this event or -   * autocommands are blocked. -   */ -  if (first_autopat[(int)event] == NULL || autocmd_blocked > 0) +  // 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 @@ -6737,7 +6755,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,     * Save the autocmd_* variables and info about the current buffer.     */    save_autocmd_fname = autocmd_fname; -  save_autocmd_fname_full = autocmd_fname_full;    save_autocmd_bufnr = autocmd_bufnr;    save_autocmd_match = autocmd_match;    save_autocmd_busy = autocmd_busy; @@ -6751,19 +6768,22 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,     * invalid.     */    if (fname_io == NULL) { -    if (event == EVENT_COLORSCHEME || event == EVENT_OPTIONSET) +    if (event == EVENT_COLORSCHEME || event == EVENT_OPTIONSET) {        autocmd_fname = NULL; -    else if (fname != NULL && *fname != NUL) +    } else if (fname != NULL && !ends_excmd(*fname)) {        autocmd_fname = fname; -    else if (buf != NULL) +    } else if (buf != NULL) {        autocmd_fname = buf->b_ffname; -    else +    } else {        autocmd_fname = NULL; -  } else +    } +  } else {      autocmd_fname = fname_io; -  if (autocmd_fname != NULL) -    autocmd_fname = vim_strsave(autocmd_fname); -  autocmd_fname_full = FALSE;   /* call FullName_save() later */ +  } +  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>. @@ -6894,8 +6914,8 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,      patcmd.next = active_apc_list;      active_apc_list = &patcmd; -    /* set v:cmdarg (only when there is a matching pattern) */ -    save_cmdbang = get_vim_var_nr(VV_CMDBANG); +    // 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); @@ -6930,7 +6950,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,    sourcing_lnum = save_sourcing_lnum;    xfree(autocmd_fname);    autocmd_fname = save_autocmd_fname; -  autocmd_fname_full = save_autocmd_fname_full;    autocmd_bufnr = save_autocmd_bufnr;    autocmd_match = save_autocmd_match;    current_SID = save_current_SID; | 
