patch 9.1.0465: missing filecopy() function

Commit: 
https://github.com/vim/vim/commit/60c8743ab6c90e402e6ed49d27455ef7e5698abe
Author: Shougo Matsushita <shougo.ma...@gmail.com>
Date:   Mon Jun 3 22:59:27 2024 +0200

    patch 9.1.0465: missing filecopy() function
    
    Problem:  missing filecopy() function
    Solution: implement filecopy() Vim script function
              (Shougo Matsushita)
    
    closes: #12346
    
    Co-authored-by: zeertzjq <zeert...@outlook.com>
    Signed-off-by: Shougo Matsushita <shougo.ma...@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 751fbc215..782c42f74 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -1,4 +1,4 @@
-*builtin.txt*  For Vim version 9.1.  Last change: 2024 May 25
+*builtin.txt*  For Vim version 9.1.  Last change: 2024 Jun 03
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -178,6 +178,8 @@ extendnew({expr1}, {expr2} [, {expr3}])
                                List/Dict like |extend()| but creates a new
                                        List or Dictionary
 feedkeys({string} [, {mode}])  Number  add key sequence to typeahead buffer
+filecopy({from}, {to})         Number  |TRUE| if copying file {from} to {to}
+                                       worked
 filereadable({file})           Number  |TRUE| if {file} is a readable file
 filewritable({file})           Number  |TRUE| if {file} is a writable file
 filter({expr1}, {expr2})       List/Dict/Blob/String
@@ -2757,6 +2759,18 @@ feedkeys({string} [, {mode}])                            
*feedkeys()*
                Can also be used as a |method|: >
                        GetInput()->feedkeys()
 
+filecopy({from}, {to})                                 *filecopy()*
+               Copy the file pointed to by the name {from} to {to}. The
+               result is a Number, which is |TRUE| if the file was copied
+               successfully, and |FALSE| when it failed.
+               If a file with name {to} already exists, it will fail.
+               Note that it does not handle directories (yet).
+
+               This function is not available in the |sandbox|.
+
+               Can also be used as a |method|: >
+                       GetOldName()->filecopy(newname)
+
 filereadable({file})                                   *filereadable()*
                The result is a Number, which is |TRUE| when a file with the
                name {file} exists, and can be read.  If {file} doesn't exist,
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 73aefe1ce..75c27f928 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -7091,6 +7091,7 @@ file-searching    editing.txt     /*file-searching*
 file-type      filetype.txt    /*file-type*
 file-types     filetype.txt    /*file-types*
 file_readable()        builtin.txt     /*file_readable()*
+filecopy()     builtin.txt     /*filecopy()*
 fileencoding-changed   version6.txt    /*fileencoding-changed*
 filename-backslash     cmdline.txt     /*filename-backslash*
 filename-modifiers     cmdline.txt     /*filename-modifiers*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index 7491b06e0..c37a1d4bd 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 9.1.  Last change: 2024 May 11
+*todo.txt*      For Vim version 9.1.  Last change: 2024 Jun 03
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -82,8 +82,6 @@ With 'smoothscroll' set and "lastline" in 'display', moving 
the cursor to a
 wrapping line that makes the display scroll up may scroll much more than
 needed, thus jump-scrolling. (part of issue 12411)
 
-Add filecopy() ?  #12346
-
 Implement foreach()  PR  #12166
 
 Errors when running tests with valgrind:
@@ -4242,7 +4240,6 @@ Vim script language:
        base64enc()             base 64 encoding
        base64dec()             base 64 decoding
        attributes()            return file protection flags "drwxrwxrwx"
-       filecopy(from, to)      Copy a file
        shorten(fname)          shorten a file name, like home_replace()
        perl(cmd)               call Perl and return string
        inputrl()               like input() but right-to-left
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 26651ebb1..4225d1f62 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1,4 +1,4 @@
-*usr_41.txt*   For Vim version 9.1.  Last change: 2024 May 07
+*usr_41.txt*   For Vim version 9.1.  Last change: 2024 Jun 03
 
                     VIM USER MANUAL - by Bram Moolenaar
 
@@ -993,6 +993,7 @@ System functions and manipulation of files:
        readdir()               get a List of file names in a directory
        readdirex()             get a List of file information in a directory
        writefile()             write a List of lines or Blob into a file
+       filecopy()              copy a file {from} to {to}
 
 Date and Time:                         *date-functions* *time-functions*
        getftime()              get last modification time of a file
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 3fe8b2dad..07c416ae2 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -41576,11 +41576,12 @@ Various syntax, indent and other plugins were added.
 Functions: ~
 
 |diff()|               diff two Lists of strings
+|filecopy()|           copy a file {from} to {to}
 |foreach()|            apply function to List items
-|matchbufline()|       all the matches of a pattern in a buffer
-|matchstrlist()|       all the matches of a pattern in a List of strings
 |getregion()|          get a region of text from a buffer
 |getregionpos()|       get a list of positions for a region
+|matchbufline()|       all the matches of a pattern in a buffer
+|matchstrlist()|       all the matches of a pattern in a List of strings
 
 
 Autocommands: ~
@@ -41607,7 +41608,7 @@ Options: ~
 't_CF'                 Support for alternate font highlighting terminal code
 
 ==============================================================================
-INCOMPATIBLE CHANGES                           *incompatible-9.2*
+INCOMPATIBLE CHANGES                                   *incompatible-9.2*
 
 Improved/Different MS-Windows mapping support
 |w32-experimental-keycode-trans-strategy|
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 5dd678b86..6ff967756 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -3,7 +3,7 @@
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <dougkea...@gmail.com>
 " URL:    https://github.com/vim-jp/syntax-vim-ex
-" Last Change:    2024 May 27
+" Last Change:    2024 Jun 03
 " Former Maintainer: Charles E. Campbell
 
 " DO NOT CHANGE DIRECTLY.
@@ -105,13 +105,14 @@ syn case match
 " Function Names {{{2
 " GEN_SYN_VIM: vimFuncName, START_STR='syn keyword vimFuncName contained', 
END_STR=''
 syn keyword vimFuncName contained abs acos add and append appendbufline argc 
argidx arglistid argv asin assert_beeps assert_equal assert_equalfile 
assert_exception assert_fails assert_false assert_inrange assert_match 
assert_nobeep assert_notequal assert_notmatch assert_report assert_true atan 
atan2 autocmd_add autocmd_delete autocmd_get balloon_gettext balloon_show 
balloon_split blob2list browse browsedir bufadd bufexists buflisted bufload 
bufloaded bufname bufnr bufwinid bufwinnr byte2line byteidx byteidxcomp call 
ceil ch_canread ch_close ch_close_in ch_evalexpr ch_evalraw ch_getbufnr 
ch_getjob ch_info ch_log ch_logfile ch_open ch_read ch_readblob ch_readraw 
ch_sendexpr ch_sendraw ch_setoptions ch_status changenr char2nr charclass 
charcol charidx chdir cindent
-syn keyword vimFuncName contained clearmatches col complete complete_add 
complete_check complete_info confirm copy cos cosh count cscope_connection 
cursor debugbreak deepcopy delete deletebufline did_filetype diff diff_filler 
diff_hlID digraph_get digraph_getlist digraph_set digraph_setlist echoraw empty 
environ err_teapot escape eval eventhandler executable execute exepath exists 
exists_compiled exp expand expandcmd extend extendnew feedkeys filereadable 
filewritable filter finddir findfile flatten flattennew float2nr floor fmod 
fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext 
foldtextresult foreach foreground fullcommand funcref function garbagecollect 
get getbufinfo getbufline getbufoneline getbufvar getcellwidths getchangelist 
getchar getcharmod
-syn keyword vimFuncName contained getcharpos getcharsearch getcharstr 
getcmdcompltype getcmdline getcmdpos getcmdscreenpos getcmdtype getcmdwintype 
getcompletion getcurpos getcursorcharpos getcwd getenv getfontname getfperm 
getfsize getftime getftype getimstatus getjumplist getline getloclist 
getmarklist getmatches getmousepos getmouseshape getpid getpos getqflist getreg 
getreginfo getregion getregionpos getregtype getscriptinfo gettabinfo gettabvar 
gettabwinvar gettagstack gettext getwininfo getwinpos getwinposx getwinposy 
getwinvar glob glob2regpat globpath has has_key haslocaldir hasmapto histadd 
histdel histget histnr hlID hlexists hlget hlset hostname iconv indent index 
indexof input inputdialog inputlist inputrestore inputsave inputsecret insert 
instanceof
-syn keyword vimFuncName contained interrupt invert isabsolutepath isdirectory 
isinf islocked isnan items job_getchannel job_info job_setoptions job_start 
job_status job_stop join js_decode js_encode json_decode json_encode keys 
keytrans len libcall libcallnr line line2byte lispindent list2blob list2str 
listener_add listener_flush listener_remove localtime log log10 luaeval map 
maparg mapcheck maplist mapnew mapset match matchadd matchaddpos matcharg 
matchbufline matchdelete matchend matchfuzzy matchfuzzypos matchlist matchstr 
matchstrlist matchstrpos max menu_info min mkdir mode mzeval nextnonblank 
nr2char or pathshorten perleval popup_atcursor popup_beval popup_clear 
popup_close popup_create popup_dialog popup_filter_menu popup_filter_yesno 
popup_findecho popup_findinfo
-syn keyword vimFuncName contained popup_findpreview popup_getoptions 
popup_getpos popup_hide popup_list popup_locate popup_menu popup_move 
popup_notification popup_setoptions popup_settext popup_show pow prevnonblank 
printf prompt_getprompt prompt_setcallback prompt_setinterrupt prompt_setprompt 
prop_add prop_add_list prop_clear prop_find prop_list prop_remove prop_type_add 
prop_type_change prop_type_delete prop_type_get prop_type_list pum_getpos 
pumvisible py3eval pyeval pyxeval rand range readblob readdir readdirex 
readfile reduce reg_executing reg_recording reltime reltimefloat reltimestr 
remote_expr remote_foreground remote_peek remote_read remote_send 
remote_startserver remove rename repeat resolve reverse round rubyeval 
screenattr screenchar screenchars
-syn keyword vimFuncName contained screencol screenpos screenrow screenstring 
search searchcount searchdecl searchpair searchpairpos searchpos server2client 
serverlist setbufline setbufvar setcellwidths setcharpos setcharsearch 
setcmdline setcmdpos setcursorcharpos setenv setfperm setline setloclist 
setmatches setpos setqflist setreg settabvar settabwinvar settagstack setwinvar 
sha256 shellescape shiftwidth sign_define sign_getdefined sign_getplaced 
sign_jump sign_place sign_placelist sign_undefine sign_unplace sign_unplacelist 
simplify sin sinh slice sort sound_clear sound_playevent sound_playfile 
sound_stop soundfold spellbadword spellsuggest split sqrt srand state str2float 
str2list str2nr strcharlen strcharpart strchars strdisplaywidth strftime 
strgetchar stridx
-syn keyword vimFuncName contained string strlen strpart strptime strridx 
strtrans strutf16len strwidth submatch substitute swapfilelist swapinfo 
swapname synID synIDattr synIDtrans synconcealed synstack system systemlist 
tabpagebuflist tabpagenr tabpagewinnr tagfiles taglist tan tanh tempname 
term_dumpdiff term_dumpload term_dumpwrite term_getaltscreen term_getansicolors 
term_getattr term_getcursor term_getjob term_getline term_getscrolled 
term_getsize term_getstatus term_gettitle term_gettty term_list term_scrape 
term_sendkeys term_setansicolors term_setapi term_setkill term_setrestore 
term_setsize term_start term_wait terminalprops test_alloc_fail test_autochdir 
test_feedinput test_garbagecollect_now test_garbagecollect_soon test_getvalue 
test_gui_event test_ignore_error
-syn keyword vimFuncName contained test_mswin_event test_null_blob 
test_null_channel test_null_dict test_null_function test_null_job 
test_null_list test_null_partial test_null_string test_option_not_set 
test_override test_refcount test_setmouse test_settime test_srand_seed 
test_unknown test_void timer_info timer_pause timer_start timer_stop 
timer_stopall tolower toupper tr trim trunc type typename undofile undotree 
uniq utf16idx values virtcol virtcol2col visualmode wildmenumode win_execute 
win_findbuf win_getid win_gettype win_gotoid win_id2tabwin win_id2win 
win_move_separator win_move_statusline win_screenpos win_splitmove winbufnr 
wincol windowsversion winheight winlayout winline winnr winrestcmd winrestview 
winsaveview winwidth wordcount writefile xor
+syn keyword vimFuncName contained clearmatches col complete complete_add 
complete_check complete_info confirm copy cos cosh count cscope_connection 
cursor debugbreak deepcopy delete deletebufline did_filetype diff diff_filler 
diff_hlID digraph_get digraph_getlist digraph_set digraph_setlist echoraw empty 
environ err_teapot escape eval eventhandler executable execute exepath exists 
exists_compiled exp expand expandcmd extend extendnew feedkeys filecopy 
filereadable filewritable filter finddir findfile flatten flattennew float2nr 
floor fmod fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext 
foldtextresult foreach foreground fullcommand funcref function garbagecollect 
get getbufinfo getbufline getbufoneline getbufvar getcellwidths getchangelist 
getchar
+syn keyword vimFuncName contained getcharmod getcharpos getcharsearch 
getcharstr getcmdcompltype getcmdline getcmdpos getcmdscreenpos getcmdtype 
getcmdwintype getcompletion getcurpos getcursorcharpos getcwd getenv 
getfontname getfperm getfsize getftime getftype getimstatus getjumplist getline 
getloclist getmarklist getmatches getmousepos getmouseshape getpid getpos 
getqflist getreg getreginfo getregion getregionpos getregtype getscriptinfo 
gettabinfo gettabvar gettabwinvar gettagstack gettext getwininfo getwinpos 
getwinposx getwinposy getwinvar glob glob2regpat globpath has has_key 
haslocaldir hasmapto histadd histdel histget histnr hlID hlexists hlget hlset 
hostname iconv indent index indexof input inputdialog inputlist inputrestore 
inputsave inputsecret insert
+syn keyword vimFuncName contained instanceof interrupt invert isabsolutepath 
isdirectory isinf islocked isnan items job_getchannel job_info job_setoptions 
job_start job_status job_stop join js_decode js_encode json_decode json_encode 
keys keytrans len libcall libcallnr line line2byte lispindent list2blob 
list2str listener_add listener_flush listener_remove localtime log log10 
luaeval map maparg mapcheck maplist mapnew mapset match matchadd matchaddpos 
matcharg matchbufline matchdelete matchend matchfuzzy matchfuzzypos matchlist 
matchstr matchstrlist matchstrpos max menu_info min mkdir mode mzeval 
nextnonblank nr2char or pathshorten perleval popup_atcursor popup_beval 
popup_clear popup_close popup_create popup_dialog popup_filter_menu 
popup_filter_yesno popup_findecho
+syn keyword vimFuncName contained popup_findinfo popup_findpreview 
popup_getoptions popup_getpos popup_hide popup_list popup_locate popup_menu 
popup_move popup_notification popup_setoptions popup_settext popup_show pow 
prevnonblank printf prompt_getprompt prompt_setcallback prompt_setinterrupt 
prompt_setprompt prop_add prop_add_list prop_clear prop_find prop_list 
prop_remove prop_type_add prop_type_change prop_type_delete prop_type_get 
prop_type_list pum_getpos pumvisible py3eval pyeval pyxeval rand range readblob 
readdir readdirex readfile reduce reg_executing reg_recording reltime 
reltimefloat reltimestr remote_expr remote_foreground remote_peek remote_read 
remote_send remote_startserver remove rename repeat resolve reverse round 
rubyeval screenattr screenchar
+syn keyword vimFuncName contained screenchars screencol screenpos screenrow 
screenstring search searchcount searchdecl searchpair searchpairpos searchpos 
server2client serverlist setbufline setbufvar setcellwidths setcharpos 
setcharsearch setcmdline setcmdpos setcursorcharpos setenv setfperm setline 
setloclist setmatches setpos setqflist setreg settabvar settabwinvar 
settagstack setwinvar sha256 shellescape shiftwidth sign_define sign_getdefined 
sign_getplaced sign_jump sign_place sign_placelist sign_undefine sign_unplace 
sign_unplacelist simplify sin sinh slice sort sound_clear sound_playevent 
sound_playfile sound_stop soundfold spellbadword spellsuggest split sqrt srand 
state str2float str2list str2nr strcharlen strcharpart strchars strdisplaywidth 
strftime
+syn keyword vimFuncName contained strgetchar stridx string strlen strpart 
strptime strridx strtrans strutf16len strwidth submatch substitute swapfilelist 
swapinfo swapname synID synIDattr synIDtrans synconcealed synstack system 
systemlist tabpagebuflist tabpagenr tabpagewinnr tagfiles taglist tan tanh 
tempname term_dumpdiff term_dumpload term_dumpwrite term_getaltscreen 
term_getansicolors term_getattr term_getcursor term_getjob term_getline 
term_getscrolled term_getsize term_getstatus term_gettitle term_gettty 
term_list term_scrape term_sendkeys term_setansicolors term_setapi term_setkill 
term_setrestore term_setsize term_start term_wait terminalprops test_alloc_fail 
test_autochdir test_feedinput test_garbagecollect_now test_garbagecollect_soon 
test_getvalue test_gui_event
+syn keyword vimFuncName contained test_ignore_error test_mswin_event 
test_null_blob test_null_channel test_null_dict test_null_function 
test_null_job test_null_list test_null_partial test_null_string 
test_option_not_set test_override test_refcount test_setmouse test_settime 
test_srand_seed test_unknown test_void timer_info timer_pause timer_start 
timer_stop timer_stopall tolower toupper tr trim trunc type typename undofile 
undotree uniq utf16idx values virtcol virtcol2col visualmode wildmenumode 
win_execute win_findbuf win_getid win_gettype win_gotoid win_id2tabwin 
win_id2win win_move_separator win_move_statusline win_screenpos win_splitmove 
winbufnr wincol windowsversion winheight winlayout winline winnr winrestcmd 
winrestview winsaveview winwidth wordcount writefile
+syn keyword vimFuncName contained xor
 
 "--- syntax here and above generated by mkvimvim ---
 " Special Vim Highlighting (not automatic) {{{1
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 3028cf975..9720691b4 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 07e05fc80..e7f333208 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 e68075a45..9f68d7c68 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 3f7b30d44..8d978709f 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 53fa4ec3f..46f51cb36 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 a80b1300e..e31d2b5f3 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 000000000..b526dce7b
--- /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 4b1bcf1ba..c3d61998c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    465,
 /**/
     464,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1sEFFm-008NpX-Od%40256bit.org.

Raspunde prin e-mail lui