diff options
author | Shougo Matsushita <Shougo.Matsu@gmail.com> | 2024-06-03 22:59:27 +0200 |
---|---|---|
committer | Christian Brabandt <cb@256bit.org> | 2024-06-03 23:01:40 +0200 |
commit | 60c8743ab6c90e402e6ed49d27455ef7e5698abe (patch) | |
tree | 441cc398a542d10a60cd9c1dd7862ebcf0f1353e /src | |
parent | 0a0830624a260660c7fa692ecb7e6e5de09114ba (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.c | 2 | ||||
-rw-r--r-- | src/fileio.c | 75 | ||||
-rw-r--r-- | src/filepath.c | 25 | ||||
-rw-r--r-- | src/proto/fileio.pro | 1 | ||||
-rw-r--r-- | src/proto/filepath.pro | 1 | ||||
-rw-r--r-- | src/testdir/Make_all.mak | 2 | ||||
-rw-r--r-- | src/testdir/test_filecopy.vim | 72 | ||||
-rw-r--r-- | src/version.c | 2 |
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, |