summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorShougo Matsushita <Shougo.Matsu@gmail.com>2024-06-03 22:59:27 +0200
committerChristian Brabandt <cb@256bit.org>2024-06-03 23:01:40 +0200
commit60c8743ab6c90e402e6ed49d27455ef7e5698abe (patch)
tree441cc398a542d10a60cd9c1dd7862ebcf0f1353e /src
parent0a0830624a260660c7fa692ecb7e6e5de09114ba (diff)
patch 9.1.0465: missing filecopy() functionv9.1.0465
Problem: missing filecopy() function Solution: implement filecopy() Vim script function (Shougo Matsushita) closes: #12346 Co-authored-by: zeertzjq <zeertzjq@outlook.com> Signed-off-by: Shougo Matsushita <Shougo.Matsu@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
Diffstat (limited to 'src')
-rw-r--r--src/evalfunc.c2
-rw-r--r--src/fileio.c75
-rw-r--r--src/filepath.c25
-rw-r--r--src/proto/fileio.pro1
-rw-r--r--src/proto/filepath.pro1
-rw-r--r--src/testdir/Make_all.mak2
-rw-r--r--src/testdir/test_filecopy.vim72
-rw-r--r--src/version.c2
8 files changed, 166 insertions, 14 deletions
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 3028cf9754..9720691b44 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -2007,6 +2007,8 @@ static funcentry_T global_functions[] =
ret_void, f_feedkeys},
{"file_readable", 1, 1, FEARG_1, arg1_string, // obsolete
ret_number_bool, f_filereadable},
+ {"filecopy", 2, 2, FEARG_1, arg2_string,
+ ret_number_bool, f_filecopy},
{"filereadable", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_filereadable},
{"filewritable", 1, 1, FEARG_1, arg1_string,
diff --git a/src/fileio.c b/src/fileio.c
index 07e05fc802..e7f333208e 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -3770,19 +3770,12 @@ vim_fgets(char_u *buf, int size, FILE *fp)
int
vim_rename(char_u *from, char_u *to)
{
- int fd_in;
- int fd_out;
int n;
- char *errmsg = NULL;
- char *buffer;
+ int ret;
#ifdef AMIGA
BPTR flock;
#endif
stat_T st;
- long perm;
-#ifdef HAVE_ACL
- vim_acl_T acl; // ACL from original file
-#endif
int use_tmp_file = FALSE;
/*
@@ -3903,6 +3896,61 @@ vim_rename(char_u *from, char_u *to)
/*
* Rename() failed, try copying the file.
*/
+ ret = vim_copyfile(from, to);
+ if (ret != OK)
+ return -1;
+
+ /*
+ * Remove copied original file
+ */
+ if (mch_stat((char *)from, &st) >= 0)
+ mch_remove(from);
+
+ return 0;
+}
+
+
+/*
+ * Create the new file with same permissions as the original.
+ * Return -1 for failure, 0 for success.
+ */
+ int
+vim_copyfile(char_u *from, char_u *to)
+{
+ int fd_in;
+ int fd_out;
+ int n;
+ char *errmsg = NULL;
+ char *buffer;
+ long perm;
+#ifdef HAVE_ACL
+ vim_acl_T acl; // ACL from original file
+#endif
+
+#ifdef HAVE_READLINK
+ int ret;
+ int len;
+ stat_T st;
+ char linkbuf[MAXPATHL + 1];
+
+ ret = mch_lstat((char *)from, &st);
+ if (ret >= 0 && S_ISLNK(st.st_mode))
+ {
+ ret = FAIL;
+
+ len = readlink((char *)from, linkbuf, MAXPATHL);
+ if (len > 0)
+ {
+ linkbuf[len] = NUL;
+
+ // Create link
+ ret = symlink(linkbuf, (char *)to);
+ }
+
+ return ret == 0 ? OK : FAIL;
+ }
+#endif
+
perm = mch_getperm(from);
#ifdef HAVE_ACL
// For systems that support ACL: get the ACL from the original file.
@@ -3914,7 +3962,7 @@ vim_rename(char_u *from, char_u *to)
#ifdef HAVE_ACL
mch_free_acl(acl);
#endif
- return -1;
+ return FAIL;
}
// Create the new file with same permissions as the original.
@@ -3926,7 +3974,7 @@ vim_rename(char_u *from, char_u *to)
#ifdef HAVE_ACL
mch_free_acl(acl);
#endif
- return -1;
+ return FAIL;
}
buffer = alloc(WRITEBUFSIZE);
@@ -3937,7 +3985,7 @@ vim_rename(char_u *from, char_u *to)
#ifdef HAVE_ACL
mch_free_acl(acl);
#endif
- return -1;
+ return FAIL;
}
while ((n = read_eintr(fd_in, buffer, WRITEBUFSIZE)) > 0)
@@ -3969,10 +4017,9 @@ vim_rename(char_u *from, char_u *to)
if (errmsg != NULL)
{
semsg(errmsg, to);
- return -1;
+ return FAIL;
}
- mch_remove(from);
- return 0;
+ return OK;
}
static int already_warned = FALSE;
diff --git a/src/filepath.c b/src/filepath.c
index e68075a45e..9f68d7c688 100644
--- a/src/filepath.c
+++ b/src/filepath.c
@@ -2649,6 +2649,31 @@ f_browsedir(typval_T *argvars UNUSED, typval_T *rettv)
rettv->v_type = VAR_STRING;
}
+/*
+ * "filecopy()" function
+ */
+ void
+f_filecopy(typval_T *argvars, typval_T *rettv)
+{
+ char_u *from;
+ stat_T st;
+
+ rettv->vval.v_number = FALSE;
+
+ if (check_restricted() || check_secure()
+ || check_for_string_arg(argvars, 0) == FAIL
+ || check_for_string_arg(argvars, 1) == FAIL)
+ return;
+
+ from = tv_get_string(&argvars[0]);
+
+ if (mch_lstat((char *)from, &st) >= 0
+ && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
+ rettv->vval.v_number = vim_copyfile(
+ tv_get_string(&argvars[0]),
+ tv_get_string(&argvars[1])) == OK ? TRUE : FALSE;
+}
+
#endif // FEAT_EVAL
/*
diff --git a/src/proto/fileio.pro b/src/proto/fileio.pro
index 3f7b30d448..8d978709fc 100644
--- a/src/proto/fileio.pro
+++ b/src/proto/fileio.pro
@@ -26,6 +26,7 @@ char_u *modname(char_u *fname, char_u *ext, int prepend_dot);
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 vim_rename(char_u *from, char_u *to);
+int vim_copyfile(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, int reload_options);
diff --git a/src/proto/filepath.pro b/src/proto/filepath.pro
index 53fa4ec3ff..46f51cb36f 100644
--- a/src/proto/filepath.pro
+++ b/src/proto/filepath.pro
@@ -6,6 +6,7 @@ void f_chdir(typval_T *argvars, typval_T *rettv);
void f_delete(typval_T *argvars, typval_T *rettv);
void f_executable(typval_T *argvars, typval_T *rettv);
void f_exepath(typval_T *argvars, typval_T *rettv);
+void f_filecopy(typval_T *argvars, typval_T *rettv);
void f_filereadable(typval_T *argvars, typval_T *rettv);
void f_filewritable(typval_T *argvars, typval_T *rettv);
void f_finddir(typval_T *argvars, typval_T *rettv);
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index a80b1300e5..e31d2b5f3e 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -143,6 +143,7 @@ NEW_TESTS = \
test_file_perm \
test_file_size \
test_filechanged \
+ test_filecopy \
test_fileformat \
test_filetype \
test_filter_cmd \
@@ -404,6 +405,7 @@ NEW_TESTS_RES = \
test_expr.res \
test_file_size.res \
test_filechanged.res \
+ test_filecopy.res \
test_fileformat.res \
test_filetype.res \
test_filter_cmd.res \
diff --git a/src/testdir/test_filecopy.vim b/src/testdir/test_filecopy.vim
new file mode 100644
index 0000000000..b526dce7b8
--- /dev/null
+++ b/src/testdir/test_filecopy.vim
@@ -0,0 +1,72 @@
+" Test filecopy()
+
+source check.vim
+source shared.vim
+
+func Test_copy_file_to_file()
+ call writefile(['foo'], 'Xcopy1')
+
+ call assert_true(filecopy('Xcopy1', 'Xcopy2'))
+
+ call assert_equal(['foo'], readfile('Xcopy2'))
+
+ " When the destination file already exists, it should not be overwritten.
+ call writefile(['foo'], 'Xcopy1')
+ call writefile(['bar'], 'Xcopy2', 'D')
+ call assert_false(filecopy('Xcopy1', 'Xcopy2'))
+ call assert_equal(['bar'], readfile('Xcopy2'))
+
+ call delete('Xcopy2')
+ call delete('Xcopy1')
+endfunc
+
+func Test_copy_symbolic_link()
+ CheckUnix
+
+ call writefile(['text'], 'Xtestfile', 'D')
+ silent !ln -s -f Xtestfile Xtestlink
+
+ call assert_true(filecopy('Xtestlink', 'Xtestlink2'))
+ call assert_equal('link', getftype('Xtestlink2'))
+ call assert_equal(['text'], readfile('Xtestlink2'))
+
+ " When the destination file already exists, it should not be overwritten.
+ call assert_false(filecopy('Xtestlink', 'Xtestlink2'))
+
+ call delete('Xtestlink2')
+ call delete('Xtestlink')
+ call delete('Xtestfile')
+endfunc
+
+func Test_copy_dir_to_dir()
+ call mkdir('Xcopydir1')
+ call writefile(['foo'], 'Xcopydir1/Xfilecopy')
+ call mkdir('Xcopydir2')
+
+ " Directory copy is not supported
+ call assert_false(filecopy('Xcopydir1', 'Xcopydir2'))
+
+ call delete('Xcopydir2', 'rf')
+ call delete('Xcopydir1', 'rf')
+endfunc
+
+func Test_copy_fails()
+ CheckUnix
+
+ call writefile(['foo'], 'Xfilecopy', 'D')
+
+ " Can't copy into a non-existing directory.
+ call assert_false(filecopy('Xfilecopy', 'Xdoesnotexist/Xfilecopy'))
+
+ " Can't copy a non-existing file.
+ call assert_false(filecopy('Xdoesnotexist', 'Xfilecopy2'))
+ call assert_equal('', glob('Xfilecopy2'))
+
+ " Can't copy to en empty file name.
+ call assert_false(filecopy('Xfilecopy', ''))
+
+ call assert_fails('call filecopy("Xfilecopy", [])', 'E1174:')
+ call assert_fails('call filecopy(0z, "Xfilecopy")', 'E1174:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 4b1bcf1bad..c3d61998cd 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 465,
+/**/
464,
/**/
463,