mirror of
https://github.com/neovim/neovim.git
synced 2025-09-18 01:08:20 +00:00
refactor(fileio.c): factor out autocmd handling from buf_write()
This commit is contained in:
@@ -2076,167 +2076,14 @@ char *new_file_message(void)
|
|||||||
return shortmess(SHM_NEW) ? _("[New]") : _("[New File]");
|
return shortmess(SHM_NEW) ? _("[New]") : _("[New File]");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// buf_write() - write to file "fname" lines "start" through "end"
|
static int buf_write_do_autocmds(buf_T *buf, char **fnamep, char **sfnamep, char **ffnamep,
|
||||||
///
|
linenr_T start, linenr_T *endp, exarg_T *eap, bool append,
|
||||||
/// We do our own buffering here because fwrite() is so slow.
|
bool filtering, bool reset_changed, bool overwriting, bool whole,
|
||||||
///
|
const pos_T orig_start, const pos_T orig_end)
|
||||||
/// If "forceit" is true, we don't care for errors when attempting backups.
|
|
||||||
/// In case of an error everything possible is done to restore the original
|
|
||||||
/// file. But when "forceit" is true, we risk losing it.
|
|
||||||
///
|
|
||||||
/// When "reset_changed" is true and "append" == false and "start" == 1 and
|
|
||||||
/// "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
|
|
||||||
///
|
|
||||||
/// This function must NOT use NameBuff (because it's called by autowrite()).
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// @param eap for forced 'ff' and 'fenc', can be NULL!
|
|
||||||
/// @param append append to the file
|
|
||||||
///
|
|
||||||
/// @return FAIL for failure, OK otherwise
|
|
||||||
int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T end, exarg_T *eap,
|
|
||||||
int append, int forceit, int reset_changed, int filtering)
|
|
||||||
{
|
{
|
||||||
int fd;
|
|
||||||
char *backup = NULL;
|
|
||||||
int backup_copy = false; // copy the original file?
|
|
||||||
int dobackup;
|
|
||||||
char *ffname;
|
|
||||||
char *wfname = NULL; // name of file to write to
|
|
||||||
char *s;
|
|
||||||
char *ptr;
|
|
||||||
char c;
|
|
||||||
int len;
|
|
||||||
linenr_T lnum;
|
|
||||||
long nchars;
|
|
||||||
#define SET_ERRMSG_NUM(num, msg) \
|
|
||||||
errnum = (num), errmsg = (msg), errmsgarg = 0
|
|
||||||
#define SET_ERRMSG_ARG(msg, error) \
|
|
||||||
errnum = NULL, errmsg = (msg), errmsgarg = error
|
|
||||||
#define SET_ERRMSG(msg) \
|
|
||||||
errnum = NULL, errmsg = (msg), errmsgarg = 0
|
|
||||||
const char *errnum = NULL;
|
|
||||||
char *errmsg = NULL;
|
|
||||||
int errmsgarg = 0;
|
|
||||||
bool errmsg_allocated = false;
|
|
||||||
char *buffer;
|
|
||||||
char smallbuf[SMBUFSIZE];
|
|
||||||
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
|
|
||||||
int prev_got_int = got_int;
|
|
||||||
int checking_conversion;
|
|
||||||
bool file_readonly = false; // overwritten file is read-only
|
|
||||||
static char *err_readonly =
|
|
||||||
"is read-only (cannot override: \"W\" in 'cpoptions')";
|
|
||||||
#if defined(UNIX)
|
|
||||||
int made_writable = false; // 'w' bit has been set
|
|
||||||
#endif
|
|
||||||
// writing everything
|
|
||||||
int whole = (start == 1 && end == buf->b_ml.ml_line_count);
|
|
||||||
linenr_T old_line_count = buf->b_ml.ml_line_count;
|
linenr_T old_line_count = buf->b_ml.ml_line_count;
|
||||||
int fileformat;
|
int msg_save = msg_scroll;
|
||||||
int write_bin;
|
|
||||||
struct bw_info write_info; // info for buf_write_bytes()
|
|
||||||
int converted = false;
|
|
||||||
int notconverted = false;
|
|
||||||
char *fenc; // effective 'fileencoding'
|
|
||||||
char *fenc_tofree = NULL; // allocated "fenc"
|
|
||||||
int wb_flags = 0;
|
|
||||||
#ifdef HAVE_ACL
|
|
||||||
vim_acl_T acl = NULL; // ACL copied from original file to
|
|
||||||
// backup or new file
|
|
||||||
#endif
|
|
||||||
int write_undo_file = false;
|
|
||||||
context_sha256_T sha_ctx;
|
|
||||||
unsigned int bkc = get_bkc_value(buf);
|
|
||||||
const pos_T orig_start = buf->b_op_start;
|
|
||||||
const pos_T orig_end = buf->b_op_end;
|
|
||||||
|
|
||||||
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 in secure mode.
|
|
||||||
if (check_secure()) {
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid a crash for a long name.
|
|
||||||
if (strlen(fname) >= MAXPATHL) {
|
|
||||||
emsg(_(e_longname));
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
write_info.bw_iconv_fd = (iconv_t)-1;
|
|
||||||
|
|
||||||
// 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
|
|
||||||
&& !bt_nofilename(buf)
|
|
||||||
&& !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 Windows, 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 && path_fnamecmp(ffname, buf->b_ffname) == 0) {
|
|
||||||
overwriting = true;
|
|
||||||
} else {
|
|
||||||
overwriting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
{
|
|
||||||
aco_save_T aco;
|
aco_save_T aco;
|
||||||
int buf_ffname = false;
|
int buf_ffname = false;
|
||||||
int buf_sfname = false;
|
int buf_sfname = false;
|
||||||
@@ -2247,19 +2094,21 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
|
|||||||
int empty_memline = (buf->b_ml.ml_mfp == NULL);
|
int empty_memline = (buf->b_ml.ml_mfp == NULL);
|
||||||
bufref_T bufref;
|
bufref_T bufref;
|
||||||
|
|
||||||
|
char *sfname = *sfnamep;
|
||||||
|
|
||||||
// Apply PRE autocommands.
|
// Apply PRE autocommands.
|
||||||
// Set curbuf to the buffer to be written.
|
// Set curbuf to the buffer to be written.
|
||||||
// Careful: The autocommands may call buf_write() recursively!
|
// Careful: The autocommands may call buf_write() recursively!
|
||||||
if (ffname == buf->b_ffname) {
|
if (*ffnamep == buf->b_ffname) {
|
||||||
buf_ffname = true;
|
buf_ffname = true;
|
||||||
}
|
}
|
||||||
if (sfname == buf->b_sfname) {
|
if (sfname == buf->b_sfname) {
|
||||||
buf_sfname = true;
|
buf_sfname = true;
|
||||||
}
|
}
|
||||||
if (fname == buf->b_ffname) {
|
if (*fnamep == buf->b_ffname) {
|
||||||
buf_fname_f = true;
|
buf_fname_f = true;
|
||||||
}
|
}
|
||||||
if (fname == buf->b_sfname) {
|
if (*fnamep == buf->b_sfname) {
|
||||||
buf_fname_s = true;
|
buf_fname_s = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2378,12 +2227,12 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
|
|||||||
// changed the number of lines that are to be written (tricky!).
|
// changed the number of lines that are to be written (tricky!).
|
||||||
if (buf->b_ml.ml_line_count != old_line_count) {
|
if (buf->b_ml.ml_line_count != old_line_count) {
|
||||||
if (whole) { // write all
|
if (whole) { // write all
|
||||||
end = buf->b_ml.ml_line_count;
|
*endp = buf->b_ml.ml_line_count;
|
||||||
} else if (buf->b_ml.ml_line_count > old_line_count) { // more lines
|
} else if (buf->b_ml.ml_line_count > old_line_count) { // more lines
|
||||||
end += buf->b_ml.ml_line_count - old_line_count;
|
*endp += buf->b_ml.ml_line_count - old_line_count;
|
||||||
} else { // less lines
|
} else { // less lines
|
||||||
end -= old_line_count - buf->b_ml.ml_line_count;
|
*endp -= old_line_count - buf->b_ml.ml_line_count;
|
||||||
if (end < start) {
|
if (*endp < start) {
|
||||||
no_wait_return--;
|
no_wait_return--;
|
||||||
msg_scroll = msg_save;
|
msg_scroll = msg_save;
|
||||||
emsg(_("E204: Autocommand changed number of lines in unexpected way"));
|
emsg(_("E204: Autocommand changed number of lines in unexpected way"));
|
||||||
@@ -2395,17 +2244,185 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
|
|||||||
// The autocommands may have changed the name of the buffer, which may
|
// The autocommands may have changed the name of the buffer, which may
|
||||||
// be kept in fname, ffname and sfname.
|
// be kept in fname, ffname and sfname.
|
||||||
if (buf_ffname) {
|
if (buf_ffname) {
|
||||||
ffname = buf->b_ffname;
|
*ffnamep = buf->b_ffname;
|
||||||
}
|
}
|
||||||
if (buf_sfname) {
|
if (buf_sfname) {
|
||||||
sfname = buf->b_sfname;
|
*sfnamep = buf->b_sfname;
|
||||||
}
|
}
|
||||||
if (buf_fname_f) {
|
if (buf_fname_f) {
|
||||||
fname = buf->b_ffname;
|
*fnamep = buf->b_ffname;
|
||||||
}
|
}
|
||||||
if (buf_fname_s) {
|
if (buf_fname_s) {
|
||||||
fname = buf->b_sfname;
|
*fnamep = buf->b_sfname;
|
||||||
}
|
}
|
||||||
|
return NOTDONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// buf_write() - write to file "fname" lines "start" through "end"
|
||||||
|
///
|
||||||
|
/// We do our own buffering here because fwrite() is so slow.
|
||||||
|
///
|
||||||
|
/// If "forceit" is true, we don't care for errors when attempting backups.
|
||||||
|
/// In case of an error everything possible is done to restore the original
|
||||||
|
/// file. But when "forceit" is true, we risk losing it.
|
||||||
|
///
|
||||||
|
/// When "reset_changed" is true and "append" == false and "start" == 1 and
|
||||||
|
/// "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
|
||||||
|
///
|
||||||
|
/// This function must NOT use NameBuff (because it's called by autowrite()).
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// @param eap for forced 'ff' and 'fenc', can be NULL!
|
||||||
|
/// @param append append to the file
|
||||||
|
///
|
||||||
|
/// @return FAIL for failure, OK otherwise
|
||||||
|
int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T end, exarg_T *eap,
|
||||||
|
int append, int forceit, int reset_changed, int filtering)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
char *backup = NULL;
|
||||||
|
int backup_copy = false; // copy the original file?
|
||||||
|
int dobackup;
|
||||||
|
char *ffname;
|
||||||
|
char *wfname = NULL; // name of file to write to
|
||||||
|
char *s;
|
||||||
|
char *ptr;
|
||||||
|
char c;
|
||||||
|
int len;
|
||||||
|
linenr_T lnum;
|
||||||
|
long nchars;
|
||||||
|
#define SET_ERRMSG_NUM(num, msg) \
|
||||||
|
errnum = (num), errmsg = (msg), errmsgarg = 0
|
||||||
|
#define SET_ERRMSG_ARG(msg, error) \
|
||||||
|
errnum = NULL, errmsg = (msg), errmsgarg = error
|
||||||
|
#define SET_ERRMSG(msg) \
|
||||||
|
errnum = NULL, errmsg = (msg), errmsgarg = 0
|
||||||
|
const char *errnum = NULL;
|
||||||
|
char *errmsg = NULL;
|
||||||
|
int errmsgarg = 0;
|
||||||
|
bool errmsg_allocated = false;
|
||||||
|
char *buffer;
|
||||||
|
char smallbuf[SMBUFSIZE];
|
||||||
|
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
|
||||||
|
int prev_got_int = got_int;
|
||||||
|
int checking_conversion;
|
||||||
|
bool file_readonly = false; // overwritten file is read-only
|
||||||
|
static char *err_readonly =
|
||||||
|
"is read-only (cannot override: \"W\" in 'cpoptions')";
|
||||||
|
#if defined(UNIX)
|
||||||
|
int made_writable = false; // 'w' bit has been set
|
||||||
|
#endif
|
||||||
|
// writing everything
|
||||||
|
int whole = (start == 1 && end == buf->b_ml.ml_line_count);
|
||||||
|
int fileformat;
|
||||||
|
int write_bin;
|
||||||
|
struct bw_info write_info; // info for buf_write_bytes()
|
||||||
|
int converted = false;
|
||||||
|
int notconverted = false;
|
||||||
|
char *fenc; // effective 'fileencoding'
|
||||||
|
char *fenc_tofree = NULL; // allocated "fenc"
|
||||||
|
int wb_flags = 0;
|
||||||
|
#ifdef HAVE_ACL
|
||||||
|
vim_acl_T acl = NULL; // ACL copied from original file to
|
||||||
|
// backup or new file
|
||||||
|
#endif
|
||||||
|
int write_undo_file = false;
|
||||||
|
context_sha256_T sha_ctx;
|
||||||
|
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 in secure mode.
|
||||||
|
if (check_secure()) {
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid a crash for a long name.
|
||||||
|
if (strlen(fname) >= MAXPATHL) {
|
||||||
|
emsg(_(e_longname));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
write_info.bw_iconv_fd = (iconv_t)-1;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
&& !bt_nofilename(buf)
|
||||||
|
&& !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 Windows, 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 && path_fnamecmp(ffname, buf->b_ffname) == 0) {
|
||||||
|
overwriting = true;
|
||||||
|
} else {
|
||||||
|
overwriting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
no_wait_return++; // don't wait for return yet
|
||||||
|
|
||||||
|
const pos_T orig_start = buf->b_op_start;
|
||||||
|
const pos_T orig_end = buf->b_op_end;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
int res = buf_write_do_autocmds(buf, &fname, &sfname, &ffname, start, &end, eap, append,
|
||||||
|
filtering, reset_changed, overwriting, whole, orig_start,
|
||||||
|
orig_end);
|
||||||
|
if (res != NOTDONE) {
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmdmod.cmod_flags & CMOD_LOCKMARKS) {
|
if (cmdmod.cmod_flags & CMOD_LOCKMARKS) {
|
||||||
|
Reference in New Issue
Block a user