move_lines(buf_T *frombuf, buf_T *tobuf)
{
    buf_T	*tbuf = curbuf;
    int		retval = OK;
    linenr_T	lnum;
    char_u	*p;

    /* Copy the lines in "frombuf" to "tobuf". */
    curbuf = tobuf;
    for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; ++lnum)
    {
	p = vim_strsave(ml_get_buf(frombuf, lnum, FALSE));
	if (p == NULL || ml_append(lnum - 1, p, 0, FALSE) == FAIL)
	{
	    vim_free(p);
	    retval = FAIL;
	    break;
	}
	vim_free(p);
    }

    /* Delete all the lines in "frombuf". */
    if (retval != FAIL)
    {
	curbuf = frombuf;
	for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; --lnum)
	    if (ml_delete(lnum, FALSE) == FAIL)
	    {
		/* Oops!  We could try putting back the saved lines, but that
		 * might fail again... */
		retval = FAIL;
		break;
	    }
    }

    curbuf = tbuf;
    return retval;
}


patch 8.0.1263: others can read the swap file if a user is careless

Problem:    Others can read the swap file if a user is careless with his
            primary group.
Solution:   If the group permission allows for reading but the world
            permissions doesn't, make sure the group is right.

set_context_in_autocmd(
    expand_T	*xp,
    char_u	*arg,
    int		doautocmd)	/* TRUE for :doauto*, FALSE for :autocmd */
{
    char_u	*p;
    int		group;

    /* check for a group name, skip it if present */
    include_groups = FALSE;
    p = arg;
    group = au_get_grouparg(&arg);
    if (group == AUGROUP_ERROR)
	return NULL;
    /* If there only is a group name that's what we expand. */
    if (*arg == NUL && group != AUGROUP_ALL && !VIM_ISWHITE(arg[-1]))
    {
	arg = p;
	group = AUGROUP_ALL;
    }

    /* skip over event name */
    for (p = arg; *p != NUL && !VIM_ISWHITE(*p); ++p)
	if (*p == ',')
	    arg = p + 1;
    if (*p == NUL)
    {
	if (group == AUGROUP_ALL)
	    include_groups = TRUE;
	xp->xp_context = EXPAND_EVENTS;	    /* expand event name */
	xp->xp_pattern = arg;
	return NULL;
    }

    /* skip over pattern */
    arg = skipwhite(p);
    while (*arg && (!VIM_ISWHITE(*arg) || arg[-1] == '\\'))
	arg++;
    if (*arg)
	return arg;			    /* expand (next) command */

    if (doautocmd)
	xp->xp_context = EXPAND_FILES;	    /* expand file names */
    else
	xp->xp_context = EXPAND_NOTHING;    /* pattern is not expanded */
    return NULL;
}

fileio.c in Vim prior to 8.0.1263 sets the group ownership of a .swp file to the editor's primary group (which may be different from the group ownership of the original file), which allows local users to obtain sensitive information by leveraging an applicable group membership, as demonstrated by /etc/shadow owned by root:shadow mode 0640, but /etc/.shadow.swp owned by root:users mode 0640, a different vulnerability than CVE-2017-1000382.


modname(
    char_u *fname,
    char_u *ext,
    int	    prepend_dot)	/* may prepend a '.' to file name */
{
    return buf_modname((curbuf->b_p_sn || curbuf->b_shortname),
						     fname, ext, prepend_dot);
}


trigger_cursorhold(void)
{
    int		state;

    if (!did_cursorhold
	    && has_cursorhold()
	    && !Recording
	    && typebuf.tb_len == 0
#ifdef FEAT_INS_EXPAND
	    && !ins_compl_active()
#endif
	    )
    {
	state = get_real_state();
	if (state == NORMAL_BUSY || (state & INSERT) != 0)
	    return TRUE;
    }
    return FALSE;
}


buf_reload(buf_T *buf, int orig_mode)
{
    exarg_T	ea;
    pos_T	old_cursor;
    linenr_T	old_topline;
    int		old_ro = buf->b_p_ro;
    buf_T	*savebuf;
    bufref_T	bufref;
    int		saved = OK;
    aco_save_T	aco;
    int		flags = READ_NEW;

    /* set curwin/curbuf for "buf" and save some things */
    aucmd_prepbuf(&aco, buf);

    /* We only want to read the text from the file, not reset the syntax
     * highlighting, clear marks, diff status, etc.  Force the fileformat
     * and encoding to be the same. */
    if (prep_exarg(&ea, buf) == OK)
    {
	old_cursor = curwin->w_cursor;
	old_topline = curwin->w_topline;

	if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur)
	{
	    /* Save all the text, so that the reload can be undone.
	     * Sync first so that this is a separate undo-able action. */
	    u_sync(FALSE);
	    saved = u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, TRUE);
	    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)
	    savebuf = NULL;
	else
	{
	    /* Allocate a buffer without putting it in the buffer list. */
	    savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
	    set_bufref(&bufref, savebuf);
	    if (savebuf != NULL && buf == curbuf)
	    {
		/* Open the memline. */
		curbuf = savebuf;
		curwin->w_buffer = savebuf;
		saved = ml_open(curbuf);
		curbuf = buf;
		curwin->w_buffer = buf;
	    }
	    if (savebuf == NULL || saved == FAIL || buf != curbuf
				      || move_lines(buf, savebuf) == FAIL)
	    {
		EMSG2(_("E462: Could not prepare for reloading \"%s\""),
							    buf->b_fname);
		saved = FAIL;
	    }
	}

	if (saved == OK)
	{
	    curbuf->b_flags |= BF_CHECK_RO;	/* check for RO again */
#ifdef FEAT_AUTOCMD
	    keep_filetype = TRUE;		/* don't detect 'filetype' */
#endif
	    if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0,
			(linenr_T)0,
			(linenr_T)MAXLNUM, &ea, flags) != OK)
	    {
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
		if (!aborting())
#endif
		    EMSG2(_("E321: Could not reload \"%s\""), buf->b_fname);
		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())
			if (ml_delete(buf->b_ml.ml_line_count, FALSE) == FAIL)
			    break;
		    (void)move_lines(savebuf, buf);
		}
	    }
	    else if (buf == curbuf)  /* "buf" still valid */
	    {
		/* Mark the buffer as unmodified and free undo info. */
		unchanged(buf, TRUE);
		if ((flags & READ_KEEP_UNDO) == 0)
		{
		    u_blockfree(buf);
		    u_clearall(buf);
		}
		else
		{
		    /* Mark all undo states as changed. */
		    u_unchanged(curbuf);
		}
	    }
	}
	vim_free(ea.cmd);

	if (savebuf != NULL && bufref_valid(&bufref))
	    wipe_buffer(savebuf, FALSE);

#ifdef FEAT_DIFF
	/* Invalidate diff info if necessary. */
	diff_invalidate(curbuf);
#endif

	/* Restore the topline and cursor position and check it (lines may
	 * have been removed). */
	if (old_topline > curbuf->b_ml.ml_line_count)
	    curwin->w_topline = curbuf->b_ml.ml_line_count;
	else
	    curwin->w_topline = old_topline;
	curwin->w_cursor = old_cursor;
	check_cursor();
	update_topline();
#ifdef FEAT_AUTOCMD
	keep_filetype = FALSE;
#endif
#ifdef FEAT_FOLDING
	{
	    win_T	*wp;
	    tabpage_T	*tp;

	    /* Update folds unless they are defined manually. */
	    FOR_ALL_TAB_WINDOWS(tp, wp)
		if (wp->w_buffer == curwin->w_buffer
			&& !foldmethodIsManual(wp))
		    foldUpdateAll(wp);
	}
#endif
	/* If the mode didn't change and 'readonly' was set, keep the old
	 * value; the user probably used the ":view" command.  But don't
	 * reset it, might have had a read error. */
	if (orig_mode == curbuf->b_orig_mode)
	    curbuf->b_p_ro |= old_ro;

	/* Modelines must override settings done by autocommands. */
	do_modelines(0);
    }

    /* restore curwin/curbuf and a few other things */
    aucmd_restbuf(&aco);
    /* Careful: autocommands may have made "buf" invalid! */
}


set_file_time(
    char_u  *fname,
    time_t  atime,	    /* access time */
    time_t  mtime)	    /* modification time */
{
# if defined(HAVE_UTIME) && defined(HAVE_UTIME_H)
    struct utimbuf  buf;

    buf.actime	= atime;
    buf.modtime	= mtime;
    (void)utime((char *)fname, &buf);
# else
#  if defined(HAVE_UTIMES)
    struct timeval  tvp[2];

    tvp[0].tv_sec   = atime;
    tvp[0].tv_usec  = 0;
    tvp[1].tv_sec   = mtime;
    tvp[1].tv_usec  = 0;
#   ifdef NeXT
    (void)utimes((char *)fname, tvp);
#   else
    (void)utimes((char *)fname, (const struct timeval *)&tvp);
#   endif
#  endif
# endif
}


vim_fgets(char_u *buf, int size, FILE *fp)
{
    char	*eof;
#define FGETS_SIZE 200
    char	tbuf[FGETS_SIZE];

    buf[size - 2] = NUL;
#ifdef USE_CR
    eof = fgets_cr((char *)buf, size, fp);
#else
    eof = fgets((char *)buf, size, fp);
#endif
    if (buf[size - 2] != NUL && buf[size - 2] != '\n')
    {
	buf[size - 1] = NUL;	    /* Truncate the line */

	/* Now throw away the rest of the line: */
	do
	{
	    tbuf[FGETS_SIZE - 2] = NUL;
#ifdef USE_CR
	    ignoredp = fgets_cr((char *)tbuf, FGETS_SIZE, fp);
#else
	    ignoredp = fgets((char *)tbuf, FGETS_SIZE, fp);
#endif
	} while (tbuf[FGETS_SIZE - 2] != NUL && tbuf[FGETS_SIZE - 2] != '\n');
    }
    return (eof == NULL);
}


aucmd_restbuf(
    aco_save_T	*aco)		/* structure holding saved values */
{
    --curbuf->b_nwindows;
    curbuf = aco->save_curbuf;
    curwin->w_buffer = curbuf;
    ++curbuf->b_nwindows;
}


vim_tempname(
    int	    extra_char UNUSED,  /* char to use in the name instead of '?' */
    int	    keep UNUSED)
{
#ifdef USE_TMPNAM
    char_u	itmp[L_tmpnam];	/* use tmpnam() */
#else
    char_u	itmp[TEMPNAMELEN];
#endif

#ifdef TEMPDIRNAMES
    static char	*(tempdirs[]) = {TEMPDIRNAMES};
    int		i;
# ifndef EEXIST
    stat_T	st;
# endif

    /*
     * This will create a directory for private use by this instance of Vim.
     * This is done once, and the same directory is used for all temp files.
     * This method avoids security problems because of symlink attacks et al.
     * It's also a bit faster, because we only need to check for an existing
     * file when creating the directory and not for each temp file.
     */
    if (vim_tempdir == NULL)
    {
	/*
	 * Try the entries in TEMPDIRNAMES to create the temp directory.
	 */
	for (i = 0; i < (int)(sizeof(tempdirs) / sizeof(char *)); ++i)
	{
# ifndef HAVE_MKDTEMP
	    size_t	itmplen;
	    long	nr;
	    long	off;
# endif

	    /* Expand $TMP, leave room for "/v1100000/999999999".
	     * Skip the directory check if the expansion fails. */
	    expand_env((char_u *)tempdirs[i], itmp, TEMPNAMELEN - 20);
	    if (itmp[0] != '$' && mch_isdir(itmp))
	    {
		/* directory exists */
		add_pathsep(itmp);

# ifdef HAVE_MKDTEMP
		{
#  if defined(UNIX) || defined(VMS)
		    /* Make sure the umask doesn't remove the executable bit.
		     * "repl" has been reported to use "177". */
		    mode_t	umask_save = umask(077);
#  endif
		    /* Leave room for filename */
		    STRCAT(itmp, "vXXXXXX");
		    if (mkdtemp((char *)itmp) != NULL)
			vim_settempdir(itmp);
#  if defined(UNIX) || defined(VMS)
		    (void)umask(umask_save);
#  endif
		}
# else
		/* Get an arbitrary number of up to 6 digits.  When it's
		 * unlikely that it already exists it will be faster,
		 * otherwise it doesn't matter.  The use of mkdir() avoids any
		 * security problems because of the predictable number. */
		nr = (mch_get_pid() + (long)time(NULL)) % 1000000L;
		itmplen = STRLEN(itmp);

		/* Try up to 10000 different values until we find a name that
		 * doesn't exist. */
		for (off = 0; off < 10000L; ++off)
		{
		    int		r;
#  if defined(UNIX) || defined(VMS)
		    mode_t	umask_save;
#  endif

		    sprintf((char *)itmp + itmplen, "v%ld", nr + off);
#  ifndef EEXIST
		    /* If mkdir() does not set errno to EEXIST, check for
		     * existing file here.  There is a race condition then,
		     * although it's fail-safe. */
		    if (mch_stat((char *)itmp, &st) >= 0)
			continue;
#  endif
#  if defined(UNIX) || defined(VMS)
		    /* Make sure the umask doesn't remove the executable bit.
		     * "repl" has been reported to use "177". */
		    umask_save = umask(077);
#  endif
		    r = vim_mkdir(itmp, 0700);
#  if defined(UNIX) || defined(VMS)
		    (void)umask(umask_save);
#  endif
		    if (r == 0)
		    {
			vim_settempdir(itmp);
			break;
		    }
#  ifdef EEXIST
		    /* If the mkdir() didn't fail because the file/dir exists,
		     * we probably can't create any dir here, try another
		     * place. */
		    if (errno != EEXIST)
#  endif
			break;
		}
# endif /* HAVE_MKDTEMP */
		if (vim_tempdir != NULL)
		    break;
	    }
	}
    }

    if (vim_tempdir != NULL)
    {
	/* There is no need to check if the file exists, because we own the
	 * directory and nobody else creates a file in it. */
	sprintf((char *)itmp, "%s%ld", vim_tempdir, temp_count++);
	return vim_strsave(itmp);
    }

    return NULL;

#else /* TEMPDIRNAMES */

# ifdef WIN3264
    char	szTempFile[_MAX_PATH + 1];
    char	buf4[4];
    char_u	*retval;
    char_u	*p;

    STRCPY(itmp, "");
    if (GetTempPath(_MAX_PATH, szTempFile) == 0)
    {
	szTempFile[0] = '.';	/* GetTempPath() failed, use current dir */
	szTempFile[1] = NUL;
    }
    strcpy(buf4, "VIM");
    buf4[2] = extra_char;   /* make it "VIa", "VIb", etc. */
    if (GetTempFileName(szTempFile, buf4, 0, (LPSTR)itmp) == 0)
	return NULL;
    if (!keep)
	/* GetTempFileName() will create the file, we don't want that */
	(void)DeleteFile((LPSTR)itmp);

    /* Backslashes in a temp file name cause problems when filtering with
     * "sh".  NOTE: This also checks 'shellcmdflag' to help those people who
     * didn't set 'shellslash'. */
    retval = vim_strsave(itmp);
    if (*p_shcf == '-' || p_ssl)
	for (p = retval; *p; ++p)
	    if (*p == '\\')
		*p = '/';
    return retval;

# else /* WIN3264 */

#  ifdef USE_TMPNAM
    char_u	*p;

    /* tmpnam() will make its own name */
    p = tmpnam((char *)itmp);
    if (p == NULL || *p == NUL)
	return NULL;
#  else
    char_u	*p;

#   ifdef VMS_TEMPNAM
    /* mktemp() is not working on VMS.  It seems to be
     * a do-nothing function. Therefore we use tempnam().
     */
    sprintf((char *)itmp, "VIM%c", extra_char);
    p = (char_u *)tempnam("tmp:", (char *)itmp);
    if (p != NULL)
    {
	/* VMS will use '.LIS' if we don't explicitly specify an extension,
	 * and VIM will then be unable to find the file later */
	STRCPY(itmp, p);
	STRCAT(itmp, ".txt");
	free(p);
    }
    else
	return NULL;
#   else
    STRCPY(itmp, TEMPNAME);
    if ((p = vim_strchr(itmp, '?')) != NULL)
	*p = extra_char;
    if (mktemp((char *)itmp) == NULL)
	return NULL;
#   endif
#  endif

    return vim_strsave(itmp);
# endif /* WIN3264 */
#endif /* TEMPDIRNAMES */
}


do_autocmd(char_u *arg_in, int forceit)
{
    char_u	*arg = arg_in;
    char_u	*pat;
    char_u	*envpat = NULL;
    char_u	*cmd;
    event_T	event;
    int		need_free = FALSE;
    int		nested = FALSE;
    int		group;

    if (*arg == '|')
    {
	arg = (char_u *)"";
	group = AUGROUP_ALL;	/* no argument, use all groups */
    }
    else
    {
	/*
	 * Check for a legal group name.  If not, use AUGROUP_ALL.
	 */
	group = au_get_grouparg(&arg);
	if (arg == NULL)	    /* out of memory */
	    return;
    }

    /*
     * Scan over the events.
     * If we find an illegal name, return here, don't do anything.
     */
    pat = find_end_event(arg, group != AUGROUP_ALL);
    if (pat == NULL)
	return;

    pat = skipwhite(pat);
    if (*pat == '|')
    {
	pat = (char_u *)"";
	cmd = (char_u *)"";
    }
    else
    {
	/*
	 * Scan over the pattern.  Put a NUL at the end.
	 */
	cmd = pat;
	while (*cmd && (!VIM_ISWHITE(*cmd) || cmd[-1] == '\\'))
	    cmd++;
	if (*cmd)
	    *cmd++ = NUL;

	/* Expand environment variables in the pattern.  Set 'shellslash', we want
	 * forward slashes here. */
	if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL)
	{
#ifdef BACKSLASH_IN_FILENAME
	    int	p_ssl_save = p_ssl;

	    p_ssl = TRUE;
#endif
	    envpat = expand_env_save(pat);
#ifdef BACKSLASH_IN_FILENAME
	    p_ssl = p_ssl_save;
#endif
	    if (envpat != NULL)
		pat = envpat;
	}

	/*
	 * Check for "nested" flag.
	 */
	cmd = skipwhite(cmd);
	if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 && VIM_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;
	}
    }

    /*
     * Print header when showing autocommands.
     */
    if (!forceit && *cmd == NUL)
    {
	/* Highlight title */
	MSG_PUTS_TITLE(_("\n--- Auto-Commands ---"));
    }

    /*
     * Loop over the events.
     */
    last_event = (event_T)-1;		/* for listing the event name */
    last_group = AUGROUP_ERROR;		/* for listing the group name */
    if (*arg == '*' || *arg == NUL || *arg == '|')
    {
	for (event = (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)
		break;
    }
    else
    {
	while (*arg && *arg != '|' && !VIM_ISWHITE(*arg))
	    if (do_autocmd_event(event_name2nr(arg, &arg), pat,
					nested,	cmd, forceit, group) == FAIL)
		break;
    }

    if (need_free)
	vim_free(cmd);
    vim_free(envpat);
}


au_exists(char_u *arg)
{
    char_u	*arg_save;
    char_u	*pattern = NULL;
    char_u	*event_name;
    char_u	*p;
    event_T	event;
    AutoPat	*ap;
    buf_T	*buflocal_buf = NULL;
    int		group;
    int		retval = FALSE;

    /* Make a copy so that we can change the '#' chars to a NUL. */
    arg_save = vim_strsave(arg);
    if (arg_save == NULL)
	return FALSE;
    p = vim_strchr(arg_save, '#');
    if (p != NULL)
	*p++ = NUL;

    /* First, look for an autocmd group name */
    group = au_find_group(arg_save);
    if (group == AUGROUP_ERROR)
    {
	/* Didn't match a group name, assume the first argument is an event. */
	group = AUGROUP_ALL;
	event_name = arg_save;
    }
    else
    {
	if (p == NULL)
	{
	    /* "Group": group name is present and it's recognized */
	    retval = TRUE;
	    goto theend;
	}

	/* Must be "Group#Event" or "Group#Event#pat". */
	event_name = p;
	p = vim_strchr(event_name, '#');
	if (p != NULL)
	    *p++ = NUL;	    /* "Group#Event#pat" */
    }

    pattern = p;	    /* "pattern" is NULL when there is no pattern */

    /* find the index (enum) for the event name */
    event = event_name2nr(event_name, &p);

    /* return FALSE if the event name is not recognized */
    if (event == NUM_EVENTS)
	goto theend;

    /* Find the first autocommand for this event.
     * If there isn't any, return FALSE;
     * If there is one and no pattern given, return TRUE; */
    ap = first_autopat[(int)event];
    if (ap == NULL)
	goto theend;

    /* if pattern is "<buffer>", special handling is needed which uses curbuf */
    /* for pattern "<buffer=N>, fnamecmp() will work fine */
    if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0)
	buflocal_buf = curbuf;

    /* Check if there is an autocommand with the given pattern. */
    for ( ; ap != NULL; ap = ap->next)
	/* only use a pattern when it has not been removed and has commands. */
	/* For buffer-local autocommands, fnamecmp() works fine. */
	if (ap->pat != NULL && ap->cmds != NULL
	    && (group == AUGROUP_ALL || ap->group == group)
	    && (pattern == NULL
		|| (buflocal_buf == NULL
		    ? fnamecmp(ap->pat, pattern) == 0
		    : ap->buflocal_nr == buflocal_buf->b_fnum)))
	{
	    retval = TRUE;
	    break;
	}

theend:
    vim_free(arg_save);
    return retval;
}


buf_modname(
    int	    shortname,		/* use 8.3 file name */
    char_u  *fname,
    char_u  *ext,
    int	    prepend_dot)	/* may prepend a '.' to file name */
{
    char_u	*retval;
    char_u	*s;
    char_u	*e;
    char_u	*ptr;
    int		fnamelen, extlen;

    extlen = (int)STRLEN(ext);

    /*
     * If there is no file name we must get the name of the current directory
     * (we need the full path in case :cd is used).
     */
    if (fname == NULL || *fname == NUL)
    {
	retval = alloc((unsigned)(MAXPATHL + extlen + 3));
	if (retval == NULL)
	    return NULL;
	if (mch_dirname(retval, MAXPATHL) == FAIL ||
				     (fnamelen = (int)STRLEN(retval)) == 0)
	{
	    vim_free(retval);
	    return NULL;
	}
	if (!after_pathsep(retval, retval + fnamelen))
	{
	    retval[fnamelen++] = PATHSEP;
	    retval[fnamelen] = NUL;
	}
	prepend_dot = FALSE;	    /* nothing to prepend a dot to */
    }
    else
    {
	fnamelen = (int)STRLEN(fname);
	retval = alloc((unsigned)(fnamelen + extlen + 3));
	if (retval == NULL)
	    return NULL;
	STRCPY(retval, fname);
#ifdef VMS
	vms_remove_version(retval); /* we do not need versions here */
#endif
    }

    /*
     * search backwards until we hit a '/', '\' or ':' replacing all '.'
     * by '_' for MSDOS or when shortname option set and ext starts with a dot.
     * Then truncate what is after the '/', '\' or ':' to 8 characters for
     * MSDOS and 26 characters for AMIGA, a lot more for UNIX.
     */
    for (ptr = retval + fnamelen; ptr > retval; MB_PTR_BACK(retval, ptr))
    {
	if (*ext == '.'
#ifdef USE_LONG_FNAME
		    && (!USE_LONG_FNAME || shortname)
#else
		    && shortname
#endif
								)
	    if (*ptr == '.')	/* replace '.' by '_' */
		*ptr = '_';
	if (vim_ispathsep(*ptr))
	{
	    ++ptr;
	    break;
	}
    }

    /* the file name has at most BASENAMELEN characters. */
    if (STRLEN(ptr) > (unsigned)BASENAMELEN)
	ptr[BASENAMELEN] = '\0';

    s = ptr + STRLEN(ptr);

    /*
     * For 8.3 file names we may have to reduce the length.
     */
#ifdef USE_LONG_FNAME
    if (!USE_LONG_FNAME || shortname)
#else
    if (shortname)
#endif
    {
	/*
	 * If there is no file name, or the file name ends in '/', and the
	 * extension starts with '.', put a '_' before the dot, because just
	 * ".ext" is invalid.
	 */
	if (fname == NULL || *fname == NUL
				   || vim_ispathsep(fname[STRLEN(fname) - 1]))
	{
	    if (*ext == '.')
		*s++ = '_';
	}
	/*
	 * If the extension starts with '.', truncate the base name at 8
	 * characters
	 */
	else if (*ext == '.')
	{
	    if ((size_t)(s - ptr) > (size_t)8)
	    {
		s = ptr + 8;
		*s = '\0';
	    }
	}
	/*
	 * If the extension doesn't start with '.', and the file name
	 * doesn't have an extension yet, append a '.'
	 */
	else if ((e = vim_strchr(ptr, '.')) == NULL)
	    *s++ = '.';
	/*
	 * If the extension doesn't start with '.', and there already is an
	 * extension, it may need to be truncated
	 */
	else if ((int)STRLEN(e) + extlen > 4)
	    s = e + 4 - extlen;
    }
#if defined(USE_LONG_FNAME) || defined(WIN3264)
    /*
     * If there is no file name, and the extension starts with '.', put a
     * '_' before the dot, because just ".ext" may be invalid if it's on a
     * FAT partition, and on HPFS it doesn't matter.
     */
    else if ((fname == NULL || *fname == NUL) && *ext == '.')
	*s++ = '_';
#endif

    /*
     * Append the extension.
     * ext can start with '.' and cannot exceed 3 more characters.
     */
    STRCPY(s, ext);

    /*
     * Prepend the dot.
     */
    if (prepend_dot && !shortname && *(e = gettail(retval)) != '.'
#ifdef USE_LONG_FNAME
	    && USE_LONG_FNAME
#endif
				)
    {
	STRMOVE(e + 1, e);
	*e = '.';
    }

    /*
     * Check that, after appending the extension, the file name is really
     * different.
     */
    if (fname != NULL && STRCMP(fname, retval) == 0)
    {
	/* we search for a character that can be replaced by '_' */
	while (--s >= ptr)
	{
	    if (*s != '_')
	    {
		*s = '_';
		break;
	    }
	}
	if (s < ptr)	/* fname was "________.<ext>", how tricky! */
	    *ptr = 'v';
    }
    return retval;
}


au_remove_cmds(AutoPat *ap)
{
    AutoCmd *ac;

    for (ac = ap->cmds; ac != NULL; ac = ac->next)
    {
	vim_free(ac->cmd);
	ac->cmd = NULL;
    }
    au_need_clean = TRUE;
}


au_del_group(char_u *name)
{
    int	    i;

    i = au_find_group(name);
    if (i == AUGROUP_ERROR)	/* the group doesn't exist */
	EMSG2(_("E367: No such group: \"%s\""), name);
    else if (i == current_augroup)
	EMSG(_("E936: Cannot delete the current group"));
    else
    {
	event_T	event;
	AutoPat	*ap;
	int	in_use = FALSE;

	for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
					    event = (event_T)((int)event + 1))
	{
	    for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
		if (ap->group == i && ap->pat != NULL)
		{
		    give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
		    in_use = TRUE;
		    event = NUM_EVENTS;
		    break;
		}
	}
	vim_free(AUGROUP_NAME(i));
	if (in_use)
	{
	    AUGROUP_NAME(i) = get_deleted_augroup();
	}
	else
	    AUGROUP_NAME(i) = NULL;
    }
}


get_fio_flags(char_u *ptr)
{
    int		prop;

    if (*ptr == NUL)
	ptr = p_enc;

    prop = enc_canon_props(ptr);
    if (prop & ENC_UNICODE)
    {
	if (prop & ENC_2BYTE)
	{
	    if (prop & ENC_ENDIAN_L)
		return FIO_UCS2 | FIO_ENDIAN_L;
	    return FIO_UCS2;
	}
	if (prop & ENC_4BYTE)
	{
	    if (prop & ENC_ENDIAN_L)
		return FIO_UCS4 | FIO_ENDIAN_L;
	    return FIO_UCS4;
	}
	if (prop & ENC_2WORD)
	{
	    if (prop & ENC_ENDIAN_L)
		return FIO_UTF16 | FIO_ENDIAN_L;
	    return FIO_UTF16;
	}
	return FIO_UTF8;
    }
    if (prop & ENC_LATIN1)
	return FIO_LATIN1;
    /* must be ENC_DBCS, requires iconv() */
    return 0;
}


shorten_fnames(int force)
{
    char_u	dirname[MAXPATHL];
    buf_T	*buf;
    char_u	*p;

    mch_dirname(dirname, MAXPATHL);
    FOR_ALL_BUFFERS(buf)
    {
	if (buf->b_fname != NULL
#ifdef FEAT_QUICKFIX
		&& !bt_nofile(buf)
#endif
		&& !path_with_url(buf->b_fname)
		&& (force
		    || buf->b_sfname == NULL
		    || mch_isFullName(buf->b_sfname)))
	{
	    vim_free(buf->b_sfname);
	    buf->b_sfname = NULL;
	    p = shorten_fname(buf->b_ffname, dirname);
	    if (p != NULL)
	    {
		buf->b_sfname = vim_strsave(p);
		buf->b_fname = buf->b_sfname;
	    }
	    if (p == NULL || buf->b_fname == NULL)
		buf->b_fname = buf->b_ffname;
	}

	/* Always make the swap file name a full path, a "nofile" buffer may
	 * also have a swap file. */
	mf_fullname(buf->b_ml.ml_mfp);
    }
    status_redraw_all();
    redraw_tabline = TRUE;
}


au_new_group(char_u *name)
{
    int		i;

    i = au_find_group(name);
    if (i == AUGROUP_ERROR)	/* the group doesn't exist yet, add it */
    {
	/* First try using a free entry. */
	for (i = 0; i < augroups.ga_len; ++i)
	    if (AUGROUP_NAME(i) == NULL)
		break;
	if (i == augroups.ga_len && ga_grow(&augroups, 1) == FAIL)
	    return AUGROUP_ERROR;

	AUGROUP_NAME(i) = vim_strsave(name);
	if (AUGROUP_NAME(i) == NULL)
	    return AUGROUP_ERROR;
	if (i == augroups.ga_len)
	    ++augroups.ga_len;
    }

    return i;
}


set_rw_fname(char_u *fname, char_u *sfname)
{
#ifdef FEAT_AUTOCMD
    buf_T	*buf = curbuf;

    /* It's like the unnamed buffer is deleted.... */
    if (curbuf->b_p_bl)
	apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
    apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf);
# ifdef FEAT_EVAL
    if (aborting())	    /* autocmds may abort script processing */
	return FAIL;
# endif
    if (curbuf != buf)
    {
	/* We are in another buffer now, don't do the renaming. */
	EMSG(_(e_auchangedbuf));
	return FAIL;
    }
#endif

    if (setfname(curbuf, fname, sfname, FALSE) == OK)
	curbuf->b_flags |= BF_NOTEDITED;

#ifdef FEAT_AUTOCMD
    /* ....and a new named one is created */
    apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, curbuf);
    if (curbuf->b_p_bl)
	apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf);
# ifdef FEAT_EVAL
    if (aborting())	    /* autocmds may abort script processing */
	return FAIL;
# endif

    /* 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, NULL);
	do_modelines(0);
    }
#endif

    return OK;
}


match_file_list(char_u *list, char_u *sfname, char_u *ffname)
{
    char_u	buf[100];
    char_u	*tail;
    char_u	*regpat;
    char	allow_dirs;
    int		match;
    char_u	*p;

    tail = gettail(sfname);

    /* try all patterns in 'wildignore' */
    p = list;
    while (*p)
    {
	copy_option_part(&p, buf, 100, ",");
	regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, FALSE);
	if (regpat == NULL)
	    break;
	match = match_file_pat(regpat, NULL, ffname, sfname,
						       tail, (int)allow_dirs);
	vim_free(regpat);
	if (match)
	    return TRUE;
    }
    return FALSE;
}


check_for_bom(
    char_u	*p,
    long	size,
    int		*lenp,
    int		flags)
{
    char	*name = NULL;
    int		len = 2;

    if (p[0] == 0xef && p[1] == 0xbb && size >= 3 && p[2] == 0xbf
	    && (flags == FIO_ALL || flags == FIO_UTF8 || flags == 0))
    {
	name = "utf-8";		/* EF BB BF */
	len = 3;
    }
    else if (p[0] == 0xff && p[1] == 0xfe)
    {
	if (size >= 4 && p[2] == 0 && p[3] == 0
	    && (flags == FIO_ALL || flags == (FIO_UCS4 | FIO_ENDIAN_L)))
	{
	    name = "ucs-4le";	/* FF FE 00 00 */
	    len = 4;
	}
	else if (flags == (FIO_UCS2 | FIO_ENDIAN_L))
	    name = "ucs-2le";	/* FF FE */
	else if (flags == FIO_ALL || flags == (FIO_UTF16 | FIO_ENDIAN_L))
	    /* utf-16le is preferred, it also works for ucs-2le text */
	    name = "utf-16le";	/* FF FE */
    }
    else if (p[0] == 0xfe && p[1] == 0xff
	    && (flags == FIO_ALL || flags == FIO_UCS2 || flags == FIO_UTF16))
    {
	/* Default to utf-16, it works also for ucs-2 text. */
	if (flags == FIO_UCS2)
	    name = "ucs-2";	/* FE FF */
	else
	    name = "utf-16";	/* FE FF */
    }
    else if (size >= 4 && p[0] == 0 && p[1] == 0 && p[2] == 0xfe
	    && p[3] == 0xff && (flags == FIO_ALL || flags == FIO_UCS4))
    {
	name = "ucs-4";		/* 00 00 FE FF */
	len = 4;
    }

    *lenp = len;
    return (char_u *)name;
}


buf_write(
    buf_T	    *buf,
    char_u	    *fname,
    char_u	    *sfname,
    linenr_T	    start,
    linenr_T	    end,
    exarg_T	    *eap,		/* for forced 'ff' and 'fenc', can be
					   NULL! */
    int		    append,		/* append to the file */
    int		    forceit,
    int		    reset_changed,
    int		    filtering)
{
    int		    fd;
    char_u	    *backup = NULL;
    int		    backup_copy = FALSE; /* copy the original file? */
    int		    dobackup;
    char_u	    *ffname;
    char_u	    *wfname = NULL;	/* name of file to write to */
    char_u	    *s;
    char_u	    *ptr;
    char_u	    c;
    int		    len;
    linenr_T	    lnum;
    long	    nchars;
    char_u	    *errmsg = NULL;
    int		    errmsg_allocated = FALSE;
    char_u	    *errnum = NULL;
    char_u	    *buffer;
    char_u	    smallbuf[SMBUFSIZE];
    char_u	    *backup_ext;
    int		    bufsize;
    long	    perm;		    /* file permissions */
    int		    retval = OK;
    int		    newfile = FALSE;	    /* TRUE if file doesn't exist yet */
    int		    msg_save = msg_scroll;
    int		    overwriting;	    /* TRUE if writing over original */
    int		    no_eol = FALSE;	    /* no end-of-line written */
    int		    device = FALSE;	    /* writing to a device */
    stat_T	    st_old;
    int		    prev_got_int = got_int;
    int		    checking_conversion;
    int		    file_readonly = FALSE;  /* overwritten file is read-only */
    static char	    *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')";
#if defined(UNIX)			    /*XXX fix me sometime? */
    int		    made_writable = FALSE;  /* 'w' bit has been set */
#endif
					/* writing everything */
    int		    whole = (start == 1 && end == buf->b_ml.ml_line_count);
#ifdef FEAT_AUTOCMD
    linenr_T	    old_line_count = buf->b_ml.ml_line_count;
#endif
    int		    attr;
    int		    fileformat;
    int		    write_bin;
    struct bw_info  write_info;		/* info for buf_write_bytes() */
#ifdef FEAT_MBYTE
    int		    converted = FALSE;
    int		    notconverted = FALSE;
    char_u	    *fenc;		/* effective 'fileencoding' */
    char_u	    *fenc_tofree = NULL; /* allocated "fenc" */
#endif
#ifdef HAS_BW_FLAGS
    int		    wb_flags = 0;
#endif
#ifdef HAVE_ACL
    vim_acl_T	    acl = NULL;		/* ACL copied from original file to
					   backup or new file */
#endif
#ifdef FEAT_PERSISTENT_UNDO
    int		    write_undo_file = FALSE;
    context_sha256_T sha_ctx;
#endif
    unsigned int    bkc = get_bkc_value(buf);

    if (fname == NULL || *fname == NUL)	/* safety check */
	return FAIL;
    if (buf->b_ml.ml_mfp == NULL)
    {
	/* This can happen during startup when there is a stray "w" in the
	 * vimrc file. */
	EMSG(_(e_emptybuf));
	return FAIL;
    }

    /*
     * Disallow writing from .exrc and .vimrc in current directory for
     * security reasons.
     */
    if (check_secure())
	return FAIL;

    /* Avoid a crash for a long name. */
    if (STRLEN(fname) >= MAXPATHL)
    {
	EMSG(_(e_longname));
	return FAIL;
    }

#ifdef FEAT_MBYTE
    /* must init bw_conv_buf and bw_iconv_fd before jumping to "fail" */
    write_info.bw_conv_buf = NULL;
    write_info.bw_conv_error = FALSE;
    write_info.bw_conv_error_lnum = 0;
    write_info.bw_restlen = 0;
# ifdef USE_ICONV
    write_info.bw_iconv_fd = (iconv_t)-1;
# endif
#endif
#ifdef FEAT_CRYPT
    write_info.bw_buffer = buf;
#endif

    /* After writing a file changedtick changes but we don't want to display
     * the line. */
    ex_no_reprint = TRUE;

    /*
     * If there is no file name yet, use the one for the written file.
     * BF_NOTEDITED is set to reflect this (in case the write fails).
     * Don't do this when the write is for a filter command.
     * Don't do this when appending.
     * Only do this when 'cpoptions' contains the 'F' flag.
     */
    if (buf->b_ffname == NULL
	    && reset_changed
	    && whole
	    && buf == curbuf
#ifdef FEAT_QUICKFIX
	    && !bt_nofile(buf)
#endif
	    && !filtering
	    && (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL)
	    && vim_strchr(p_cpo, CPO_FNAMEW) != NULL)
    {
	if (set_rw_fname(fname, sfname) == FAIL)
	    return FAIL;
	buf = curbuf;	    /* just in case autocmds made "buf" invalid */
    }

    if (sfname == NULL)
	sfname = fname;
    /*
     * For Unix: Use the short file name whenever possible.
     * Avoids problems with networks and when directory names are changed.
     * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to
     * another directory, which we don't detect
     */
    ffname = fname;			    /* remember full fname */
#ifdef UNIX
    fname = sfname;
#endif

    if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0)
	overwriting = TRUE;
    else
	overwriting = FALSE;

    if (exiting)
	settmode(TMODE_COOK);	    /* when exiting allow typeahead now */

    ++no_wait_return;		    /* don't wait for return yet */

    /*
     * Set '[ and '] marks to the lines to be written.
     */
    buf->b_op_start.lnum = start;
    buf->b_op_start.col = 0;
    buf->b_op_end.lnum = end;
    buf->b_op_end.col = 0;

#ifdef FEAT_AUTOCMD
    {
	aco_save_T	aco;
	int		buf_ffname = FALSE;
	int		buf_sfname = FALSE;
	int		buf_fname_f = FALSE;
	int		buf_fname_s = FALSE;
	int		did_cmd = FALSE;
	int		nofile_err = FALSE;
	int		empty_memline = (buf->b_ml.ml_mfp == NULL);
	bufref_T	bufref;

	/*
	 * Apply PRE autocommands.
	 * Set curbuf to the buffer to be written.
	 * Careful: The autocommands may call buf_write() recursively!
	 */
	if (ffname == buf->b_ffname)
	    buf_ffname = TRUE;
	if (sfname == buf->b_sfname)
	    buf_sfname = TRUE;
	if (fname == buf->b_ffname)
	    buf_fname_f = TRUE;
	if (fname == buf->b_sfname)
	    buf_fname_s = TRUE;

	/* set curwin/curbuf to buf and save a few things */
	aucmd_prepbuf(&aco, buf);
	set_bufref(&bufref, buf);

	if (append)
	{
	    if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD,
					 sfname, sfname, FALSE, curbuf, eap)))
	    {
#ifdef FEAT_QUICKFIX
		if (overwriting && bt_nofile(curbuf))
		    nofile_err = TRUE;
		else
#endif
		    apply_autocmds_exarg(EVENT_FILEAPPENDPRE,
					  sfname, sfname, FALSE, curbuf, eap);
	    }
	}
	else if (filtering)
	{
	    apply_autocmds_exarg(EVENT_FILTERWRITEPRE,
					    NULL, sfname, FALSE, curbuf, eap);
	}
	else if (reset_changed && whole)
	{
	    int was_changed = curbufIsChanged();

	    did_cmd = apply_autocmds_exarg(EVENT_BUFWRITECMD,
					  sfname, sfname, FALSE, curbuf, eap);
	    if (did_cmd)
	    {
		if (was_changed && !curbufIsChanged())
		{
		    /* Written everything correctly and BufWriteCmd has reset
		     * 'modified': Correct the undo information so that an
		     * undo now sets 'modified'. */
		    u_unchanged(curbuf);
		    u_update_save_nr(curbuf);
		}
	    }
	    else
	    {
#ifdef FEAT_QUICKFIX
		if (overwriting && bt_nofile(curbuf))
		    nofile_err = TRUE;
		else
#endif
		    apply_autocmds_exarg(EVENT_BUFWRITEPRE,
					  sfname, sfname, FALSE, curbuf, eap);
	    }
	}
	else
	{
	    if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD,
					 sfname, sfname, FALSE, curbuf, eap)))
	    {
#ifdef FEAT_QUICKFIX
		if (overwriting && bt_nofile(curbuf))
		    nofile_err = TRUE;
		else
#endif
		    apply_autocmds_exarg(EVENT_FILEWRITEPRE,
					  sfname, sfname, FALSE, curbuf, eap);
	    }
	}

	/* restore curwin/curbuf and a few other things */
	aucmd_restbuf(&aco);

	/*
	 * In three situations we return here and don't write the file:
	 * 1. the autocommands deleted or unloaded the buffer.
	 * 2. The autocommands abort script processing.
	 * 3. If one of the "Cmd" autocommands was executed.
	 */
	if (!bufref_valid(&bufref))
	    buf = NULL;
	if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline)
				       || did_cmd || nofile_err
#ifdef FEAT_EVAL
				       || aborting()
#endif
				       )
	{
	    --no_wait_return;
	    msg_scroll = msg_save;
	    if (nofile_err)
		EMSG(_("E676: No matching autocommands for acwrite buffer"));

	    if (nofile_err
#ifdef FEAT_EVAL
		    || aborting()
#endif
		    )
		/* An aborting error, interrupt or exception in the
		 * autocommands. */
		return FAIL;
	    if (did_cmd)
	    {
		if (buf == NULL)
		    /* The buffer was deleted.  We assume it was written
		     * (can't retry anyway). */
		    return OK;
		if (overwriting)
		{
		    /* Assume the buffer was written, update the timestamp. */
		    ml_timestamp(buf);
		    if (append)
			buf->b_flags &= ~BF_NEW;
		    else
			buf->b_flags &= ~BF_WRITE_MASK;
		}
		if (reset_changed && buf->b_changed && !append
			&& (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL))
		    /* Buffer still changed, the autocommands didn't work
		     * properly. */
		    return FAIL;
		return OK;
	    }
#ifdef FEAT_EVAL
	    if (!aborting())
#endif
		EMSG(_("E203: Autocommands deleted or unloaded buffer to be written"));
	    return FAIL;
	}

	/*
	 * The autocommands may have changed the number of lines in the file.
	 * When writing the whole file, adjust the end.
	 * When writing part of the file, assume that the autocommands only
	 * changed the number of lines that are to be written (tricky!).
	 */
	if (buf->b_ml.ml_line_count != old_line_count)
	{
	    if (whole)						/* write all */
		end = buf->b_ml.ml_line_count;
	    else if (buf->b_ml.ml_line_count > old_line_count)	/* more lines */
		end += buf->b_ml.ml_line_count - old_line_count;
	    else						/* less lines */
	    {
		end -= old_line_count - buf->b_ml.ml_line_count;
		if (end < start)
		{
		    --no_wait_return;
		    msg_scroll = msg_save;
		    EMSG(_("E204: Autocommand changed number of lines in unexpected way"));
		    return FAIL;
		}
	    }
	}

	/*
	 * The autocommands may have changed the name of the buffer, which may
	 * be kept in fname, ffname and sfname.
	 */
	if (buf_ffname)
	    ffname = buf->b_ffname;
	if (buf_sfname)
	    sfname = buf->b_sfname;
	if (buf_fname_f)
	    fname = buf->b_ffname;
	if (buf_fname_s)
	    fname = buf->b_sfname;
    }
#endif

#ifdef FEAT_NETBEANS_INTG
    if (netbeans_active() && isNetbeansBuffer(buf))
    {
	if (whole)
	{
	    /*
	     * b_changed can be 0 after an undo, but we still need to write
	     * the buffer to NetBeans.
	     */
	    if (buf->b_changed || isNetbeansModified(buf))
	    {
		--no_wait_return;		/* may wait for return now */
		msg_scroll = msg_save;
		netbeans_save_buffer(buf);	/* no error checking... */
		return retval;
	    }
	    else
	    {
		errnum = (char_u *)"E656: ";
		errmsg = (char_u *)_("NetBeans disallows writes of unmodified buffers");
		buffer = NULL;
		goto fail;
	    }
	}
	else
	{
	    errnum = (char_u *)"E657: ";
	    errmsg = (char_u *)_("Partial writes disallowed for NetBeans buffers");
	    buffer = NULL;
	    goto fail;
	}
    }
#endif

    if (shortmess(SHM_OVER) && !exiting)
	msg_scroll = FALSE;	    /* overwrite previous file message */
    else
	msg_scroll = TRUE;	    /* don't overwrite previous file message */
    if (!filtering)
	filemess(buf,
#ifndef UNIX
		sfname,
#else
		fname,
#endif
		    (char_u *)"", 0);	/* show that we are busy */
    msg_scroll = FALSE;		    /* always overwrite the file message now */

    buffer = alloc(BUFSIZE);
    if (buffer == NULL)		    /* can't allocate big buffer, use small
				     * one (to be able to write when out of
				     * memory) */
    {
	buffer = smallbuf;
	bufsize = SMBUFSIZE;
    }
    else
	bufsize = BUFSIZE;

    /*
     * Get information about original file (if there is one).
     */
#if defined(UNIX)
    st_old.st_dev = 0;
    st_old.st_ino = 0;
    perm = -1;
    if (mch_stat((char *)fname, &st_old) < 0)
	newfile = TRUE;
    else
    {
	perm = st_old.st_mode;
	if (!S_ISREG(st_old.st_mode))		/* not a file */
	{
	    if (S_ISDIR(st_old.st_mode))
	    {
		errnum = (char_u *)"E502: ";
		errmsg = (char_u *)_("is a directory");
		goto fail;
	    }
	    if (mch_nodetype(fname) != NODE_WRITABLE)
	    {
		errnum = (char_u *)"E503: ";
		errmsg = (char_u *)_("is not a file or writable device");
		goto fail;
	    }
	    /* It's a device of some kind (or a fifo) which we can write to
	     * but for which we can't make a backup. */
	    device = TRUE;
	    newfile = TRUE;
	    perm = -1;
	}
    }
#else /* !UNIX */
    /*
     * Check for a writable device name.
     */
    c = mch_nodetype(fname);
    if (c == NODE_OTHER)
    {
	errnum = (char_u *)"E503: ";
	errmsg = (char_u *)_("is not a file or writable device");
	goto fail;
    }
    if (c == NODE_WRITABLE)
    {
# if defined(MSWIN)
	/* MS-Windows allows opening a device, but we will probably get stuck
	 * trying to write to it.  */
	if (!p_odev)
	{
	    errnum = (char_u *)"E796: ";
	    errmsg = (char_u *)_("writing to device disabled with 'opendevice' option");
	    goto fail;
	}
# endif
	device = TRUE;
	newfile = TRUE;
	perm = -1;
    }
    else
    {
	perm = mch_getperm(fname);
	if (perm < 0)
	    newfile = TRUE;
	else if (mch_isdir(fname))
	{
	    errnum = (char_u *)"E502: ";
	    errmsg = (char_u *)_("is a directory");
	    goto fail;
	}
	if (overwriting)
	    (void)mch_stat((char *)fname, &st_old);
    }
#endif /* !UNIX */

    if (!device && !newfile)
    {
	/*
	 * Check if the file is really writable (when renaming the file to
	 * make a backup we won't discover it later).
	 */
	file_readonly = check_file_readonly(fname, (int)perm);

	if (!forceit && file_readonly)
	{
	    if (vim_strchr(p_cpo, CPO_FWRITE) != NULL)
	    {
		errnum = (char_u *)"E504: ";
		errmsg = (char_u *)_(err_readonly);
	    }
	    else
	    {
		errnum = (char_u *)"E505: ";
		errmsg = (char_u *)_("is read-only (add ! to override)");
	    }
	    goto fail;
	}

	/*
	 * Check if the timestamp hasn't changed since reading the file.
	 */
	if (overwriting)
	{
	    retval = check_mtime(buf, &st_old);
	    if (retval == FAIL)
		goto fail;
	}
    }

#ifdef HAVE_ACL
    /*
     * For systems that support ACL: get the ACL from the original file.
     */
    if (!newfile)
	acl = mch_get_acl(fname);
#endif

    /*
     * If 'backupskip' is not empty, don't make a backup for some files.
     */
    dobackup = (p_wb || p_bk || *p_pm != NUL);
#ifdef FEAT_WILDIGN
    if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname))
	dobackup = FALSE;
#endif

    /*
     * Save the value of got_int and reset it.  We don't want a previous
     * interruption cancel writing, only hitting CTRL-C while writing should
     * abort it.
     */
    prev_got_int = got_int;
    got_int = FALSE;

    /* Mark the buffer as 'being saved' to prevent changed buffer warnings */
    buf->b_saving = TRUE;

    /*
     * If we are not appending or filtering, the file exists, and the
     * 'writebackup', 'backup' or 'patchmode' option is set, need a backup.
     * When 'patchmode' is set also make a backup when appending.
     *
     * Do not make any backup, if 'writebackup' and 'backup' are both switched
     * off.  This helps when editing large files on almost-full disks.
     */
    if (!(append && *p_pm == NUL) && !filtering && perm >= 0 && dobackup)
    {
#if defined(UNIX) || defined(WIN32)
	stat_T	    st;
#endif

	if ((bkc & BKC_YES) || append)	/* "yes" */
	    backup_copy = TRUE;
#if defined(UNIX) || defined(WIN32)
	else if ((bkc & BKC_AUTO))	/* "auto" */
	{
	    int		i;

# ifdef UNIX
	    /*
	     * Don't rename the file when:
	     * - it's a hard link
	     * - it's a symbolic link
	     * - we don't have write permission in the directory
	     * - we can't set the owner/group of the new file
	     */
	    if (st_old.st_nlink > 1
		    || mch_lstat((char *)fname, &st) < 0
		    || st.st_dev != st_old.st_dev
		    || st.st_ino != st_old.st_ino
#  ifndef HAVE_FCHOWN
		    || st.st_uid != st_old.st_uid
		    || st.st_gid != st_old.st_gid
#  endif
		    )
		backup_copy = TRUE;
	    else
# else
#  ifdef WIN32
	    /* On NTFS file systems hard links are possible. */
	    if (mch_is_linked(fname))
		backup_copy = TRUE;
	    else
#  endif
# endif
	    {
		/*
		 * Check if we can create a file and set the owner/group to
		 * the ones from the original file.
		 * First find a file name that doesn't exist yet (use some
		 * arbitrary numbers).
		 */
		STRCPY(IObuff, fname);
		for (i = 4913; ; i += 123)
		{
		    sprintf((char *)gettail(IObuff), "%d", i);
		    if (mch_lstat((char *)IObuff, &st) < 0)
			break;
		}
		fd = mch_open((char *)IObuff,
				    O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
		if (fd < 0)	/* can't write in directory */
		    backup_copy = TRUE;
		else
		{
# ifdef UNIX
#  ifdef HAVE_FCHOWN
		    ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
#  endif
		    if (mch_stat((char *)IObuff, &st) < 0
			    || st.st_uid != st_old.st_uid
			    || st.st_gid != st_old.st_gid
			    || (long)st.st_mode != perm)
			backup_copy = TRUE;
# endif
		    /* Close the file before removing it, on MS-Windows we
		     * can't delete an open file. */
		    close(fd);
		    mch_remove(IObuff);
# ifdef MSWIN
		    /* MS-Windows may trigger a virus scanner to open the
		     * file, we can't delete it then.  Keep trying for half a
		     * second. */
		    {
			int try;

			for (try = 0; try < 10; ++try)
			{
			    if (mch_lstat((char *)IObuff, &st) < 0)
				break;
			    ui_delay(50L, TRUE);  /* wait 50 msec */
			    mch_remove(IObuff);
			}
		    }
# endif
		}
	    }
	}

	/*
	 * Break symlinks and/or hardlinks if we've been asked to.
	 */
	if ((bkc & BKC_BREAKSYMLINK) || (bkc & BKC_BREAKHARDLINK))
	{
# ifdef UNIX
	    int	lstat_res;

	    lstat_res = mch_lstat((char *)fname, &st);

	    /* Symlinks. */
	    if ((bkc & BKC_BREAKSYMLINK)
		    && lstat_res == 0
		    && st.st_ino != st_old.st_ino)
		backup_copy = FALSE;

	    /* Hardlinks. */
	    if ((bkc & BKC_BREAKHARDLINK)
		    && st_old.st_nlink > 1
		    && (lstat_res != 0 || st.st_ino == st_old.st_ino))
		backup_copy = FALSE;
# else
#  if defined(WIN32)
	    /* Symlinks. */
	    if ((bkc & BKC_BREAKSYMLINK) && mch_is_symbolic_link(fname))
		backup_copy = FALSE;

	    /* Hardlinks. */
	    if ((bkc & BKC_BREAKHARDLINK) && mch_is_hard_link(fname))
		backup_copy = FALSE;
#  endif
# endif
	}

#endif

	/* make sure we have a valid backup extension to use */
	if (*p_bex == NUL)
	    backup_ext = (char_u *)".bak";
	else
	    backup_ext = p_bex;

	if (backup_copy
		&& (fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
	{
	    int		bfd;
	    char_u	*copybuf, *wp;
	    int		some_error = FALSE;
	    stat_T	st_new;
	    char_u	*dirp;
	    char_u	*rootname;
#if defined(UNIX)
	    int		did_set_shortname;
#endif

	    copybuf = alloc(BUFSIZE + 1);
	    if (copybuf == NULL)
	    {
		some_error = TRUE;	    /* out of memory */
		goto nobackup;
	    }

	    /*
	     * Try to make the backup in each directory in the 'bdir' option.
	     *
	     * Unix semantics has it, that we may have a writable file,
	     * that cannot be recreated with a simple open(..., O_CREAT, ) e.g:
	     *  - the directory is not writable,
	     *  - the file may be a symbolic link,
	     *  - the file may belong to another user/group, etc.
	     *
	     * For these reasons, the existing writable file must be truncated
	     * and reused. Creation of a backup COPY will be attempted.
	     */
	    dirp = p_bdir;
	    while (*dirp)
	    {
#ifdef UNIX
		st_new.st_ino = 0;
		st_new.st_dev = 0;
		st_new.st_gid = 0;
#endif

		/*
		 * Isolate one directory name, using an entry in 'bdir'.
		 */
		(void)copy_option_part(&dirp, copybuf, BUFSIZE, ",");
		rootname = get_file_in_dir(fname, copybuf);
		if (rootname == NULL)
		{
		    some_error = TRUE;	    /* out of memory */
		    goto nobackup;
		}

#if defined(UNIX)
		did_set_shortname = FALSE;
#endif

		/*
		 * May try twice if 'shortname' not set.
		 */
		for (;;)
		{
		    /*
		     * Make backup file name.
		     */
		    backup = buf_modname((buf->b_p_sn || buf->b_shortname),
						 rootname, backup_ext, FALSE);
		    if (backup == NULL)
		    {
			vim_free(rootname);
			some_error = TRUE;		/* out of memory */
			goto nobackup;
		    }

		    /*
		     * Check if backup file already exists.
		     */
		    if (mch_stat((char *)backup, &st_new) >= 0)
		    {
#ifdef UNIX
			/*
			 * Check if backup file is same as original file.
			 * May happen when modname() gave the same file back.
			 * E.g. silly link, or file name-length reached.
			 * If we don't check here, we either ruin the file
			 * when copying or erase it after writing. jw.
			 */
			if (st_new.st_dev == st_old.st_dev
					    && st_new.st_ino == st_old.st_ino)
			{
			    vim_free(backup);
			    backup = NULL;	/* no backup file to delete */
			    /*
			     * may try again with 'shortname' set
			     */
			    if (!(buf->b_shortname || buf->b_p_sn))
			    {
				buf->b_shortname = TRUE;
				did_set_shortname = TRUE;
				continue;
			    }
				/* setting shortname didn't help */
			    if (did_set_shortname)
				buf->b_shortname = FALSE;
			    break;
			}
#endif

			/*
			 * If we are not going to keep the backup file, don't
			 * delete an existing one, try to use another name.
			 * Change one character, just before the extension.
			 */
			if (!p_bk)
			{
			    wp = backup + STRLEN(backup) - 1
							 - STRLEN(backup_ext);
			    if (wp < backup)	/* empty file name ??? */
				wp = backup;
			    *wp = 'z';
			    while (*wp > 'a'
				    && mch_stat((char *)backup, &st_new) >= 0)
				--*wp;
			    /* They all exist??? Must be something wrong. */
			    if (*wp == 'a')
			    {
				vim_free(backup);
				backup = NULL;
			    }
			}
		    }
		    break;
		}
		vim_free(rootname);

		/*
		 * Try to create the backup file
		 */
		if (backup != NULL)
		{
		    /* remove old backup, if present */
		    mch_remove(backup);
		    /* Open with O_EXCL to avoid the file being created while
		     * we were sleeping (symlink hacker attack?) */
		    bfd = mch_open((char *)backup,
				O_WRONLY|O_CREAT|O_EXTRA|O_EXCL|O_NOFOLLOW,
								 perm & 0777);
		    if (bfd < 0)
		    {
			vim_free(backup);
			backup = NULL;
		    }
		    else
		    {
			/* set file protection same as original file, but
			 * strip s-bit */
			(void)mch_setperm(backup, perm & 0777);

#ifdef UNIX
			/*
			 * Try to set the group of the backup same as the
			 * original file. If this fails, set the protection
			 * bits for the group same as the protection bits for
			 * others.
			 */
			if (st_new.st_gid != st_old.st_gid
# ifdef HAVE_FCHOWN  /* sequent-ptx lacks fchown() */
				&& fchown(bfd, (uid_t)-1, st_old.st_gid) != 0
# endif
						)
			    mch_setperm(backup,
					  (perm & 0707) | ((perm & 07) << 3));
# if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
			mch_copy_sec(fname, backup);
# endif
#endif

			/*
			 * copy the file.
			 */
			write_info.bw_fd = bfd;
			write_info.bw_buf = copybuf;
#ifdef HAS_BW_FLAGS
			write_info.bw_flags = FIO_NOCONVERT;
#endif
			while ((write_info.bw_len = read_eintr(fd, copybuf,
								BUFSIZE)) > 0)
			{
			    if (buf_write_bytes(&write_info) == FAIL)
			    {
				errmsg = (char_u *)_("E506: Can't write to backup file (add ! to override)");
				break;
			    }
			    ui_breakcheck();
			    if (got_int)
			    {
				errmsg = (char_u *)_(e_interr);
				break;
			    }
			}

			if (close(bfd) < 0 && errmsg == NULL)
			    errmsg = (char_u *)_("E507: Close error for backup file (add ! to override)");
			if (write_info.bw_len < 0)
			    errmsg = (char_u *)_("E508: Can't read file for backup (add ! to override)");
#ifdef UNIX
			set_file_time(backup, st_old.st_atime, st_old.st_mtime);
#endif
#ifdef HAVE_ACL
			mch_set_acl(backup, acl);
#endif
#if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
			mch_copy_sec(fname, backup);
#endif
			break;
		    }
		}
	    }
    nobackup:
	    close(fd);		/* ignore errors for closing read file */
	    vim_free(copybuf);

	    if (backup == NULL && errmsg == NULL)
		errmsg = (char_u *)_("E509: Cannot create backup file (add ! to override)");
	    /* ignore errors when forceit is TRUE */
	    if ((some_error || errmsg != NULL) && !forceit)
	    {
		retval = FAIL;
		goto fail;
	    }
	    errmsg = NULL;
	}
	else
	{
	    char_u	*dirp;
	    char_u	*p;
	    char_u	*rootname;

	    /*
	     * Make a backup by renaming the original file.
	     */
	    /*
	     * If 'cpoptions' includes the "W" flag, we don't want to
	     * overwrite a read-only file.  But rename may be possible
	     * anyway, thus we need an extra check here.
	     */
	    if (file_readonly && vim_strchr(p_cpo, CPO_FWRITE) != NULL)
	    {
		errnum = (char_u *)"E504: ";
		errmsg = (char_u *)_(err_readonly);
		goto fail;
	    }

	    /*
	     *
	     * Form the backup file name - change path/fo.o.h to
	     * path/fo.o.h.bak Try all directories in 'backupdir', first one
	     * that works is used.
	     */
	    dirp = p_bdir;
	    while (*dirp)
	    {
		/*
		 * Isolate one directory name and make the backup file name.
		 */
		(void)copy_option_part(&dirp, IObuff, IOSIZE, ",");
		rootname = get_file_in_dir(fname, IObuff);
		if (rootname == NULL)
		    backup = NULL;
		else
		{
		    backup = buf_modname((buf->b_p_sn || buf->b_shortname),
						 rootname, backup_ext, FALSE);
		    vim_free(rootname);
		}

		if (backup != NULL)
		{
		    /*
		     * If we are not going to keep the backup file, don't
		     * delete an existing one, try to use another name.
		     * Change one character, just before the extension.
		     */
		    if (!p_bk && mch_getperm(backup) >= 0)
		    {
			p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
			if (p < backup)	/* empty file name ??? */
			    p = backup;
			*p = 'z';
			while (*p > 'a' && mch_getperm(backup) >= 0)
			    --*p;
			/* They all exist??? Must be something wrong! */
			if (*p == 'a')
			{
			    vim_free(backup);
			    backup = NULL;
			}
		    }
		}
		if (backup != NULL)
		{
		    /*
		     * Delete any existing backup and move the current version
		     * to the backup.	For safety, we don't remove the backup
		     * until the write has finished successfully. And if the
		     * 'backup' option is set, leave it around.
		     */
		    /*
		     * If the renaming of the original file to the backup file
		     * works, quit here.
		     */
		    if (vim_rename(fname, backup) == 0)
			break;

		    vim_free(backup);   /* don't do the rename below */
		    backup = NULL;
		}
	    }
	    if (backup == NULL && !forceit)
	    {
		errmsg = (char_u *)_("E510: Can't make backup file (add ! to override)");
		goto fail;
	    }
	}
    }

#if defined(UNIX)
    /* When using ":w!" and the file was read-only: make it writable */
    if (forceit && perm >= 0 && !(perm & 0200) && st_old.st_uid == getuid()
				     && vim_strchr(p_cpo, CPO_FWRITE) == NULL)
    {
	perm |= 0200;
	(void)mch_setperm(fname, perm);
	made_writable = TRUE;
    }
#endif

    /* When using ":w!" and writing to the current file, 'readonly' makes no
     * sense, reset it, unless 'Z' appears in 'cpoptions'.  */
    if (forceit && overwriting && vim_strchr(p_cpo, CPO_KEEPRO) == NULL)
    {
	buf->b_p_ro = FALSE;
#ifdef FEAT_TITLE
	need_maketitle = TRUE;	    /* set window title later */
#endif
	status_redraw_all();	    /* redraw status lines later */
    }

    if (end > buf->b_ml.ml_line_count)
	end = buf->b_ml.ml_line_count;
    if (buf->b_ml.ml_flags & ML_EMPTY)
	start = end + 1;

    /*
     * If the original file is being overwritten, there is a small chance that
     * we crash in the middle of writing. Therefore the file is preserved now.
     * This makes all block numbers positive so that recovery does not need
     * the original file.
     * Don't do this if there is a backup file and we are exiting.
     */
    if (reset_changed && !newfile && overwriting
					      && !(exiting && backup != NULL))
    {
	ml_preserve(buf, FALSE);
	if (got_int)
	{
	    errmsg = (char_u *)_(e_interr);
	    goto restore_backup;
	}
    }

#ifdef VMS
    vms_remove_version(fname); /* remove version */
#endif
    /* Default: write the file directly.  May write to a temp file for
     * multi-byte conversion. */
    wfname = fname;

#ifdef FEAT_MBYTE
    /* Check for forced 'fileencoding' from "++opt=val" argument. */
    if (eap != NULL && eap->force_enc != 0)
    {
	fenc = eap->cmd + eap->force_enc;
	fenc = enc_canonize(fenc);
	fenc_tofree = fenc;
    }
    else
	fenc = buf->b_p_fenc;

    /*
     * Check if the file needs to be converted.
     */
    converted = need_conversion(fenc);

    /*
     * Check if UTF-8 to UCS-2/4 or Latin1 conversion needs to be done.  Or
     * Latin1 to Unicode conversion.  This is handled in buf_write_bytes().
     * Prepare the flags for it and allocate bw_conv_buf when needed.
     */
    if (converted && (enc_utf8 || STRCMP(p_enc, "latin1") == 0))
    {
	wb_flags = get_fio_flags(fenc);
	if (wb_flags & (FIO_UCS2 | FIO_UCS4 | FIO_UTF16 | FIO_UTF8))
	{
	    /* Need to allocate a buffer to translate into. */
	    if (wb_flags & (FIO_UCS2 | FIO_UTF16 | FIO_UTF8))
		write_info.bw_conv_buflen = bufsize * 2;
	    else /* FIO_UCS4 */
		write_info.bw_conv_buflen = bufsize * 4;
	    write_info.bw_conv_buf
			   = lalloc((long_u)write_info.bw_conv_buflen, TRUE);
	    if (write_info.bw_conv_buf == NULL)
		end = 0;
	}
    }

# ifdef WIN3264
    if (converted && wb_flags == 0 && (wb_flags = get_win_fio_flags(fenc)) != 0)
    {
	/* Convert UTF-8 -> UCS-2 and UCS-2 -> DBCS.  Worst-case * 4: */
	write_info.bw_conv_buflen = bufsize * 4;
	write_info.bw_conv_buf
			    = lalloc((long_u)write_info.bw_conv_buflen, TRUE);
	if (write_info.bw_conv_buf == NULL)
	    end = 0;
    }
# endif

# ifdef MACOS_CONVERT
    if (converted && wb_flags == 0 && (wb_flags = get_mac_fio_flags(fenc)) != 0)
    {
	write_info.bw_conv_buflen = bufsize * 3;
	write_info.bw_conv_buf
			    = lalloc((long_u)write_info.bw_conv_buflen, TRUE);
	if (write_info.bw_conv_buf == NULL)
	    end = 0;
    }
# endif

# if defined(FEAT_EVAL) || defined(USE_ICONV)
    if (converted && wb_flags == 0)
    {
#  ifdef USE_ICONV
	/*
	 * Use iconv() conversion when conversion is needed and it's not done
	 * internally.
	 */
	write_info.bw_iconv_fd = (iconv_t)my_iconv_open(fenc,
					enc_utf8 ? (char_u *)"utf-8" : p_enc);
	if (write_info.bw_iconv_fd != (iconv_t)-1)
	{
	    /* We're going to use iconv(), allocate a buffer to convert in. */
	    write_info.bw_conv_buflen = bufsize * ICONV_MULT;
	    write_info.bw_conv_buf
			   = lalloc((long_u)write_info.bw_conv_buflen, TRUE);
	    if (write_info.bw_conv_buf == NULL)
		end = 0;
	    write_info.bw_first = TRUE;
	}
#   ifdef FEAT_EVAL
	else
#   endif
#  endif

#  ifdef FEAT_EVAL
	    /*
	     * When the file needs to be converted with 'charconvert' after
	     * writing, write to a temp file instead and let the conversion
	     * overwrite the original file.
	     */
	    if (*p_ccv != NUL)
	    {
		wfname = vim_tempname('w', FALSE);
		if (wfname == NULL)	/* Can't write without a tempfile! */
		{
		    errmsg = (char_u *)_("E214: Can't find temp file for writing");
		    goto restore_backup;
		}
	    }
#  endif
    }
# endif
    if (converted && wb_flags == 0
#  ifdef USE_ICONV
	    && write_info.bw_iconv_fd == (iconv_t)-1
#  endif
#  ifdef FEAT_EVAL
	    && wfname == fname
#  endif
	    )
    {
	if (!forceit)
	{
	    errmsg = (char_u *)_("E213: Cannot convert (add ! to write without conversion)");
	    goto restore_backup;
	}
	notconverted = TRUE;
    }
#endif

    /*
     * If conversion is taking place, we may first pretend to write and check
     * for conversion errors.  Then loop again to write for real.
     * When not doing conversion this writes for real right away.
     */
    for (checking_conversion = TRUE; ; checking_conversion = FALSE)
    {
	/*
	 * There is no need to check conversion when:
	 * - there is no conversion
	 * - we make a backup file, that can be restored in case of conversion
	 *   failure.
	 */
#ifdef FEAT_MBYTE
	if (!converted || dobackup)
#endif
	    checking_conversion = FALSE;

	if (checking_conversion)
	{
	    /* Make sure we don't write anything. */
	    fd = -1;
	    write_info.bw_fd = fd;
	}
	else
	{
	    /*
	     * Open the file "wfname" for writing.
	     * We may try to open the file twice: If we can't write to the file
	     * and forceit is TRUE we delete the existing file and try to
	     * create a new one. If this still fails we may have lost the
	     * original file!  (this may happen when the user reached his
	     * quotum for number of files).
	     * Appending will fail if the file does not exist and forceit is
	     * FALSE.
	     */
	    while ((fd = mch_open((char *)wfname, O_WRONLY | O_EXTRA | (append
				? (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
				: (O_CREAT | O_TRUNC))
				, perm < 0 ? 0666 : (perm & 0777))) < 0)
	    {
		/*
		 * A forced write will try to create a new file if the old one
		 * is still readonly. This may also happen when the directory
		 * is read-only. In that case the mch_remove() will fail.
		 */
		if (errmsg == NULL)
		{
#ifdef UNIX
		    stat_T	st;

		    /* Don't delete the file when it's a hard or symbolic link.
		     */
		    if ((!newfile && st_old.st_nlink > 1)
			    || (mch_lstat((char *)fname, &st) == 0
				&& (st.st_dev != st_old.st_dev
				    || st.st_ino != st_old.st_ino)))
			errmsg = (char_u *)_("E166: Can't open linked file for writing");
		    else
#endif
		    {
			errmsg = (char_u *)_("E212: Can't open file for writing");
			if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
								  && perm >= 0)
			{
#ifdef UNIX
			    /* we write to the file, thus it should be marked
			       writable after all */
			    if (!(perm & 0200))
				made_writable = TRUE;
			    perm |= 0200;
			    if (st_old.st_uid != getuid()
						  || st_old.st_gid != getgid())
				perm &= 0777;
#endif
			    if (!append)  /* don't remove when appending */
				mch_remove(wfname);
			    continue;
			}
		    }
		}

restore_backup:
		{
		    stat_T	st;

		    /*
		     * If we failed to open the file, we don't need a backup.
		     * Throw it away.  If we moved or removed the original file
		     * try to put the backup in its place.
		     */
		    if (backup != NULL && wfname == fname)
		    {
			if (backup_copy)
			{
			    /*
			     * There is a small chance that we removed the
			     * original, try to move the copy in its place.
			     * 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 (mch_stat((char *)fname, &st) < 0)
				vim_rename(backup, fname);
			    /* if original file does exist throw away the copy
			     */
			    if (mch_stat((char *)fname, &st) >= 0)
				mch_remove(backup);
			}
			else
			{
			    /* try to put the original file back */
			    vim_rename(backup, fname);
			}
		    }

		    /* if original file no longer exists give an extra warning
		     */
		    if (!newfile && mch_stat((char *)fname, &st) < 0)
			end = 0;
		}

#ifdef FEAT_MBYTE
		if (wfname != fname)
		    vim_free(wfname);
#endif
		goto fail;
	    }
	    write_info.bw_fd = fd;

#if defined(WIN3264)
	    if (backup != NULL && overwriting && !append)
	    {
		if (backup_copy)
		    (void)mch_copy_file_attribute(wfname, backup);
		else
		    (void)mch_copy_file_attribute(backup, wfname);
	    }

	    if (!overwriting && !append)
	    {
		if (buf->b_ffname != NULL)
		    (void)mch_copy_file_attribute(buf->b_ffname, wfname);
		/* Should copy resource fork */
	    }
#endif

#ifdef FEAT_CRYPT
	    if (*buf->b_p_key != NUL && !filtering)
	    {
		char_u		*header;
		int		header_len;

		buf->b_cryptstate = crypt_create_for_writing(
						      crypt_get_method_nr(buf),
					   buf->b_p_key, &header, &header_len);
		if (buf->b_cryptstate == NULL || header == NULL)
		    end = 0;
		else
		{
		    /* Write magic number, so that Vim knows how this file is
		     * encrypted when reading it back. */
		    write_info.bw_buf = header;
		    write_info.bw_len = header_len;
		    write_info.bw_flags = FIO_NOCONVERT;
		    if (buf_write_bytes(&write_info) == FAIL)
			end = 0;
		    wb_flags |= FIO_ENCRYPTED;
		    vim_free(header);
		}
	    }
#endif
	}
	errmsg = NULL;

	write_info.bw_buf = buffer;
	nchars = 0;

	/* use "++bin", "++nobin" or 'binary' */
	if (eap != NULL && eap->force_bin != 0)
	    write_bin = (eap->force_bin == FORCE_BIN);
	else
	    write_bin = buf->b_p_bin;

#ifdef FEAT_MBYTE
	/*
	 * The BOM is written just after the encryption magic number.
	 * Skip it when appending and the file already existed, the BOM only
	 * makes sense at the start of the file.
	 */
	if (buf->b_p_bomb && !write_bin && (!append || perm < 0))
	{
	    write_info.bw_len = make_bom(buffer, fenc);
	    if (write_info.bw_len > 0)
	    {
		/* don't convert, do encryption */
		write_info.bw_flags = FIO_NOCONVERT | wb_flags;
		if (buf_write_bytes(&write_info) == FAIL)
		    end = 0;
		else
		    nchars += write_info.bw_len;
	    }
	}
	write_info.bw_start_lnum = start;
#endif

#ifdef FEAT_PERSISTENT_UNDO
	write_undo_file = (buf->b_p_udf
			    && overwriting
			    && !append
			    && !filtering
			    && reset_changed
			    && !checking_conversion);
	if (write_undo_file)
	    /* Prepare for computing the hash value of the text. */
	    sha256_start(&sha_ctx);
#endif

	write_info.bw_len = bufsize;
#ifdef HAS_BW_FLAGS
	write_info.bw_flags = wb_flags;
#endif
	fileformat = get_fileformat_force(buf, eap);
	s = buffer;
	len = 0;
	for (lnum = start; lnum <= end; ++lnum)
	{
	    /*
	     * The next while loop is done once for each character written.
	     * Keep it fast!
	     */
	    ptr = ml_get_buf(buf, lnum, FALSE) - 1;
#ifdef FEAT_PERSISTENT_UNDO
	    if (write_undo_file)
		sha256_update(&sha_ctx, ptr + 1,
					      (UINT32_T)(STRLEN(ptr + 1) + 1));
#endif
	    while ((c = *++ptr) != NUL)
	    {
		if (c == NL)
		    *s = NUL;		/* replace newlines with NULs */
		else if (c == CAR && fileformat == EOL_MAC)
		    *s = NL;		/* Mac: replace CRs with NLs */
		else
		    *s = c;
		++s;
		if (++len != bufsize)
		    continue;
		if (buf_write_bytes(&write_info) == FAIL)
		{
		    end = 0;		/* write error: break loop */
		    break;
		}
		nchars += bufsize;
		s = buffer;
		len = 0;
#ifdef FEAT_MBYTE
		write_info.bw_start_lnum = lnum;
#endif
	    }
	    /* write failed or last line has no EOL: stop here */
	    if (end == 0
		    || (lnum == end
			&& (write_bin || !buf->b_p_fixeol)
			&& (lnum == buf->b_no_eol_lnum
			    || (lnum == buf->b_ml.ml_line_count
							   && !buf->b_p_eol))))
	    {
		++lnum;			/* written the line, count it */
		no_eol = TRUE;
		break;
	    }
	    if (fileformat == EOL_UNIX)
		*s++ = NL;
	    else
	    {
		*s++ = CAR;		    /* EOL_MAC or EOL_DOS: write CR */
		if (fileformat == EOL_DOS)  /* write CR-NL */
		{
		    if (++len == bufsize)
		    {
			if (buf_write_bytes(&write_info) == FAIL)
			{
			    end = 0;	/* write error: break loop */
			    break;
			}
			nchars += bufsize;
			s = buffer;
			len = 0;
		    }
		    *s++ = NL;
		}
	    }
	    if (++len == bufsize && end)
	    {
		if (buf_write_bytes(&write_info) == FAIL)
		{
		    end = 0;		/* write error: break loop */
		    break;
		}
		nchars += bufsize;
		s = buffer;
		len = 0;

		ui_breakcheck();
		if (got_int)
		{
		    end = 0;		/* Interrupted, break loop */
		    break;
		}
	    }
#ifdef VMS
	    /*
	     * On VMS there is a problem: newlines get added when writing
	     * blocks at a time. Fix it by writing a line at a time.
	     * This is much slower!
	     * Explanation: VAX/DECC RTL insists that records in some RMS
	     * structures end with a newline (carriage return) character, and
	     * if they don't it adds one.
	     * With other RMS structures it works perfect without this fix.
	     */
	    if (buf->b_fab_rfm == FAB$C_VFC
		    || ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0))
	    {
		int b2write;

		buf->b_fab_mrs = (buf->b_fab_mrs == 0
			? MIN(4096, bufsize)
			: MIN(buf->b_fab_mrs, bufsize));

		b2write = len;
		while (b2write > 0)
		{
		    write_info.bw_len = MIN(b2write, buf->b_fab_mrs);
		    if (buf_write_bytes(&write_info) == FAIL)
		    {
			end = 0;
			break;
		    }
		    b2write -= MIN(b2write, buf->b_fab_mrs);
		}
		write_info.bw_len = bufsize;
		nchars += len;
		s = buffer;
		len = 0;
	    }
#endif
	}
	if (len > 0 && end > 0)
	{
	    write_info.bw_len = len;
	    if (buf_write_bytes(&write_info) == FAIL)
		end = 0;		    /* write error */
	    nchars += len;
	}

	/* Stop when writing done or an error was encountered. */
	if (!checking_conversion || end == 0)
	    break;

	/* If no error happened until now, writing should be ok, so loop to
	 * really write the buffer. */
    }

    /* If we started writing, finish writing. Also when an error was
     * encountered. */
    if (!checking_conversion)
    {
#if defined(UNIX) && defined(HAVE_FSYNC)
	/*
	 * On many journalling file systems there is a bug that causes both the
	 * original and the backup file to be lost when halting the system
	 * right after writing the file.  That's because only the meta-data is
	 * journalled.  Syncing the file slows down the system, but assures it
	 * has been written to disk and we don't lose it.
	 * For a device do try the fsync() but don't complain if it does not
	 * work (could be a pipe).
	 * If the 'fsync' option is FALSE, don't fsync().  Useful for laptops.
	 */
	if (p_fs && fsync(fd) != 0 && !device)
	{
	    errmsg = (char_u *)_("E667: Fsync failed");
	    end = 0;
	}
#endif

#if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
	/* Probably need to set the security context. */
	if (!backup_copy)
	    mch_copy_sec(backup, wfname);
#endif

#ifdef UNIX
	/* When creating a new file, set its owner/group to that of the
	 * original file.  Get the new device and inode number. */
	if (backup != NULL && !backup_copy)
	{
# ifdef HAVE_FCHOWN
	    stat_T	st;

	    /* don't change the owner when it's already OK, some systems remove
	     * permission or ACL stuff */
	    if (mch_stat((char *)wfname, &st) < 0
		    || st.st_uid != st_old.st_uid
		    || st.st_gid != st_old.st_gid)
	    {
		ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
		if (perm >= 0)	/* set permission again, may have changed */
		    (void)mch_setperm(wfname, perm);
	    }
# endif
	    buf_setino(buf);
	}
	else if (!buf->b_dev_valid)
	    /* Set the inode when creating a new file. */
	    buf_setino(buf);
#endif

	if (close(fd) != 0)
	{
	    errmsg = (char_u *)_("E512: Close failed");
	    end = 0;
	}

#ifdef UNIX
	if (made_writable)
	    perm &= ~0200;	/* reset 'w' bit for security reasons */
#endif
	if (perm >= 0)		/* set perm. of new file same as old file */
	    (void)mch_setperm(wfname, perm);
#ifdef HAVE_ACL
	/*
	 * Probably need to set the ACL before changing the user (can't set the
	 * ACL on a file the user doesn't own).
	 * On Solaris, with ZFS and the aclmode property set to "discard" (the
	 * default), chmod() discards all part of a file's ACL that don't
	 * represent the mode of the file.  It's non-trivial for us to discover
	 * whether we're in that situation, so we simply always re-set the ACL.
	 */
# ifndef HAVE_SOLARIS_ZFS_ACL
	if (!backup_copy)
# endif
	    mch_set_acl(wfname, acl);
#endif
#ifdef FEAT_CRYPT
	if (buf->b_cryptstate != NULL)
	{
	    crypt_free_state(buf->b_cryptstate);
	    buf->b_cryptstate = NULL;
	}
#endif

#if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
	if (wfname != fname)
	{
	    /*
	     * The file was written to a temp file, now it needs to be
	     * converted with 'charconvert' to (overwrite) the output file.
	     */
	    if (end != 0)
	    {
		if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc,
						  fenc, wfname, fname) == FAIL)
		{
		    write_info.bw_conv_error = TRUE;
		    end = 0;
		}
	    }
	    mch_remove(wfname);
	    vim_free(wfname);
	}
#endif
    }

    if (end == 0)
    {
	/*
	 * Error encountered.
	 */
	if (errmsg == NULL)
	{
#ifdef FEAT_MBYTE
	    if (write_info.bw_conv_error)
	    {
		if (write_info.bw_conv_error_lnum == 0)
		    errmsg = (char_u *)_("E513: write error, conversion failed (make 'fenc' empty to override)");
		else
		{
		    errmsg_allocated = TRUE;
		    errmsg = alloc(300);
		    vim_snprintf((char *)errmsg, 300, _("E513: write error, conversion failed in line %ld (make 'fenc' empty to override)"),
					 (long)write_info.bw_conv_error_lnum);
		}
	    }
	    else
#endif
		if (got_int)
		    errmsg = (char_u *)_(e_interr);
		else
		    errmsg = (char_u *)_("E514: write error (file system full?)");
	}

	/*
	 * If we have a backup file, try to put it in place of the new file,
	 * because the new file is probably corrupt.  This avoids losing the
	 * original file when trying to make a backup when writing the file a
	 * second time.
	 * When "backup_copy" is set we need to copy the backup over the new
	 * file.  Otherwise rename the backup file.
	 * If this is OK, don't give the extra warning message.
	 */
	if (backup != NULL)
	{
	    if (backup_copy)
	    {
		/* This may take a while, if we were interrupted let the user
		 * know we got the message. */
		if (got_int)
		{
		    MSG(_(e_interr));
		    out_flush();
		}
		if ((fd = mch_open((char *)backup, O_RDONLY | O_EXTRA, 0)) >= 0)
		{
		    if ((write_info.bw_fd = mch_open((char *)fname,
				    O_WRONLY | O_CREAT | O_TRUNC | O_EXTRA,
							   perm & 0777)) >= 0)
		    {
			/* copy the file. */
			write_info.bw_buf = smallbuf;
#ifdef HAS_BW_FLAGS
			write_info.bw_flags = FIO_NOCONVERT;
#endif
			while ((write_info.bw_len = read_eintr(fd, smallbuf,
						      SMBUFSIZE)) > 0)
			    if (buf_write_bytes(&write_info) == FAIL)
				break;

			if (close(write_info.bw_fd) >= 0
						   && write_info.bw_len == 0)
			    end = 1;		/* success */
		    }
		    close(fd);	/* ignore errors for closing read file */
		}
	    }
	    else
	    {
		if (vim_rename(backup, fname) == 0)
		    end = 1;
	    }
	}
	goto fail;
    }

    lnum -= start;	    /* compute number of written lines */
    --no_wait_return;	    /* may wait for return now */

#if !(defined(UNIX) || defined(VMS))
    fname = sfname;	    /* use shortname now, for the messages */
#endif
    if (!filtering)
    {
	msg_add_fname(buf, fname);	/* put fname in IObuff with quotes */
	c = FALSE;
#ifdef FEAT_MBYTE
	if (write_info.bw_conv_error)
	{
	    STRCAT(IObuff, _(" CONVERSION ERROR"));
	    c = TRUE;
	    if (write_info.bw_conv_error_lnum != 0)
		vim_snprintf_add((char *)IObuff, IOSIZE, _(" in line %ld;"),
			(long)write_info.bw_conv_error_lnum);
	}
	else if (notconverted)
	{
	    STRCAT(IObuff, _("[NOT converted]"));
	    c = TRUE;
	}
	else if (converted)
	{
	    STRCAT(IObuff, _("[converted]"));
	    c = TRUE;
	}
#endif
	if (device)
	{
	    STRCAT(IObuff, _("[Device]"));
	    c = TRUE;
	}
	else if (newfile)
	{
	    STRCAT(IObuff, shortmess(SHM_NEW) ? _("[New]") : _("[New File]"));
	    c = TRUE;
	}
	if (no_eol)
	{
	    msg_add_eol();
	    c = TRUE;
	}
	/* may add [unix/dos/mac] */
	if (msg_add_fileformat(fileformat))
	    c = TRUE;
#ifdef FEAT_CRYPT
	if (wb_flags & FIO_ENCRYPTED)
	{
	    crypt_append_msg(buf);
	    c = TRUE;
	}
#endif
	msg_add_lines(c, (long)lnum, nchars);	/* add line/char count */
	if (!shortmess(SHM_WRITE))
	{
	    if (append)
		STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended"));
	    else
		STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written"));
	}

	set_keep_msg(msg_trunc_attr(IObuff, FALSE, 0), 0);
    }

    /* When written everything correctly: reset 'modified'.  Unless not
     * writing to the original file and '+' is not in 'cpoptions'. */
    if (reset_changed && whole && !append
#ifdef FEAT_MBYTE
	    && !write_info.bw_conv_error
#endif
	    && (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL)
	    )
    {
	unchanged(buf, TRUE);
#ifdef FEAT_AUTOCMD
	/* b:changedtick is always incremented in unchanged() but that
	 * should not trigger a TextChanged event. */
	if (last_changedtick + 1 == CHANGEDTICK(buf)
					       && last_changedtick_buf == buf)
	    last_changedtick = CHANGEDTICK(buf);
#endif
	u_unchanged(buf);
	u_update_save_nr(buf);
    }

    /*
     * If written to the current file, update the timestamp of the swap file
     * and reset the BF_WRITE_MASK flags. Also sets buf->b_mtime.
     */
    if (overwriting)
    {
	ml_timestamp(buf);
	if (append)
	    buf->b_flags &= ~BF_NEW;
	else
	    buf->b_flags &= ~BF_WRITE_MASK;
    }

    /*
     * If we kept a backup until now, and we are in patch mode, then we make
     * the backup file our 'original' file.
     */
    if (*p_pm && dobackup)
    {
	char *org = (char *)buf_modname((buf->b_p_sn || buf->b_shortname),
							  fname, p_pm, FALSE);

	if (backup != NULL)
	{
	    stat_T	st;

	    /*
	     * If the original file does not exist yet
	     * the current backup file becomes the original file
	     */
	    if (org == NULL)
		EMSG(_("E205: Patchmode: can't save original file"));
	    else if (mch_stat(org, &st) < 0)
	    {
		vim_rename(backup, (char_u *)org);
		vim_free(backup);	    /* don't delete the file */
		backup = NULL;
#ifdef UNIX
		set_file_time((char_u *)org, st_old.st_atime, st_old.st_mtime);
#endif
	    }
	}
	/*
	 * If there is no backup file, remember that a (new) file was
	 * created.
	 */
	else
	{
	    int empty_fd;

	    if (org == NULL
		    || (empty_fd = mch_open(org,
				      O_CREAT | O_EXTRA | O_EXCL | O_NOFOLLOW,
					perm < 0 ? 0666 : (perm & 0777))) < 0)
	      EMSG(_("E206: patchmode: can't touch empty original file"));
	    else
	      close(empty_fd);
	}
	if (org != NULL)
	{
	    mch_setperm((char_u *)org, mch_getperm(fname) & 0777);
	    vim_free(org);
	}
    }

    /*
     * Remove the backup unless 'backup' option is set
     */
    if (!p_bk && backup != NULL && mch_remove(backup) != 0)
	EMSG(_("E207: Can't delete backup file"));

#ifdef FEAT_SUN_WORKSHOP
    if (usingSunWorkShop)
	workshop_file_saved((char *) ffname);
#endif

    goto nofail;

    /*
     * Finish up.  We get here either after failure or success.
     */
fail:
    --no_wait_return;		/* may wait for return now */
nofail:

    /* Done saving, we accept changed buffer warnings again */
    buf->b_saving = FALSE;

    vim_free(backup);
    if (buffer != smallbuf)
	vim_free(buffer);
#ifdef FEAT_MBYTE
    vim_free(fenc_tofree);
    vim_free(write_info.bw_conv_buf);
# ifdef USE_ICONV
    if (write_info.bw_iconv_fd != (iconv_t)-1)
    {
	iconv_close(write_info.bw_iconv_fd);
	write_info.bw_iconv_fd = (iconv_t)-1;
    }
# endif
#endif
#ifdef HAVE_ACL
    mch_free_acl(acl);
#endif

    if (errmsg != NULL)
    {
	int numlen = errnum != NULL ? (int)STRLEN(errnum) : 0;

	attr = HL_ATTR(HLF_E);	/* set highlight for error messages */
	msg_add_fname(buf,
#ifndef UNIX
		sfname
#else
		fname
#endif
		     );		/* put file name in IObuff with quotes */
	if (STRLEN(IObuff) + STRLEN(errmsg) + numlen >= IOSIZE)
	    IObuff[IOSIZE - STRLEN(errmsg) - numlen - 1] = NUL;
	/* If the error message has the form "is ...", put the error number in
	 * front of the file name. */
	if (errnum != NULL)
	{
	    STRMOVE(IObuff + numlen, IObuff);
	    mch_memmove(IObuff, errnum, (size_t)numlen);
	}
	STRCAT(IObuff, errmsg);
	emsg(IObuff);
	if (errmsg_allocated)
	    vim_free(errmsg);

	retval = FAIL;
	if (end == 0)
	{
	    MSG_PUTS_ATTR(_("\nWARNING: Original file may be lost or damaged\n"),
		    attr | MSG_HIST);
	    MSG_PUTS_ATTR(_("don't quit the editor until the file is successfully written!"),
		    attr | MSG_HIST);

	    /* Update the timestamp to avoid an "overwrite changed file"
	     * prompt when writing again. */
	    if (mch_stat((char *)fname, &st_old) >= 0)
	    {
		buf_store_time(buf, &st_old, fname);
		buf->b_mtime_read = buf->b_mtime;
	    }
	}
    }
    msg_scroll = msg_save;

#ifdef FEAT_PERSISTENT_UNDO
    /*
     * When writing the whole file and 'undofile' is set, also write the undo
     * file.
     */
    if (retval == OK && write_undo_file)
    {
	char_u	    hash[UNDO_HASH_SIZE];

	sha256_finish(&sha_ctx, hash);
	u_write_undo(NULL, FALSE, buf, hash);
    }
#endif

#ifdef FEAT_AUTOCMD
#ifdef FEAT_EVAL
    if (!should_abort(retval))
#else
    if (!got_int)
#endif
    {
	aco_save_T	aco;

	curbuf->b_no_eol_lnum = 0;  /* in case it was set by the previous read */

	/*
	 * Apply POST autocommands.
	 * Careful: The autocommands may call buf_write() recursively!
	 */
	aucmd_prepbuf(&aco, buf);

	if (append)
	    apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname,
							  FALSE, curbuf, eap);
	else if (filtering)
	    apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname,
							  FALSE, curbuf, eap);
	else if (reset_changed && whole)
	    apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname,
							  FALSE, curbuf, eap);
	else
	    apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname,
							  FALSE, curbuf, eap);

	/* restore curwin/curbuf and a few other things */
	aucmd_restbuf(&aco);

#ifdef FEAT_EVAL
	if (aborting())	    /* autocmds may abort script processing */
	    retval = FALSE;
#endif
    }
#endif

    got_int |= prev_got_int;

    return retval;
}


ex_doautoall(exarg_T *eap)
{
    int		retval;
    aco_save_T	aco;
    buf_T	*buf;
    bufref_T	bufref;
    char_u	*arg = eap->arg;
    int		call_do_modelines = check_nomodeline(&arg);
    int		did_aucmd;

    /*
     * This is a bit tricky: For some commands curwin->w_buffer needs to be
     * equal to curbuf, but for some buffers there may not be a window.
     * So we change the buffer for the current window for a moment.  This
     * gives problems when the autocommands make changes to the list of
     * buffers or windows...
     */
    FOR_ALL_BUFFERS(buf)
    {
	if (buf->b_ml.ml_mfp != NULL)
	{
	    /* find a window for this buffer and save some values */
	    aucmd_prepbuf(&aco, buf);
	    set_bufref(&bufref, buf);

	    /* execute the autocommands for this buffer */
	    retval = do_doautocmd(arg, FALSE, &did_aucmd);

	    if (call_do_modelines && did_aucmd)
	    {
		/* Execute the modeline settings, but don't set window-local
		 * options if we are using the current window for another
		 * buffer. */
		do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0);
	    }

	    /* restore the current window */
	    aucmd_restbuf(&aco);

	    /* stop if there is some error or buffer was deleted */
	    if (retval == FAIL || !bufref_valid(&bufref))
		break;
	}
    }

    check_cursor();	    /* just in case lines got deleted */
}


msg_add_fname(buf_T *buf, char_u *fname)
{
    if (fname == NULL)
	fname = (char_u *)"-stdin-";
    home_replace(buf, fname, IObuff + 1, IOSIZE - 4, TRUE);
    IObuff[0] = '"';
    STRCAT(IObuff, "\" ");
}


delete_recursive(char_u *name)
{
    int result = 0;
    char_u	**files;
    int		file_count;
    int		i;
    char_u	*exp;

    /* A symbolic link to a directory itself is deleted, not the directory it
     * points to. */
    if (
# if defined(UNIX) || defined(WIN32)
	 mch_isrealdir(name)
# else
	 mch_isdir(name)
# endif
	    )
    {
	vim_snprintf((char *)NameBuff, MAXPATHL, "%s/*", name);
	exp = vim_strsave(NameBuff);
	if (exp == NULL)
	    return -1;
	if (gen_expand_wildcards(1, &exp, &file_count, &files,
	      EW_DIR|EW_FILE|EW_SILENT|EW_ALLLINKS|EW_DODOT|EW_EMPTYOK) == OK)
	{
	    for (i = 0; i < file_count; ++i)
		if (delete_recursive(files[i]) != 0)
		    result = -1;
	    FreeWild(file_count, files);
	}
	else
	    result = -1;
	vim_free(exp);
	(void)mch_rmdir(name);
    }
    else
	result = mch_remove(name) == 0 ? 0 : -1;

    return result;
}


msg_add_fileformat(int eol_type)
{
#ifndef USE_CRNL
    if (eol_type == EOL_DOS)
    {
	STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]"));
	return TRUE;
    }
#endif
#ifndef USE_CR
    if (eol_type == EOL_MAC)
    {
	STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]"));
	return TRUE;
    }
#endif
#if defined(USE_CRNL) || defined(USE_CR)
    if (eol_type == EOL_UNIX)
    {
	STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]"));
	return TRUE;
    }
#endif
    return FALSE;
}


do_augroup(char_u *arg, int del_group)
{
    int	    i;

    if (del_group)
    {
	if (*arg == NUL)
	    EMSG(_(e_argreq));
	else
	    au_del_group(arg);
    }
    else if (STRICMP(arg, "end") == 0)   /* ":aug end": back to group 0 */
	current_augroup = AUGROUP_DEFAULT;
    else if (*arg)		    /* ":aug xxx": switch to group xxx */
    {
	i = au_new_group(arg);
	if (i != AUGROUP_ERROR)
	    current_augroup = i;
    }
    else			    /* ":aug": list the group names */
    {
	msg_start();
	for (i = 0; i < augroups.ga_len; ++i)
	{
	    if (AUGROUP_NAME(i) != NULL)
	    {
		msg_puts(AUGROUP_NAME(i));
		msg_puts((char_u *)"  ");
	    }
	}
	msg_clr_eos();
	msg_end();
    }
}


need_conversion(char_u *fenc)
{
    int		same_encoding;
    int		enc_flags;
    int		fenc_flags;

    if (*fenc == NUL || STRCMP(p_enc, fenc) == 0)
    {
	same_encoding = TRUE;
	fenc_flags = 0;
    }
    else
    {
	/* Ignore difference between "ansi" and "latin1", "ucs-4" and
	 * "ucs-4be", etc. */
	enc_flags = get_fio_flags(p_enc);
	fenc_flags = get_fio_flags(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;
    }

    /* Encodings differ.  However, conversion is not needed when 'enc' is any
     * Unicode encoding and the file is UTF-8. */
    return !(enc_utf8 && fenc_flags == FIO_UTF8);
}


event_name2nr(char_u *start, char_u **end)
{
    char_u	*p;
    int		i;
    int		len;

    /* the event name ends with end of line, '|', a blank or a comma */
    for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
	;
    for (i = 0; event_names[i].name != NULL; ++i)
    {
	len = (int)STRLEN(event_names[i].name);
	if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
	    break;
    }
    if (*p == ',')
	++p;
    *end = p;
    if (event_names[i].name == NULL)
	return NUM_EVENTS;
    return event_names[i].event;
}


check_marks_read(void)
{
    if (!curbuf->b_marks_read && get_viminfo_parameter('\'') > 0
						  && curbuf->b_ffname != NULL)
	read_viminfo(NULL, VIF_WANT_MARKS);

    /* Always set b_marks_read; needed when 'viminfo' is changed to include
     * the ' parameter after opening a buffer. */
    curbuf->b_marks_read = TRUE;
}


shorten_fname(char_u *full_path, char_u *dir_name)
{
    int		len;
    char_u	*p;

    if (full_path == NULL)
	return NULL;
    len = (int)STRLEN(dir_name);
    if (fnamencmp(dir_name, full_path, len) == 0)
    {
	p = full_path + len;
#if defined(MSWIN)
	/*
	 * MSWIN: when a file is in the root directory, dir_name will end in a
	 * slash, since C: by itself does not define a specific dir. In this
	 * case p may already be correct. <negri>
	 */
	if (!((len > 2) && (*(p - 2) == ':')))
#endif
	{
	    if (vim_ispathsep(*p))
		++p;
#ifndef VMS   /* the path separator is always part of the path */
	    else
		p = NULL;
#endif
	}
    }
#if defined(MSWIN)
    /*
     * When using a file in the current drive, remove the drive name:
     * "A:\dir\file" -> "\dir\file".  This helps when moving a session file on
     * a floppy from "A:\dir" to "B:\dir".
     */
    else if (len > 3
	    && TOUPPER_LOC(full_path[0]) == TOUPPER_LOC(dir_name[0])
	    && full_path[1] == ':'
	    && vim_ispathsep(full_path[2]))
	p = full_path + 2;
#endif
    else
	p = NULL;
    return p;
}


msg_add_lines(
    int	    insert_space,
    long    lnum,
    off_T   nchars)
{
    char_u  *p;

    p = IObuff + STRLEN(IObuff);

    if (insert_space)
	*p++ = ' ';
    if (shortmess(SHM_LINES))
	vim_snprintf((char *)p, IOSIZE - (p - IObuff),
		"%ldL, %lldC", lnum, (varnumber_T)nchars);
    else
    {
	if (lnum == 1)
	    STRCPY(p, _("1 line, "));
	else
	    sprintf((char *)p, _("%ld lines, "), lnum);
	p += STRLEN(p);
	if (nchars == 1)
	    STRCPY(p, _("1 character"));
	else
	    vim_snprintf((char *)p, IOSIZE - (p - IObuff),
		    _("%lld characters"), (varnumber_T)nchars);
    }
}


time_differs(long t1, long t2)
{
#if defined(__linux__) || defined(MSWIN)
    /* On a FAT filesystem, esp. under Linux, there are only 5 bits to store
     * the seconds.  Since the roundoff is done when flushing the inode, the
     * time may change unexpectedly by one second!!! */
    return (t1 - t2 > 1 || t2 - t1 > 1);
#else
    return (t1 != t2);
#endif
}


buf_check_timestamp(
    buf_T	*buf,
    int		focus UNUSED)	/* called for GUI focus event */
{
    stat_T	st;
    int		stat_res;
    int		retval = 0;
    char_u	*path;
    char_u	*tbuf;
    char	*mesg = NULL;
    char	*mesg2 = "";
    int		helpmesg = FALSE;
    int		reload = FALSE;
    char	*reason;
#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
    int		can_reload = FALSE;
#endif
    off_T	orig_size = buf->b_orig_size;
    int		orig_mode = buf->b_orig_mode;
#ifdef FEAT_GUI
    int		save_mouse_correct = need_mouse_correct;
#endif
#ifdef FEAT_AUTOCMD
    static int	busy = FALSE;
    int		n;
    char_u	*s;
    bufref_T	bufref;

    set_bufref(&bufref, buf);
#endif

    /* If there is no file name, the buffer is not loaded, 'buftype' is
     * set, we are in the middle of a save or being called recursively: ignore
     * this buffer. */
    if (buf->b_ffname == NULL
	    || buf->b_ml.ml_mfp == NULL
	    || *buf->b_p_bt != NUL
	    || buf->b_saving
#ifdef FEAT_AUTOCMD
	    || busy
#endif
#ifdef FEAT_NETBEANS_INTG
	    || isNetbeansBuffer(buf)
#endif
#ifdef FEAT_TERMINAL
	    || buf->b_term != NULL
#endif
	    )
	return 0;

    if (       !(buf->b_flags & BF_NOTEDITED)
	    && buf->b_mtime != 0
	    && ((stat_res = mch_stat((char *)buf->b_ffname, &st)) < 0
		|| time_differs((long)st.st_mtime, buf->b_mtime)
		|| st.st_size != buf->b_orig_size
#ifdef HAVE_ST_MODE
		|| (int)st.st_mode != buf->b_orig_mode
#else
		|| mch_getperm(buf->b_ffname) != buf->b_orig_mode
#endif
		))
    {
	retval = 1;

	/* set b_mtime to stop further warnings (e.g., when executing
	 * FileChangedShell autocmd) */
	if (stat_res < 0)
	{
	    buf->b_mtime = 0;
	    buf->b_orig_size = 0;
	    buf->b_orig_mode = 0;
	}
	else
	    buf_store_time(buf, &st, buf->b_ffname);

	/* Don't do anything for a directory.  Might contain the file
	 * explorer. */
	if (mch_isdir(buf->b_fname))
	    ;

	/*
	 * If 'autoread' is set, the buffer has no changes and the file still
	 * exists, reload the buffer.  Use the buffer-local option value if it
	 * was set, the global option value otherwise.
	 */
	else if ((buf->b_p_ar >= 0 ? buf->b_p_ar : p_ar)
				       && !bufIsChanged(buf) && stat_res >= 0)
	    reload = TRUE;
	else
	{
	    if (stat_res < 0)
		reason = "deleted";
	    else if (bufIsChanged(buf))
		reason = "conflict";
	    else if (orig_size != buf->b_orig_size || buf_contents_changed(buf))
		reason = "changed";
	    else if (orig_mode != buf->b_orig_mode)
		reason = "mode";
	    else
		reason = "time";

#ifdef FEAT_AUTOCMD
	    /*
	     * Only give the warning if there are no FileChangedShell
	     * autocommands.
	     * Avoid being called recursively by setting "busy".
	     */
	    busy = TRUE;
# ifdef FEAT_EVAL
	    set_vim_var_string(VV_FCS_REASON, (char_u *)reason, -1);
	    set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", -1);
# endif
	    ++allbuf_lock;
	    n = apply_autocmds(EVENT_FILECHANGEDSHELL,
				      buf->b_fname, buf->b_fname, FALSE, buf);
	    --allbuf_lock;
	    busy = FALSE;
	    if (n)
	    {
		if (!bufref_valid(&bufref))
		    EMSG(_("E246: FileChangedShell autocommand deleted buffer"));
# ifdef FEAT_EVAL
		s = get_vim_var_str(VV_FCS_CHOICE);
		if (STRCMP(s, "reload") == 0 && *reason != 'd')
		    reload = TRUE;
		else if (STRCMP(s, "ask") == 0)
		    n = FALSE;
		else
# endif
		    return 2;
	    }
	    if (!n)
#endif
	    {
		if (*reason == 'd')
		    mesg = _("E211: File \"%s\" no longer available");
		else
		{
		    helpmesg = TRUE;
#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
		    can_reload = TRUE;
#endif
		    /*
		     * Check if the file contents really changed to avoid
		     * giving a warning when only the timestamp was set (e.g.,
		     * checked out of CVS).  Always warn when the buffer was
		     * changed.
		     */
		    if (reason[2] == 'n')
		    {
			mesg = _("W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as well");
			mesg2 = _("See \":help W12\" for more info.");
		    }
		    else if (reason[1] == 'h')
		    {
			mesg = _("W11: Warning: File \"%s\" has changed since editing started");
			mesg2 = _("See \":help W11\" for more info.");
		    }
		    else if (*reason == 'm')
		    {
			mesg = _("W16: Warning: Mode of file \"%s\" has changed since editing started");
			mesg2 = _("See \":help W16\" for more info.");
		    }
		    else
			/* Only timestamp changed, store it to avoid a warning
			 * in check_mtime() later. */
			buf->b_mtime_read = buf->b_mtime;
		}
	    }
	}

    }
    else if ((buf->b_flags & BF_NEW) && !(buf->b_flags & BF_NEW_W)
						&& vim_fexists(buf->b_ffname))
    {
	retval = 1;
	mesg = _("W13: Warning: File \"%s\" has been created after editing started");
	buf->b_flags |= BF_NEW_W;
#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
	can_reload = TRUE;
#endif
    }

    if (mesg != NULL)
    {
	path = home_replace_save(buf, buf->b_fname);
	if (path != NULL)
	{
	    if (!helpmesg)
		mesg2 = "";
	    tbuf = alloc((unsigned)(STRLEN(path) + STRLEN(mesg)
							+ STRLEN(mesg2) + 2));
	    sprintf((char *)tbuf, mesg, path);
#ifdef FEAT_EVAL
	    /* Set warningmsg here, before the unimportant and output-specific
	     * mesg2 has been appended. */
	    set_vim_var_string(VV_WARNINGMSG, tbuf, -1);
#endif
#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
	    if (can_reload)
	    {
		if (*mesg2 != NUL)
		{
		    STRCAT(tbuf, "\n");
		    STRCAT(tbuf, mesg2);
		}
		if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), tbuf,
			  (char_u *)_("&OK\n&Load File"), 1, NULL, TRUE) == 2)
		    reload = TRUE;
	    }
	    else
#endif
	    if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned)
	    {
		if (*mesg2 != NUL)
		{
		    STRCAT(tbuf, "; ");
		    STRCAT(tbuf, mesg2);
		}
		EMSG(tbuf);
		retval = 2;
	    }
	    else
	    {
# ifdef FEAT_AUTOCMD
		if (!autocmd_busy)
# endif
		{
		    msg_start();
		    msg_puts_attr(tbuf, HL_ATTR(HLF_E) + MSG_HIST);
		    if (*mesg2 != NUL)
			msg_puts_attr((char_u *)mesg2,
						   HL_ATTR(HLF_W) + MSG_HIST);
		    msg_clr_eos();
		    (void)msg_end();
		    if (emsg_silent == 0)
		    {
			out_flush();
# ifdef FEAT_GUI
			if (!focus)
# endif
			    /* give the user some time to think about it */
			    ui_delay(1000L, TRUE);

			/* don't redraw and erase the message */
			redraw_cmdline = FALSE;
		    }
		}
		already_warned = TRUE;
	    }

	    vim_free(path);
	    vim_free(tbuf);
	}
    }

    if (reload)
    {
	/* Reload the buffer. */
	buf_reload(buf, orig_mode);
#ifdef FEAT_PERSISTENT_UNDO
	if (buf->b_p_udf && buf->b_ffname != NULL)
	{
	    char_u	    hash[UNDO_HASH_SIZE];
	    buf_T	    *save_curbuf = curbuf;

	    /* Any existing undo file is unusable, write it now. */
	    curbuf = buf;
	    u_compute_hash(hash);
	    u_write_undo(NULL, FALSE, buf, hash);
	    curbuf = save_curbuf;
	}
#endif
    }

#ifdef FEAT_AUTOCMD
    /* Trigger FileChangedShell when the file was changed in any way. */
    if (bufref_valid(&bufref) && retval != 0)
	(void)apply_autocmds(EVENT_FILECHANGEDSHELLPOST,
				      buf->b_fname, buf->b_fname, FALSE, buf);
#endif
#ifdef FEAT_GUI
    /* restore this in case an autocommand has set it; it would break
     * 'mousefocus' */
    need_mouse_correct = save_mouse_correct;
#endif

    return retval;
}


filemess(
    buf_T	*buf,
    char_u	*name,
    char_u	*s,
    int		attr)
{
    int		msg_scroll_save;

    if (msg_silent != 0)
	return;
    msg_add_fname(buf, name);	    /* put file name in IObuff with quotes */
    /* If it's extremely long, truncate it. */
    if (STRLEN(IObuff) > IOSIZE - 80)
	IObuff[IOSIZE - 80] = NUL;
    STRCAT(IObuff, s);
    /*
     * For the first message may have to start a new line.
     * For further ones overwrite the previous one, reset msg_scroll before
     * calling filemess().
     */
    msg_scroll_save = msg_scroll;
    if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0)
	msg_scroll = FALSE;
    if (!msg_scroll)	/* wait a bit when overwriting an error msg */
	check_for_delay(FALSE);
    msg_start();
    msg_scroll = msg_scroll_save;
    msg_scrolled_ign = TRUE;
    /* may truncate the message to avoid a hit-return prompt */
    msg_outtrans_attr(msg_may_trunc(FALSE, IObuff), attr);
    msg_clr_eos();
    out_flush();
    msg_scrolled_ign = FALSE;
}


read_eintr(int fd, void *buf, size_t bufsize)
{
    long ret;

    for (;;)
    {
	ret = vim_read(fd, buf, bufsize);
	if (ret >= 0 || errno != EINTR)
	    break;
    }
    return ret;
}