refactor(fileio.c): factor out autocmd handling from buf_write()

This commit is contained in:
Lewis Russell
2023-01-30 13:39:15 +00:00
parent f4b1f0d042
commit b001f25204

View File

@@ -2076,167 +2076,14 @@ char *new_file_message(void)
return shortmess(SHM_NEW) ? _("[New]") : _("[New File]");
}
/// 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)
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,
bool filtering, bool reset_changed, bool overwriting, bool whole,
const pos_T orig_start, const pos_T orig_end)
{
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;
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);
const pos_T orig_start = buf->b_op_start;
const pos_T orig_end = buf->b_op_end;
int msg_save = msg_scroll;
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;
int buf_ffname = 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);
bufref_T bufref;
char *sfname = *sfnamep;
// Apply PRE autocommands.
// Set curbuf to the buffer to be written.
// Careful: The autocommands may call buf_write() recursively!
if (ffname == buf->b_ffname) {
if (*ffnamep == buf->b_ffname) {
buf_ffname = true;
}
if (sfname == buf->b_sfname) {
buf_sfname = true;
}
if (fname == buf->b_ffname) {
if (*fnamep == buf->b_ffname) {
buf_fname_f = true;
}
if (fname == buf->b_sfname) {
if (*fnamep == buf->b_sfname) {
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!).
if (buf->b_ml.ml_line_count != old_line_count) {
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
end += buf->b_ml.ml_line_count - old_line_count;
*endp += 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) {
*endp -= old_line_count - buf->b_ml.ml_line_count;
if (*endp < start) {
no_wait_return--;
msg_scroll = msg_save;
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
// be kept in fname, ffname and sfname.
if (buf_ffname) {
ffname = buf->b_ffname;
*ffnamep = buf->b_ffname;
}
if (buf_sfname) {
sfname = buf->b_sfname;
*sfnamep = buf->b_sfname;
}
if (buf_fname_f) {
fname = buf->b_ffname;
*fnamep = buf->b_ffname;
}
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) {