mirror of
https://github.com/neovim/neovim.git
synced 2025-09-12 14:28:18 +00:00
Replace mch_rename and vim_rename with libuv
This commit is contained in:
13
src/eval.c
13
src/eval.c
@@ -12329,13 +12329,14 @@ static void f_remove(typval_T *argvars, typval_T *rettv)
|
||||
*/
|
||||
static void f_rename(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u buf[NUMBUFLEN];
|
||||
|
||||
if (check_restricted() || check_secure())
|
||||
if (check_restricted() || check_secure()) {
|
||||
rettv->vval.v_number = -1;
|
||||
else
|
||||
rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]),
|
||||
get_tv_string_buf(&argvars[1], buf));
|
||||
} else {
|
||||
char_u buf[NUMBUFLEN];
|
||||
char_u *from = get_tv_string(&argvars[0]);
|
||||
char_u *to = get_tv_string_buf(&argvars[1], buf);
|
||||
rettv->vval.v_number = os_rename(from, to) == OK ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -1711,7 +1711,7 @@ void write_viminfo(char_u *file, int forceit)
|
||||
* In case of an error keep the original viminfo file.
|
||||
* Otherwise rename the newly written file.
|
||||
*/
|
||||
if (viminfo_errcnt || vim_rename(tempname, fname) == -1)
|
||||
if (viminfo_errcnt || os_rename(tempname, fname) == FAIL)
|
||||
mch_remove(tempname);
|
||||
|
||||
}
|
||||
|
175
src/fileio.c
175
src/fileio.c
@@ -3314,7 +3314,7 @@ nobackup:
|
||||
* If the renaming of the original file to the backup file
|
||||
* works, quit here.
|
||||
*/
|
||||
if (vim_rename(fname, backup) == 0)
|
||||
if (os_rename(fname, backup) == OK)
|
||||
break;
|
||||
|
||||
vim_free(backup); /* don't do the rename below */
|
||||
@@ -3520,18 +3520,18 @@ restore_backup:
|
||||
/*
|
||||
* 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.
|
||||
* This may not work if the os_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);
|
||||
os_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);
|
||||
os_rename(backup, fname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3827,7 +3827,7 @@ restore_backup:
|
||||
close(fd); /* ignore errors for closing read file */
|
||||
}
|
||||
} else {
|
||||
if (vim_rename(backup, fname) == 0)
|
||||
if (os_rename(backup, fname) == OK)
|
||||
end = 1;
|
||||
}
|
||||
}
|
||||
@@ -3934,7 +3934,7 @@ restore_backup:
|
||||
if (org == NULL)
|
||||
EMSG(_("E205: Patchmode: can't save original file"));
|
||||
else if (mch_stat(org, &st) < 0) {
|
||||
vim_rename(backup, (char_u *)org);
|
||||
os_rename(backup, (char_u *)org);
|
||||
vim_free(backup); /* don't delete the file */
|
||||
backup = NULL;
|
||||
#ifdef UNIX
|
||||
@@ -4969,169 +4969,6 @@ int tag_fgets(char_u *buf, int size, FILE *fp)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* rename() only works if both files are on the same file system, this
|
||||
* function will (attempts to?) copy the file across if rename fails -- webb
|
||||
* Return -1 for failure, 0 for success.
|
||||
*/
|
||||
int vim_rename(char_u *from, char_u *to)
|
||||
{
|
||||
int fd_in;
|
||||
int fd_out;
|
||||
int n;
|
||||
char *errmsg = NULL;
|
||||
char *buffer;
|
||||
struct stat st;
|
||||
long perm;
|
||||
#ifdef HAVE_ACL
|
||||
vim_acl_T acl; /* ACL from original file */
|
||||
#endif
|
||||
int use_tmp_file = FALSE;
|
||||
|
||||
/*
|
||||
* When the names are identical, there is nothing to do. When they refer
|
||||
* to the same file (ignoring case and slash/backslash differences) but
|
||||
* the file name differs we need to go through a temp file.
|
||||
*/
|
||||
if (fnamecmp(from, to) == 0) {
|
||||
if (p_fic && STRCMP(path_tail(from), path_tail(to)) != 0)
|
||||
use_tmp_file = TRUE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail if the "from" file doesn't exist. Avoids that "to" is deleted.
|
||||
*/
|
||||
if (mch_stat((char *)from, &st) < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef UNIX
|
||||
{
|
||||
struct stat st_to;
|
||||
|
||||
/* It's possible for the source and destination to be the same file.
|
||||
* This happens when "from" and "to" differ in case and are on a FAT32
|
||||
* filesystem. In that case go through a temp file name. */
|
||||
if (mch_stat((char *)to, &st_to) >= 0
|
||||
&& st.st_dev == st_to.st_dev
|
||||
&& st.st_ino == st_to.st_ino)
|
||||
use_tmp_file = TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (use_tmp_file) {
|
||||
char tempname[MAXPATHL + 1];
|
||||
|
||||
/*
|
||||
* Find a name that doesn't exist and is in the same directory.
|
||||
* Rename "from" to "tempname" and then rename "tempname" to "to".
|
||||
*/
|
||||
if (STRLEN(from) >= MAXPATHL - 5)
|
||||
return -1;
|
||||
STRCPY(tempname, from);
|
||||
for (n = 123; n < 99999; ++n) {
|
||||
sprintf((char *)path_tail((char_u *)tempname), "%d", n);
|
||||
if (mch_stat(tempname, &st) < 0) {
|
||||
if (rename((char *)from, tempname) == 0) {
|
||||
if (rename(tempname, (char *)to) == 0)
|
||||
return 0;
|
||||
/* Strange, the second step failed. Try moving the
|
||||
* file back and return failure. */
|
||||
rename(tempname, (char *)from);
|
||||
return -1;
|
||||
}
|
||||
/* If it fails for one temp name it will most likely fail
|
||||
* for any temp name, give up. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the "to" file, this is required on some systems to make the
|
||||
* rename() work, on other systems it makes sure that we don't have
|
||||
* two files when the rename() fails.
|
||||
*/
|
||||
|
||||
mch_remove(to);
|
||||
|
||||
/*
|
||||
* First try a normal rename, return if it works.
|
||||
*/
|
||||
if (rename((char *)from, (char *)to) == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Rename() failed, try copying the file.
|
||||
*/
|
||||
perm = os_getperm(from);
|
||||
#ifdef HAVE_ACL
|
||||
/* For systems that support ACL: get the ACL from the original file. */
|
||||
acl = mch_get_acl(from);
|
||||
#endif
|
||||
fd_in = mch_open((char *)from, O_RDONLY|O_EXTRA, 0);
|
||||
if (fd_in == -1) {
|
||||
#ifdef HAVE_ACL
|
||||
mch_free_acl(acl);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create the new file with same permissions as the original. */
|
||||
fd_out = mch_open((char *)to,
|
||||
O_CREAT|O_EXCL|O_WRONLY|O_EXTRA|O_NOFOLLOW, (int)perm);
|
||||
if (fd_out == -1) {
|
||||
close(fd_in);
|
||||
#ifdef HAVE_ACL
|
||||
mch_free_acl(acl);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer = (char *)alloc(BUFSIZE);
|
||||
if (buffer == NULL) {
|
||||
close(fd_out);
|
||||
close(fd_in);
|
||||
#ifdef HAVE_ACL
|
||||
mch_free_acl(acl);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((n = read_eintr(fd_in, buffer, BUFSIZE)) > 0)
|
||||
if (write_eintr(fd_out, buffer, n) != n) {
|
||||
errmsg = _("E208: Error writing to \"%s\"");
|
||||
break;
|
||||
}
|
||||
|
||||
vim_free(buffer);
|
||||
close(fd_in);
|
||||
if (close(fd_out) < 0)
|
||||
errmsg = _("E209: Error closing \"%s\"");
|
||||
if (n < 0) {
|
||||
errmsg = _("E210: Error reading \"%s\"");
|
||||
to = from;
|
||||
}
|
||||
#ifndef UNIX /* for Unix mch_open() already set the permission */
|
||||
os_setperm(to, perm);
|
||||
#endif
|
||||
#ifdef HAVE_ACL
|
||||
mch_set_acl(to, acl);
|
||||
mch_free_acl(acl);
|
||||
#endif
|
||||
#ifdef HAVE_SELINUX
|
||||
mch_copy_sec(from, to);
|
||||
#endif
|
||||
if (errmsg != NULL) {
|
||||
EMSG2(errmsg, to);
|
||||
return -1;
|
||||
}
|
||||
mch_remove(from);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int already_warned = FALSE;
|
||||
|
||||
/*
|
||||
|
@@ -39,7 +39,6 @@ char_u *buf_modname(int shortname, char_u *fname, char_u *ext,
|
||||
int prepend_dot);
|
||||
int vim_fgets(char_u *buf, int size, FILE *fp);
|
||||
int tag_fgets(char_u *buf, int size, FILE *fp);
|
||||
int vim_rename(char_u *from, char_u *to);
|
||||
int check_timestamps(int focus);
|
||||
int buf_check_timestamp(buf_T *buf, int focus);
|
||||
void buf_reload(buf_T *buf, int orig_mode);
|
||||
|
@@ -598,7 +598,7 @@ void ml_setname(buf_T *buf)
|
||||
}
|
||||
|
||||
/* try to rename the swap file */
|
||||
if (vim_rename(mfp->mf_fname, fname) == 0) {
|
||||
if (os_rename(mfp->mf_fname, fname) == OK) {
|
||||
success = TRUE;
|
||||
vim_free(mfp->mf_fname);
|
||||
mfp->mf_fname = fname;
|
||||
|
45
src/os/fs.c
45
src/os/fs.c
@@ -5,6 +5,9 @@
|
||||
#include "misc1.h"
|
||||
#include "misc2.h"
|
||||
|
||||
// Many fs functions from libuv return that value on success.
|
||||
static const int kLibuvSuccess = 0;
|
||||
|
||||
int os_chdir(const char *path) {
|
||||
if (p_verbose >= 5) {
|
||||
verbose_enter();
|
||||
@@ -19,7 +22,7 @@ int os_dirname(char_u *buf, size_t len)
|
||||
assert(buf && len);
|
||||
|
||||
int errno;
|
||||
if ((errno = uv_cwd((char *)buf, &len)) != 0) {
|
||||
if ((errno = uv_cwd((char *)buf, &len)) != kLibuvSuccess) {
|
||||
vim_strncpy(buf, (char_u *)uv_strerror(errno), len - 1);
|
||||
return FAIL;
|
||||
}
|
||||
@@ -46,11 +49,11 @@ int os_full_dir_name(char *directory, char *buffer, int len)
|
||||
}
|
||||
|
||||
// We have to get back to the current dir at the end, check if that works.
|
||||
if (os_chdir(old_dir) != 0) {
|
||||
if (os_chdir(old_dir) != kLibuvSuccess) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (os_chdir(directory) != 0) {
|
||||
if (os_chdir(directory) != kLibuvSuccess) {
|
||||
// Do not return immediatly since we may be in the wrong directory.
|
||||
retval = FAIL;
|
||||
}
|
||||
@@ -60,7 +63,7 @@ int os_full_dir_name(char *directory, char *buffer, int len)
|
||||
retval = FAIL;
|
||||
}
|
||||
|
||||
if (os_chdir(old_dir) != 0) {
|
||||
if (os_chdir(old_dir) != kLibuvSuccess) {
|
||||
// That shouldn't happen, since we've tested if it works.
|
||||
retval = FAIL;
|
||||
EMSG(_(e_prev_dir));
|
||||
@@ -241,11 +244,11 @@ int os_stat(const char_u *name, uv_stat_t *statbuf)
|
||||
*statbuf = request.statbuf;
|
||||
uv_fs_req_cleanup(&request);
|
||||
|
||||
if (result == 0) {
|
||||
if (result == kLibuvSuccess) {
|
||||
return OK;
|
||||
} else {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
int32_t os_getperm(const char_u *name)
|
||||
@@ -265,11 +268,11 @@ int os_setperm(const char_u *name, int perm)
|
||||
(const char*)name, perm, NULL);
|
||||
uv_fs_req_cleanup(&request);
|
||||
|
||||
if (result != 0) {
|
||||
return FAIL;
|
||||
} else {
|
||||
if (result == kLibuvSuccess) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
int os_file_exists(const char_u *name)
|
||||
@@ -277,18 +280,18 @@ int os_file_exists(const char_u *name)
|
||||
uv_stat_t statbuf;
|
||||
if (os_stat(name, &statbuf) == OK) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int os_file_is_readonly(const char *name)
|
||||
{
|
||||
if (access(name, W_OK) == 0) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int os_file_is_writable(const char *name)
|
||||
@@ -302,3 +305,17 @@ int os_file_is_writable(const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int os_rename(const char_u *path, const char_u *new_path)
|
||||
{
|
||||
uv_fs_t request;
|
||||
int result = uv_fs_rename(uv_default_loop(), &request,
|
||||
(const char *)path, (const char *)new_path, NULL);
|
||||
uv_fs_req_cleanup(&request);
|
||||
|
||||
if (result == kLibuvSuccess) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
@@ -73,6 +73,11 @@ int os_file_is_readonly(const char *name);
|
||||
/// @return `2` for a directory which we have rights to write into.
|
||||
int os_file_is_writable(const char *name);
|
||||
|
||||
/// Rename a file or directory.
|
||||
///
|
||||
/// @return `OK` for success, `FAIL` for failure.
|
||||
int os_rename(const char_u *path, const char_u *new_path);
|
||||
|
||||
long_u os_total_mem(int special);
|
||||
const char *os_getenv(const char *name);
|
||||
int os_setenv(const char *name, const char *value, int overwrite);
|
||||
|
@@ -21,6 +21,7 @@ int os_setperm(char_u *name, long perm);
|
||||
int os_file_exists(const char_u *name);
|
||||
int os_file_is_readonly(char *fname);
|
||||
int os_file_is_writable(const char *name);
|
||||
int os_rename(const char_u *path, const char_u *new_path);
|
||||
]]
|
||||
|
||||
-- import constants parsed by ffi
|
||||
@@ -361,12 +362,43 @@ describe 'fs function', ->
|
||||
it 'returns 2 when given a folder with rights to write into', ->
|
||||
eq 2, os_file_is_writable 'unit-test-directory'
|
||||
|
||||
describe 'os_file_exists', ->
|
||||
describe 'file operations', ->
|
||||
os_file_exists = (filename) ->
|
||||
fs.os_file_exists (to_cstr filename)
|
||||
|
||||
it 'returns FALSE when given a non-existing file', ->
|
||||
eq FALSE, (os_file_exists 'non-existing-file')
|
||||
os_rename = (path, new_path) ->
|
||||
fs.os_rename (to_cstr path), (to_cstr new_path)
|
||||
|
||||
it 'returns TRUE when given an existing file', ->
|
||||
eq TRUE, (os_file_exists 'unit-test-directory/test.file')
|
||||
describe 'os_file_exists', ->
|
||||
it 'returns FALSE when given a non-existing file', ->
|
||||
eq FALSE, (os_file_exists 'non-existing-file')
|
||||
|
||||
it 'returns TRUE when given an existing file', ->
|
||||
eq TRUE, (os_file_exists 'unit-test-directory/test.file')
|
||||
|
||||
describe 'os_rename', ->
|
||||
test = 'unit-test-directory/test.file'
|
||||
not_exist = 'unit-test-directory/not_exist.file'
|
||||
|
||||
it 'can rename file if destination file does not exist', ->
|
||||
eq OK, (os_rename test, not_exist)
|
||||
eq FALSE, (os_file_exists test)
|
||||
eq TRUE, (os_file_exists not_exist)
|
||||
eq OK, (os_rename not_exist, test) -- restore test file
|
||||
|
||||
it 'fail if source file does not exist', ->
|
||||
eq FAIL, (os_rename not_exist, test)
|
||||
|
||||
it 'can overwrite destination file if it exists', ->
|
||||
other = 'unit-test-directory/other.file'
|
||||
file = io.open other, 'w'
|
||||
file\write 'other'
|
||||
file\flush!
|
||||
file\close!
|
||||
|
||||
eq OK, (os_rename other, test)
|
||||
eq FALSE, (os_file_exists other)
|
||||
eq TRUE, (os_file_exists test)
|
||||
file = io.open test, 'r'
|
||||
eq 'other', (file\read '*all')
|
||||
file\close!
|
||||
|
Reference in New Issue
Block a user