📄 fileio.c
字号:
else
backup_ext = p_bex;
if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0)
overwriting = TRUE;
else
overwriting = FALSE;
/*
* Disallow writing from .exrc and .vimrc in current directory for
* security reasons.
*/
if (check_secure())
return FAIL;
if (exiting)
settmode(TMODE_COOK); /* when exiting allow typahead 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 AUTOCMD
/*
* Apply PRE aucocommands.
* Set curbuf to the buffer to be written.
* Careful: The autocommands may call buf_write() recursively!
*/
save_buf = curbuf;
curbuf = buf;
curwin->w_buffer = buf;
if (append)
apply_autocmds(EVENT_FILEAPPENDPRE, fname, fname, FALSE, curbuf);
else if (filtering)
apply_autocmds(EVENT_FILTERWRITEPRE, NULL, fname, FALSE, curbuf);
else if (reset_changed && whole)
apply_autocmds(EVENT_BUFWRITEPRE, fname, fname, FALSE, curbuf);
else
apply_autocmds(EVENT_FILEWRITEPRE, fname, fname, FALSE, curbuf);
/*
* If the autocommands deleted or unloaded the buffer, give an error
* message.
*/
if (!buf_valid(buf) || buf->b_ml.ml_mfp == NULL)
{
--no_wait_return;
msg_scroll = msg_save;
EMSG("Autocommands deleted or unloaded buffer to be written");
return FAIL;
}
/*
* If the autocommands didn't change the current buffer, go back to the
* original current buffer, if it still exists.
*/
if (curbuf == buf && buf_valid(save_buf))
{
curbuf = save_buf;
curwin->w_buffer = save_buf;
}
/*
* 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) /* writing 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("Autocommand changed number of lines in unexpected way");
return FAIL;
}
}
}
#endif
if (shortmess(SHM_OVER))
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;
#if defined(UNIX) && !defined(ARCHIE)
/* get information about original file (if there is one) */
st_old.st_dev = st_old.st_ino = 0;
perm = -1;
if (stat((char *)fname, &st_old))
newfile = TRUE;
else
{
if (!S_ISREG(st_old.st_mode)) /* not a file */
{
if (S_ISDIR(st_old.st_mode))
errmsg = (char_u *)"is a directory";
else
errmsg = (char_u *)"is not a file";
goto fail;
}
if (overwriting)
{
retval = check_mtime(buf, &st_old);
if (retval == FAIL)
goto fail;
}
perm = st_old.st_mode;
}
/*
* If we are not appending or filtering, the file exists, and the
* 'writebackup', 'backup' or 'patchmode' option is set, try to make a backup
* copy of the file.
*/
if (!append && !filtering && perm >= 0 && (p_wb || p_bk || *p_pm != NUL)
&& (fd = open((char *)fname, O_RDONLY | O_EXTRA
#ifndef macintosh
, 0 /* mode (avoid UMR) */
#endif
)) >= 0)
{
int bfd, buflen;
char_u copybuf[BUFSIZE + 1], *wp;
int some_error = FALSE;
struct stat st_new;
char_u *dirp;
char_u *rootname;
#ifndef SHORT_FNAME
int did_set_shortname;
#endif
/*
* 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)
{
st_new.st_dev = st_new.st_ino = 0;
st_new.st_gid = 0;
/*
* 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;
}
#ifndef SHORT_FNAME
did_set_shortname = FALSE;
#endif
/*
* May try twice if 'shortname' not set.
*/
for (;;)
{
/*
* Make backup file name.
*/
backup = buf_modname(
#ifdef SHORT_FNAME
TRUE,
#else
(buf->b_p_sn || buf->b_shortname),
#endif
rootname, backup_ext, FALSE);
if (backup == NULL)
{
some_error = TRUE; /* out of memory */
vim_free(rootname);
goto nobackup;
}
/*
* Check if backup file already exists.
*/
if (!stat((char *)backup, &st_new))
{
/*
* 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; /* there is no backup file to delete */
#ifndef SHORT_FNAME
/*
* 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;
#endif
break;
}
/*
* 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' && !stat((char *)backup, &st_new))
--*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);
bfd = open((char *)backup, O_WRONLY | O_CREAT | O_EXTRA, 0666);
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);
/*
* 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
#else
chown((char *)backup, (uid_t)-1, st_old.st_gid) != 0
#endif
)
mch_setperm(backup, (perm & 0707) | ((perm & 07) << 3));
/* copy the file. */
while ((buflen = read(fd, (char *)copybuf, BUFSIZE)) > 0)
{
if (write_buf(bfd, copybuf, buflen) == FAIL)
{
errmsg = (char_u *)"Can't write to backup file (use ! to override)";
break;
}
}
if (close(bfd) < 0 && errmsg == NULL)
errmsg = (char_u *)"Close error for backup file (use ! to override)";
if (buflen < 0)
errmsg = (char_u *)"Can't read file for backup (use ! to override)";
set_file_time(backup, st_old.st_atime, st_old.st_mtime);
break;
}
}
}
nobackup:
close(fd); /* ignore errors for closing read file */
if (backup == NULL && errmsg == NULL)
errmsg = (char_u *)"Cannot create backup file (use ! to override)";
/* ignore errors when forceit is TRUE */
if ((some_error || errmsg) && !forceit)
{
retval = FAIL;
goto fail;
}
errmsg = NULL;
}
/* When using ":w!" and the file was read-only: make it writable */
if (forceit && st_old.st_uid == getuid() && perm >= 0 && !(perm & 0200)
&& vim_strchr(p_cpo, CPO_FWRITE) == NULL)
{
perm |= 0200;
(void)mch_setperm(fname, perm);
made_writable = TRUE;
}
#else /* end of UNIX, start of the rest */
/*
* If we are not appending, the file exists, and the 'writebackup' or
* 'backup' option is set, make a backup.
* Do not make any backup, if "writebackup" and "backup" are
* both switched off. This helps when editing large files on
* almost-full disks. (jw)
*/
perm = mch_getperm(fname);
if (perm < 0)
newfile = TRUE;
else if (mch_isdir(fname))
{
errmsg = (char_u *)"is a directory";
goto fail;
}
else if (overwriting)
{
struct stat st;
if (stat((char *)fname, &st) >= 0)
{
retval = check_mtime(buf, &st);
if (retval == FAIL)
goto fail;
}
}
if (!append && !filtering && perm >= 0 && (p_wb || p_bk || *p_pm != NUL))
{
char_u *dirp;
char_u *p;
char_u *rootname;
/*
* 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(
#ifdef SHORT_FNAME
TRUE,
#else
(buf->b_p_sn || buf->b_shortname),
#endif
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.
*/
#ifdef AMIGA
/*
* With MSDOS-compatible filesystems (crossdos, messydos) it is
* possible that the name of the backup file is the same as the
* original file. To avoid the chance of accidently deleting the
* original file (horror!) we lock it during the remove.
* This should not happen with ":w", because startscript()
* should detect this problem and set buf->b_shortname,
* causing modname to return a correct ".bak" file name. This
* problem does exist with ":w file name", but then the
* original file will be somewhere else so the backup isn't
* really important. If autoscripting is off the rename may
* fail.
*/
flock = Lock((UBYTE *)fname, (long)ACCESS_READ);
#endif
mch_remove(backup);
#ifdef AMIGA
if (flock)
UnLock(flock);
#endif
/*
* 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 *)"Can't make backup file (use ! to override)";
goto fail;
}
}
#endif /* UNIX */
/* When using ":w!" and writing to the current file, readonly makes no
* sense, reset it */
if (forceit && overwriting)
buf->b_p_ro = FALSE;
/*
* 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 && !otherfile(ffname) &&
!(exiting && backup != NULL))
ml_preserve(buf, FALSE);
/*
* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -