mirror of
https://github.com/neovim/neovim.git
synced 2025-09-17 08:48:16 +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]");
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
|
Reference in New Issue
Block a user