mirror of
https://github.com/neovim/neovim.git
synced 2026-05-25 06:18:32 +00:00
Merge pull request #37882 from zeertzjq/vim-a2d87ba
vim-patch: netrw updates
This commit is contained in:
337
runtime/pack/dist/opt/netrw/autoload/netrw.vim
vendored
337
runtime/pack/dist/opt/netrw/autoload/netrw.vim
vendored
@@ -18,6 +18,16 @@
|
||||
" 2025 Nov 28 by Vim Project fix undefined variable in *NetrwMenu #18829
|
||||
" 2025 Dec 26 by Vim Project fix use of g:netrw_cygwin #19015
|
||||
" 2026 Jan 19 by Vim Project do not create swapfiles #18854
|
||||
" 2026 Feb 15 by Vim Project fix global variable initialization for MS-Windows #19287
|
||||
" 2026 Feb 21 by Vim Project better absolute path detection on MS-Windows #19477
|
||||
" 2026 Feb 27 by Vim Project Make the hostname validation more strict
|
||||
" 2026 Mar 01 by Vim Project include portnumber in hostname checking #19533
|
||||
" 2026 Apr 01 by Vim Project use fnameescape() with netrw#FileUrlEdit()
|
||||
" 2026 Apr 05 by Vim Project Fix netrw#RFC2396() #19913
|
||||
" 2026 Apr 15 by Vim Project Add missing escape()
|
||||
" 2026 Apr 19 by Vim Project expand ~ on Windows #20003
|
||||
" 2026 Apr 21 by Vim Project fix shell-injection via tempfile suffix (sftp://, file://)
|
||||
" 2026 Apr 21 by Vim Project drop unused g:netrw_tmpfile_escape
|
||||
" Copyright: Copyright (C) 2016 Charles E. Campbell {{{1
|
||||
" Permission is hereby granted to use and distribute this code,
|
||||
" with or without modifications, provided that this copyright
|
||||
@@ -265,8 +275,8 @@ if !exists("g:netrw_localcopycmd")
|
||||
let g:netrw_localcopycmdopt = ''
|
||||
|
||||
if has("win32") && !g:netrw_cygwin
|
||||
let g:netrw_localcopycmd = expand("$COMSPEC", v:true)
|
||||
let g:netrw_localcopycmdopt = '/c copy'
|
||||
let g:netrw_localcopycmd = $COMSPEC
|
||||
let g:netrw_localcopycmdopt = ' /c copy'
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -275,30 +285,31 @@ if !exists("g:netrw_localcopydircmd")
|
||||
let g:netrw_localcopydircmdopt = '-R'
|
||||
|
||||
if has("win32") && !g:netrw_cygwin
|
||||
let g:netrw_localcopydircmd = "cp"
|
||||
call s:NetrwInit("g:netrw_localcopydircmdopt", "-R")
|
||||
let g:netrw_localcopydircmd = "xcopy"
|
||||
let g:netrw_localcopydircmdopt = " /E /I /H /C /Y"
|
||||
endif
|
||||
endif
|
||||
|
||||
if has("win32")
|
||||
if g:netrw_cygwin
|
||||
call s:NetrwInit("g:netrw_localmkdir","mkdir")
|
||||
if !exists("g:netrw_localmkdir")
|
||||
if has("win32")
|
||||
if g:netrw_cygwin
|
||||
let g:netrw_localmkdir= "mkdir"
|
||||
else
|
||||
let g:netrw_localmkdir = $COMSPEC
|
||||
let g:netrw_localmkdiropt= " /c mkdir"
|
||||
endif
|
||||
else
|
||||
call s:NetrwInit("g:netrw_localmkdir",expand("$COMSPEC", v:true))
|
||||
call s:NetrwInit("g:netrw_localmkdiropt"," /c mkdir")
|
||||
let g:netrw_localmkdir= "mkdir"
|
||||
endif
|
||||
else
|
||||
call s:NetrwInit("g:netrw_localmkdir","mkdir")
|
||||
endif
|
||||
call s:NetrwInit("g:netrw_remote_mkdir","mkdir")
|
||||
|
||||
if !exists("g:netrw_localmovecmd")
|
||||
if has("win32")
|
||||
if g:netrw_cygwin
|
||||
let g:netrw_localmovecmd= "mv"
|
||||
else
|
||||
let g:netrw_localmovecmd = expand("$COMSPEC", v:true)
|
||||
call s:NetrwInit("g:netrw_localmovecmdopt"," /c move")
|
||||
let g:netrw_localmovecmd = $COMSPEC
|
||||
let g:netrw_localmovecmdopt= " /c move"
|
||||
endif
|
||||
elseif has("unix") || has("macunix")
|
||||
let g:netrw_localmovecmd= "mv"
|
||||
@@ -391,7 +402,6 @@ else
|
||||
call s:NetrwInit("g:netrw_glob_escape",'*[]?`{~$\')
|
||||
endif
|
||||
call s:NetrwInit("g:netrw_menu_escape",'.&? \')
|
||||
call s:NetrwInit("g:netrw_tmpfile_escape",' &;')
|
||||
call s:NetrwInit("s:netrw_map_escape","<|\n\r\\\<C-V>\"")
|
||||
if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4')
|
||||
let s:treedepthstring= "│ "
|
||||
@@ -522,8 +532,8 @@ function netrw#Explore(indx,dosplit,style,...)
|
||||
NetrwKeepj norm! 0
|
||||
|
||||
if a:0 > 0
|
||||
if a:1 =~ '^\~' && (has("unix") || g:netrw_cygwin)
|
||||
let dirname= simplify(substitute(a:1,'\~',expand("$HOME"),''))
|
||||
if a:1 =~ '^\~' && (has("unix") || has("win32") || g:netrw_cygwin)
|
||||
let dirname= simplify(substitute(a:1,'^\~',escape(expand("$HOME"),'\&~'),''))
|
||||
elseif a:1 == '.'
|
||||
let dirname= simplify(exists("b:netrw_curdir")? b:netrw_curdir : getcwd())
|
||||
if dirname !~ '/$'
|
||||
@@ -980,7 +990,7 @@ function netrw#Obtain(islocal,fname,...)
|
||||
" obtain a file from local b:netrw_curdir to (local) tgtdir
|
||||
if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir
|
||||
let topath = netrw#fs#ComposePath(tgtdir,"")
|
||||
if has("win32")
|
||||
if has("win32") && !g:netrw_cygwin
|
||||
" transfer files one at time
|
||||
for fname in fnamelist
|
||||
call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".netrw#os#Escape(fname)." ".netrw#os#Escape(topath))
|
||||
@@ -1812,14 +1822,14 @@ function netrw#NetRead(mode,...)
|
||||
".........................................
|
||||
" NetRead: (sftp) NetRead Method #9 {{{3
|
||||
elseif b:netrw_method == 9
|
||||
call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile)
|
||||
call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1)." ".netrw#os#Escape(tmpfile,1))
|
||||
let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
|
||||
let b:netrw_lastfile = choice
|
||||
|
||||
".........................................
|
||||
" NetRead: (file) NetRead Method #10 {{{3
|
||||
elseif b:netrw_method == 10 && exists("g:netrw_file_cmd")
|
||||
call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_file_cmd." ".netrw#os#Escape(b:netrw_fname,1)." ".tmpfile)
|
||||
call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_file_cmd." ".netrw#os#Escape(b:netrw_fname,1)." ".netrw#os#Escape(tmpfile,1))
|
||||
let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
|
||||
let b:netrw_lastfile = choice
|
||||
|
||||
@@ -2588,13 +2598,29 @@ endfunction
|
||||
|
||||
" s:NetrwValidateHostname: Validate that the hostname is valid {{{2
|
||||
" Input:
|
||||
" hostname
|
||||
" hostname, may include an optional username and port number, e.g.
|
||||
" user@hostname:port
|
||||
" allow a alphanumeric hostname or an IPv(4/6) address
|
||||
" Output:
|
||||
" true if g:netrw_machine is valid according to RFC1123 #Section 2
|
||||
function s:NetrwValidateHostname(hostname)
|
||||
" RFC1123#section-2 mandates, a valid hostname starts with letters or digits
|
||||
" so reject everyhing else
|
||||
return a:hostname =~? '^[a-z0-9]'
|
||||
" Username:
|
||||
let user_pat = '\%([a-zA-Z0-9._-]\+@\)\?'
|
||||
" Hostname: 1-64 chars, alphanumeric/dots/hyphens.
|
||||
" No underscores. No leading/trailing dots/hyphens.
|
||||
let host_pat = '[a-zA-Z0-9]\%([-a-zA-Z0-9.]\{0,62}[a-zA-Z0-9]\)\?'
|
||||
" Port: 16 bit unsigned integer
|
||||
let port_pat = '\%(:\d\{1,5\}\)\?$'
|
||||
|
||||
" IPv4: 1-3 digits separated by dots
|
||||
let ipv4_pat = '\%(\d\{1,3}\.\)\{3\}\d\{1,3\}'
|
||||
|
||||
" IPv6: Hex, colons, and optional brackets
|
||||
let ipv6_pat = '\[\?\%([a-fA-F0-9:]\{2,}\)\+\]\?'
|
||||
|
||||
return a:hostname =~? '^'.user_pat.host_pat.port_pat ||
|
||||
\ a:hostname =~? '^'.user_pat.ipv4_pat.port_pat ||
|
||||
\ a:hostname =~? '^'.user_pat.ipv6_pat.port_pat
|
||||
endfunction
|
||||
|
||||
" NetUserPass: set username and password for subsequent ftp transfer {{{2
|
||||
@@ -3237,7 +3263,7 @@ function s:NetrwFile(fname)
|
||||
endif
|
||||
|
||||
if !g:netrw_cygwin && has("win32")
|
||||
if fname =~ '^\' || fname =~ '^\a:\'
|
||||
if isabsolutepath(fname)
|
||||
" windows, but full path given
|
||||
let ret= fname
|
||||
else
|
||||
@@ -4735,38 +4761,16 @@ function s:NetrwMakeDir(usrhost)
|
||||
|
||||
" requested new local directory is neither a pre-existing file or
|
||||
" directory, so make it!
|
||||
if exists("*mkdir")
|
||||
if has("unix")
|
||||
call mkdir(fullnewdir,"p",xor(0777, system("umask")))
|
||||
else
|
||||
call mkdir(fullnewdir,"p")
|
||||
endif
|
||||
if has("unix")
|
||||
call mkdir(fullnewdir,"p",xor(0777, system("umask")))
|
||||
else
|
||||
let netrw_origdir= netrw#fs#Cwd(1)
|
||||
if s:NetrwLcd(b:netrw_curdir)
|
||||
return
|
||||
endif
|
||||
call netrw#os#Execute("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.netrw#os#Escape(newdirname,1))
|
||||
if v:shell_error != 0
|
||||
let @@= ykeep
|
||||
call netrw#msg#Notify('ERROR', printf('consider setting g:netrw_localmkdir<%s> to something that works', g:netrw_localmkdir))
|
||||
return
|
||||
endif
|
||||
if !g:netrw_keepdir
|
||||
if s:NetrwLcd(netrw_origdir)
|
||||
return
|
||||
endif
|
||||
endif
|
||||
call mkdir(fullnewdir,"p")
|
||||
endif
|
||||
|
||||
if v:shell_error == 0
|
||||
" refresh listing
|
||||
let svpos= winsaveview()
|
||||
call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
|
||||
call winrestview(svpos)
|
||||
else
|
||||
call netrw#msg#Notify('ERROR', printf('unable to make directory<%s>', newdirname))
|
||||
endif
|
||||
" on success refresh listing
|
||||
let svpos= winsaveview()
|
||||
call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
|
||||
call winrestview(svpos)
|
||||
|
||||
elseif !exists("b:netrw_method") || b:netrw_method == 4
|
||||
" Remote mkdir: using ssh
|
||||
@@ -5307,6 +5311,8 @@ function s:NetrwMarkFileCompress(islocal)
|
||||
if a:islocal
|
||||
if g:netrw_keepdir
|
||||
let fname= netrw#os#Escape(netrw#fs#ComposePath(curdir,fname))
|
||||
else
|
||||
let fname= netrw#os#Escape(fname)
|
||||
endif
|
||||
call system(exe." ".fname)
|
||||
if v:shell_error
|
||||
@@ -5350,17 +5356,14 @@ function s:NetrwMarkFileCopy(islocal,...)
|
||||
|
||||
let curdir = s:NetrwGetCurdir(a:islocal)
|
||||
let curbufnr = bufnr("%")
|
||||
if b:netrw_curdir !~ '/$'
|
||||
if !exists("b:netrw_curdir")
|
||||
let b:netrw_curdir= curdir
|
||||
endif
|
||||
let b:netrw_curdir= b:netrw_curdir."/"
|
||||
if !exists("b:netrw_curdir")
|
||||
let b:netrw_curdir= curdir
|
||||
endif
|
||||
|
||||
" sanity check
|
||||
if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr})
|
||||
call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)')
|
||||
return
|
||||
return 0
|
||||
endif
|
||||
|
||||
if !exists("s:netrwmftgt")
|
||||
@@ -5368,7 +5371,7 @@ function s:NetrwMarkFileCopy(islocal,...)
|
||||
return 0
|
||||
endif
|
||||
|
||||
if a:islocal && s:netrwmftgt_islocal
|
||||
if a:islocal && s:netrwmftgt_islocal
|
||||
" Copy marked files, local directory to local directory
|
||||
if !executable(g:netrw_localcopycmd)
|
||||
call netrw#msg#Notify('ERROR', printf('g:netrw_localcopycmd<%s> not executable on your system, aborting', g:netrw_localcopycmd))
|
||||
@@ -5376,69 +5379,85 @@ function s:NetrwMarkFileCopy(islocal,...)
|
||||
endif
|
||||
|
||||
" copy marked files while within the same directory (ie. allow renaming)
|
||||
if simplify(s:netrwmftgt) ==# simplify(b:netrw_curdir)
|
||||
if len(s:netrwmarkfilelist_{bufnr('%')}) == 1
|
||||
" only one marked file
|
||||
let args = netrw#os#Escape(b:netrw_curdir.s:netrwmarkfilelist_{bufnr('%')}[0])
|
||||
let oldname = s:netrwmarkfilelist_{bufnr('%')}[0]
|
||||
elseif a:0 == 1
|
||||
" this happens when the next case was used to recursively call s:NetrwMarkFileCopy()
|
||||
let args = netrw#os#Escape(b:netrw_curdir.a:1)
|
||||
let oldname = a:1
|
||||
else
|
||||
" copy multiple marked files inside the same directory
|
||||
let s:recursive= 1
|
||||
for oldname in s:netrwmarkfilelist_{bufnr("%")}
|
||||
let ret= s:NetrwMarkFileCopy(a:islocal,oldname)
|
||||
if ret == 0
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
unlet s:recursive
|
||||
call s:NetrwUnmarkList(curbufnr,curdir)
|
||||
return ret
|
||||
endif
|
||||
if simplify(s:netrwmftgt."/") ==# simplify(b:netrw_curdir."/")
|
||||
" copy multiple marked files inside the same directory
|
||||
for oldname in s:netrwmarkfilelist_{curbufnr}
|
||||
call inputsave()
|
||||
let newname= input(printf("Copy %s to: ", oldname), oldname, 'file')
|
||||
call inputrestore()
|
||||
|
||||
call inputsave()
|
||||
let newname= input(printf("Copy %s to: ", oldname), oldname, 'file')
|
||||
call inputrestore()
|
||||
if empty(newname)
|
||||
return 0
|
||||
endif
|
||||
|
||||
if empty(newname)
|
||||
return 0
|
||||
endif
|
||||
let tgt = netrw#fs#ComposePath(s:netrwmftgt, newname)
|
||||
let oldname = netrw#fs#ComposePath(b:netrw_curdir, oldname)
|
||||
if tgt ==# oldname
|
||||
continue
|
||||
endif
|
||||
|
||||
let args = netrw#os#Escape(oldname)
|
||||
let tgt = netrw#os#Escape(s:netrwmftgt.'/'.newname)
|
||||
let ret = filecopy(oldname, tgt)
|
||||
if ret == v:false
|
||||
call netrw#msg#Notify('ERROR', $'copy failed, unable to filecopy() <{oldname}> to <{tgt}>')
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
call s:NetrwUnmarkList(curbufnr,curdir)
|
||||
NetrwKeepj call s:NetrwRefreshDir(a:islocal, b:netrw_curdir)
|
||||
return ret
|
||||
else
|
||||
let args = join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"netrw#os#Escape(b:netrw_curdir.\"/\".v:val)"))
|
||||
let tgt = netrw#os#Escape(s:netrwmftgt)
|
||||
let args = []
|
||||
for arg in s:netrwmarkfilelist_{curbufnr}
|
||||
call add(args, netrw#fs#ComposePath(b:netrw_curdir, arg))
|
||||
endfor
|
||||
let tgt = s:netrwmftgt
|
||||
endif
|
||||
|
||||
if !g:netrw_cygwin && has("win32")
|
||||
let args = substitute(args,'/','\\','g')
|
||||
let tgt = substitute(tgt, '/','\\','g')
|
||||
endif
|
||||
|
||||
if args =~ "'" |let args= substitute(args,"'\\(.*\\)'",'\1','')|endif
|
||||
if tgt =~ "'" |let tgt = substitute(tgt ,"'\\(.*\\)'",'\1','')|endif
|
||||
if args =~ '//'|let args= substitute(args,'//','/','g')|endif
|
||||
if tgt =~ '//'|let tgt = substitute(tgt ,'//','/','g')|endif
|
||||
|
||||
let copycmd = g:netrw_localcopycmd
|
||||
let copycmdopt = g:netrw_localcopycmdopt
|
||||
|
||||
if isdirectory(s:NetrwFile(args))
|
||||
" on Windows, no builtin command supports copying multiple files at once
|
||||
" (powershell's Copy-Item cmdlet does but requires , as file separator)
|
||||
if len(s:netrwmarkfilelist_{curbufnr}) > 1 && has("win32") && !g:netrw_cygwin
|
||||
" copy multiple marked files
|
||||
for file in args
|
||||
let dest = netrw#fs#ComposePath(tgt, fnamemodify(file, ':t'))
|
||||
let ret = filecopy(file, dest)
|
||||
if ret == v:false
|
||||
call netrw#msg#Notify('ERROR', $'copy failed, unable to filecopy() <{file}> to <{dest}>')
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
call s:NetrwUnmarkList(curbufnr,curdir)
|
||||
NetrwKeepj call s:NetrwRefreshDir(a:islocal, b:netrw_curdir)
|
||||
return ret
|
||||
endif
|
||||
|
||||
if len(args) == 1 && isdirectory(s:NetrwFile(args[0]))
|
||||
let copycmd = g:netrw_localcopydircmd
|
||||
let copycmdopt = g:netrw_localcopydircmdopt
|
||||
if has('win32') && !g:netrw_cygwin
|
||||
if has('win32') && g:netrw_localcopydircmd == "xcopy"
|
||||
" window's xcopy doesn't copy a directory to a target properly. Instead, it copies a directory's
|
||||
" contents to a target. One must append the source directory name to the target to get xcopy to
|
||||
" do the right thing.
|
||||
let tgt= tgt.'\'.substitute(a:1,'^.*[\\/]','','')
|
||||
let tgt = netrw#fs#ComposePath(tgt, fnamemodify(simplify(netrw#fs#PathJoin(args[0],".")),":t"))
|
||||
endif
|
||||
endif
|
||||
|
||||
call system(printf("%s %s '%s' '%s'", copycmd, copycmdopt, args, tgt))
|
||||
" prepare arguments for shell call
|
||||
let args = join(map(args,'netrw#os#Escape(v:val)'))
|
||||
let tgt = netrw#os#Escape(tgt)
|
||||
|
||||
" enforce noshellslash for system calls
|
||||
if exists('+shellslash') && &shellslash
|
||||
for var in ['copycmd', 'args', 'tgt']
|
||||
let {var} = substitute({var}, '/', '\', 'g')
|
||||
endfor
|
||||
endif
|
||||
|
||||
" shell call
|
||||
let shell_cmd = printf("%s %s %s %s", copycmd, copycmdopt, args, tgt)
|
||||
call system(shell_cmd)
|
||||
if v:shell_error != 0
|
||||
if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && g:netrw_keepdir
|
||||
call netrw#msg#Notify('ERROR', printf("copy failed; perhaps due to vim's current directory<%s> not matching netrw's (%s) (see :help netrw-cd)", getcwd(), b:netrw_curdir))
|
||||
@@ -5450,11 +5469,11 @@ function s:NetrwMarkFileCopy(islocal,...)
|
||||
|
||||
elseif a:islocal && !s:netrwmftgt_islocal
|
||||
" Copy marked files, local directory to remote directory
|
||||
NetrwKeepj call s:NetrwUpload(s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt)
|
||||
NetrwKeepj call s:NetrwUpload(s:netrwmarkfilelist_{curbufnr},s:netrwmftgt)
|
||||
|
||||
elseif !a:islocal && s:netrwmftgt_islocal
|
||||
" Copy marked files, remote directory to local directory
|
||||
NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt)
|
||||
NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{curbufnr},s:netrwmftgt)
|
||||
|
||||
elseif !a:islocal && !s:netrwmftgt_islocal
|
||||
" Copy marked files, remote directory to remote directory
|
||||
@@ -5463,24 +5482,16 @@ function s:NetrwMarkFileCopy(islocal,...)
|
||||
if tmpdir !~ '/'
|
||||
let tmpdir= curdir."/".tmpdir
|
||||
endif
|
||||
if exists("*mkdir")
|
||||
call mkdir(tmpdir)
|
||||
else
|
||||
call netrw#os#Execute("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.netrw#os#Escape(tmpdir,1))
|
||||
if v:shell_error != 0
|
||||
call netrw#msg#Notify('WARNING', printf("consider setting g:netrw_localmkdir<%s> to something that works", g:netrw_localmkdir))
|
||||
return
|
||||
endif
|
||||
endif
|
||||
call mkdir(tmpdir)
|
||||
if isdirectory(s:NetrwFile(tmpdir))
|
||||
if s:NetrwLcd(tmpdir)
|
||||
return
|
||||
endif
|
||||
NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},tmpdir)
|
||||
let localfiles= map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),'substitute(v:val,"^.*/","","")')
|
||||
NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{curbufnr},tmpdir)
|
||||
let localfiles= map(deepcopy(s:netrwmarkfilelist_{curbufnr}),'substitute(v:val,"^.*/","","")')
|
||||
NetrwKeepj call s:NetrwUpload(localfiles,s:netrwmftgt)
|
||||
if getcwd() == tmpdir
|
||||
for fname in s:netrwmarkfilelist_{bufnr('%')}
|
||||
for fname in s:netrwmarkfilelist_{curbufnr}
|
||||
call netrw#fs#Remove(fname)
|
||||
endfor
|
||||
if s:NetrwLcd(curdir)
|
||||
@@ -5502,18 +5513,13 @@ function s:NetrwMarkFileCopy(islocal,...)
|
||||
" -------
|
||||
" remove markings from local buffer
|
||||
call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer
|
||||
if exists("s:recursive")
|
||||
else
|
||||
endif
|
||||
" see s:LocalFastBrowser() for g:netrw_fastbrowse interpretation (refreshing done for both slow and medium)
|
||||
if g:netrw_fastbrowse <= 1
|
||||
NetrwKeepj call s:LocalBrowseRefresh()
|
||||
else
|
||||
" refresh local and targets for fast browsing
|
||||
if !exists("s:recursive")
|
||||
" remove markings from local buffer
|
||||
NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir)
|
||||
endif
|
||||
" remove markings from local buffer
|
||||
NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir)
|
||||
|
||||
" refresh buffers
|
||||
if s:netrwmftgt_islocal
|
||||
@@ -5641,6 +5647,8 @@ function s:NetrwMarkFileExe(islocal,enbloc)
|
||||
if a:islocal
|
||||
if g:netrw_keepdir
|
||||
let fname= netrw#os#Escape(netrw#fs#WinPath(netrw#fs#ComposePath(curdir,fname)))
|
||||
else
|
||||
let fname= netrw#os#Escape(netrw#fs#WinPath(fname))
|
||||
endif
|
||||
else
|
||||
let fname= netrw#os#Escape(netrw#fs#WinPath(b:netrw_curdir.fname))
|
||||
@@ -5882,28 +5890,35 @@ function s:NetrwMarkFileMove(islocal)
|
||||
call netrw#msg#Notify('ERROR', printf('g:netrw_localmovecmd<%s> not executable on your system, aborting', g:netrw_localmovecmd))
|
||||
return
|
||||
endif
|
||||
|
||||
let tgt = netrw#os#Escape(s:netrwmftgt)
|
||||
if !g:netrw_cygwin && has("win32")
|
||||
let tgt= substitute(tgt, '/','\\','g')
|
||||
if g:netrw_localmovecmd =~ '\s'
|
||||
let movecmd = substitute(g:netrw_localmovecmd,'\s.*$','','')
|
||||
let movecmdargs = substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$','\1','')
|
||||
let movecmd = netrw#fs#WinPath(movecmd).movecmdargs
|
||||
else
|
||||
let movecmd = netrw#fs#WinPath(g:netrw_localmovecmd)
|
||||
endif
|
||||
if has("win32") && !g:netrw_cygwin && g:netrw_localmovecmd =~ '\s' && g:netrw_localmovecmdopt == ""
|
||||
let movecmd = substitute(g:netrw_localmovecmd,'\s.*$','','')
|
||||
let movecmdargs = substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$','\1','')
|
||||
else
|
||||
let movecmd = netrw#fs#WinPath(g:netrw_localmovecmd)
|
||||
let movecmd = g:netrw_localmovecmd
|
||||
let movecmdargs = g:netrw_localmovecmdopt
|
||||
endif
|
||||
for fname in s:netrwmarkfilelist_{bufnr("%")}
|
||||
|
||||
" build args list
|
||||
let args = []
|
||||
for fname in s:netrwmarkfilelist_{curbufnr}
|
||||
if g:netrw_keepdir
|
||||
" Jul 19, 2022: fixing file move when g:netrw_keepdir is 1
|
||||
let fname= b:netrw_curdir."/".fname
|
||||
let fname= netrw#fs#ComposePath(b:netrw_curdir, fname)
|
||||
endif
|
||||
if !g:netrw_cygwin && has("win32")
|
||||
let fname= substitute(fname,'/','\\','g')
|
||||
endif
|
||||
let ret= system(movecmd.g:netrw_localmovecmdopt." ".netrw#os#Escape(fname)." ".tgt)
|
||||
call add(args, netrw#os#Escape(fname))
|
||||
endfor
|
||||
|
||||
" enforce noshellslash for system calls
|
||||
if exists('+shellslash') && &shellslash
|
||||
let tgt = substitute(tgt, '/', '\', 'g')
|
||||
call map(args, "substitute(v:val, '/', '\\', 'g')")
|
||||
endif
|
||||
|
||||
for fname in args
|
||||
let shell_cmd = printf("%s %s %s %s", movecmd, movecmdargs, fname, tgt)
|
||||
let ret= system(shell_cmd)
|
||||
if v:shell_error != 0
|
||||
if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir
|
||||
call netrw#msg#Notify('ERROR', printf("move failed; perhaps due to vim's current directory<%s> not matching netrw's (%s) (see :help netrw-cd)", getcwd(), b:netrw_curdir))
|
||||
@@ -5916,7 +5931,7 @@ function s:NetrwMarkFileMove(islocal)
|
||||
|
||||
elseif a:islocal && !s:netrwmftgt_islocal
|
||||
" move: local -> remote
|
||||
let mflist= s:netrwmarkfilelist_{bufnr("%")}
|
||||
let mflist= s:netrwmarkfilelist_{curbufnr}
|
||||
NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
|
||||
for fname in mflist
|
||||
let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
|
||||
@@ -5926,7 +5941,7 @@ function s:NetrwMarkFileMove(islocal)
|
||||
|
||||
elseif !a:islocal && s:netrwmftgt_islocal
|
||||
" move: remote -> local
|
||||
let mflist= s:netrwmarkfilelist_{bufnr("%")}
|
||||
let mflist= s:netrwmarkfilelist_{curbufnr}
|
||||
NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
|
||||
for fname in mflist
|
||||
let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
|
||||
@@ -5936,7 +5951,7 @@ function s:NetrwMarkFileMove(islocal)
|
||||
|
||||
elseif !a:islocal && !s:netrwmftgt_islocal
|
||||
" move: remote -> remote
|
||||
let mflist= s:netrwmarkfilelist_{bufnr("%")}
|
||||
let mflist= s:netrwmarkfilelist_{curbufnr}
|
||||
NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
|
||||
for fname in mflist
|
||||
let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
|
||||
@@ -8280,7 +8295,7 @@ function netrw#FileUrlEdit(fname)
|
||||
endif
|
||||
|
||||
exe "sil doau BufReadPre ".fname2396e
|
||||
exe 'NetrwKeepj keepalt edit '.plainfname
|
||||
exe 'NetrwKeepj keepalt edit '. fnameescape(plainfname)
|
||||
exe 'sil! NetrwKeepj keepalt bdelete '.fnameescape(a:fname)
|
||||
|
||||
exe "sil doau BufReadPost ".fname2396e
|
||||
@@ -8823,8 +8838,7 @@ endfunction
|
||||
|
||||
" netrw#RFC2396: converts %xx into characters {{{2
|
||||
function netrw#RFC2396(fname)
|
||||
let fname = escape(substitute(a:fname,'%\(\x\x\)','\=printf("%c","0x".submatch(1))','ge')," \t")
|
||||
return fname
|
||||
return substitute(a:fname, '%\(\x\x\)', '\=printf("%c","0x".submatch(1))', 'ge')
|
||||
endfunction
|
||||
|
||||
" netrw#UserMaps: supports user-specified maps {{{2
|
||||
@@ -8956,14 +8970,17 @@ function s:GetTempfile(fname)
|
||||
endif
|
||||
|
||||
" use fname's suffix for the temporary file
|
||||
" Restrict the suffix to word characters so shell metacharacters in a
|
||||
" remote filename (e.g. sftp://host/foo.txt;id) cannot ride along into
|
||||
" the tempfile name and out into a downstream shell command.
|
||||
if a:fname != ""
|
||||
if a:fname =~ '\.[^./]\+$'
|
||||
if a:fname =~ '\.\w\+$'
|
||||
if a:fname =~ '\.tar\.gz$' || a:fname =~ '\.tar\.bz2$' || a:fname =~ '\.tar\.xz$'
|
||||
let suffix = ".tar".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
|
||||
let suffix = ".tar".substitute(a:fname,'^.*\(\.\w\+\)$','\1','e')
|
||||
elseif a:fname =~ '.txz$'
|
||||
let suffix = ".txz".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
|
||||
let suffix = ".txz".substitute(a:fname,'^.*\(\.\w\+\)$','\1','e')
|
||||
else
|
||||
let suffix = substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
|
||||
let suffix = substitute(a:fname,'^.*\(\.\w\+\)$','\1','e')
|
||||
endif
|
||||
let tmpfile= substitute(tmpfile,'\.tmp$','','e')
|
||||
let tmpfile .= suffix
|
||||
@@ -8977,15 +8994,15 @@ endfunction
|
||||
" s:MakeSshCmd: transforms input command using USEPORT HOSTNAME into {{{2
|
||||
" a correct command for use with a system() call
|
||||
function s:MakeSshCmd(sshcmd)
|
||||
if s:user == ""
|
||||
let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:machine,'')
|
||||
else
|
||||
let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:user."@".s:machine,'')
|
||||
let machine = shellescape(s:machine, 1)
|
||||
if s:user != ''
|
||||
let machine = shellescape(s:user, 1).'@'.machine
|
||||
endif
|
||||
let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',machine,'')
|
||||
if exists("g:netrw_port") && g:netrw_port != ""
|
||||
let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.g:netrw_port,'')
|
||||
let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.shellescape(g:netrw_port,1),'')
|
||||
elseif exists("s:port") && s:port != ""
|
||||
let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.s:port,'')
|
||||
let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.shellescape(s:port,1),'')
|
||||
else
|
||||
let sshcmd= substitute(sshcmd,"USEPORT ",'','')
|
||||
endif
|
||||
|
||||
@@ -24,6 +24,7 @@ endfunction
|
||||
" netrw#fs#ComposePath: Appends a new part to a path taking different systems into consideration {{{
|
||||
|
||||
function! netrw#fs#ComposePath(base, subdir)
|
||||
const slash = !exists('+shellslash') || &shellslash ? '/' : '\'
|
||||
if has('amiga')
|
||||
let ec = a:base[strdisplaywidth(a:base)-1]
|
||||
if ec != '/' && ec != ':'
|
||||
@@ -40,7 +41,7 @@ function! netrw#fs#ComposePath(base, subdir)
|
||||
if a:base =~ '[/\\]$'
|
||||
let ret = a:base . a:subdir
|
||||
else
|
||||
let ret = a:base . '/' . a:subdir
|
||||
let ret = a:base . slash . a:subdir
|
||||
endif
|
||||
|
||||
elseif a:base =~ '^\a\{3,}://'
|
||||
|
||||
@@ -32,7 +32,7 @@ endfunction
|
||||
" netrw#msg#Notify('ERROR'|'WARNING'|'NOTE', ["message1","message2",...])
|
||||
" (this function can optionally take a list of messages)
|
||||
function! netrw#msg#Notify(level, msg)
|
||||
if has('nvim')
|
||||
if has('nvim') && !v:testing
|
||||
" Convert string to corresponding vim.log.level value
|
||||
if a:level ==# 'ERROR'
|
||||
let level = 4
|
||||
|
||||
4
runtime/pack/dist/opt/netrw/doc/netrw.txt
vendored
4
runtime/pack/dist/opt/netrw/doc/netrw.txt
vendored
@@ -2851,10 +2851,6 @@ your browsing preferences. (see also: |netrw-settings|)
|
||||
such as listing, file removal, etc.
|
||||
default: ssh
|
||||
|
||||
*g:netrw_tmpfile_escape* =' &;'
|
||||
escape() is applied to all temporary files
|
||||
to escape these characters.
|
||||
|
||||
*g:netrw_timefmt* specify format string to vim's strftime().
|
||||
The default, "%c", is "the preferred date
|
||||
and time representation for the current
|
||||
|
||||
@@ -1,32 +1,228 @@
|
||||
let s:netrw_path = $VIMRUNTIME . '/pack/dist/opt/netrw/autoload/netrw.vim'
|
||||
let s:netrw_test_dir = 'samples'
|
||||
let s:netrw_test_path = s:netrw_test_dir . '/netrw.vim'
|
||||
const s:testdir = expand("<script>:h")
|
||||
const s:runtimedir = simplify(s:testdir . '/../../../runtime')
|
||||
const s:netrw_path = s:runtimedir . '/pack/dist/opt/netrw/autoload/netrw.vim'
|
||||
const s:netrw_test_path = s:testdir . '/samples/netrw.vim'
|
||||
const s:testScript =<< trim END
|
||||
|
||||
" Testing functions: {{{1
|
||||
function! TestNetrwCaptureRemotePath(dirname)
|
||||
call s:RemotePathAnalysis(a:dirname)
|
||||
return {"method": s:method, "user": s:user, "machine": s:machine, "port": s:port, "path": s:path, "fname": s:fname}
|
||||
endfunction
|
||||
|
||||
" Test directory creation via s:NetrwMakeDir()
|
||||
" Precondition: inputsave() and inputrestore() must be disabled in s:NetrwMakeDir
|
||||
|
||||
function s:test_inputsave()
|
||||
if exists("s:inputguards_disabled") && s:inputguards_disabled
|
||||
return
|
||||
endif
|
||||
call inputsave()
|
||||
endfunction
|
||||
|
||||
function s:test_inputrestore()
|
||||
if exists("s:inputguards_disabled") && s:inputguards_disabled
|
||||
return
|
||||
endif
|
||||
call inputrestore()
|
||||
endfunction
|
||||
|
||||
function s:test_input(prompt, text = v:null, completion = v:null) " Nvim: use v:null instead of v:none
|
||||
|
||||
if exists("s:inputdefaults_disabled") && s:inputdefaults_disabled || a:text == v:null
|
||||
return input(a:prompt)
|
||||
elseif a:completion == v:null
|
||||
return input(a:prompt, a:text)
|
||||
endif
|
||||
|
||||
return input(a:prompt, a:text, a:completion)
|
||||
endfunction
|
||||
|
||||
function Test_NetrwMakeDir(parentdir = $HOME, dirname = "NetrwMakeDir", symlink = 0) abort
|
||||
if a:symlink
|
||||
" Plainly delegate, this device is necessary because feedkeys() can't
|
||||
" access script functions directly.
|
||||
call s:NetrwMakeDir('')
|
||||
" wipe out the test buffer
|
||||
bw
|
||||
" reenable the guards
|
||||
let s:inputguards_disabled = 0
|
||||
else
|
||||
" Use feedkeys() to simulate user input (directory name)
|
||||
new
|
||||
let b:netrw_curdir = a:parentdir
|
||||
let s:inputguards_disabled = 1
|
||||
call feedkeys($"\<Cmd>call Test_NetrwMakeDir('{a:parentdir}', '{a:dirname}', 1)\<CR>{a:dirname}\<CR>", "x")
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Test file copy operations via s:NetrwMarkFileCopy()
|
||||
function Test_NetrwMarkFileCopy(source_dir, target_dir, marked_files) abort
|
||||
" set up
|
||||
new
|
||||
let b:netrw_curdir= a:source_dir
|
||||
let s:netrwmftgt = a:target_dir
|
||||
let s:netrwmarkfilelist_{bufnr("%")} = a:marked_files
|
||||
let s:netrwmftgt_islocal = 1
|
||||
" delegate
|
||||
call s:NetrwMarkFileCopy(1)
|
||||
" wipe out the test buffer
|
||||
bw
|
||||
endfunction
|
||||
|
||||
" Corner case: copy into the same dir triggers a user prompt
|
||||
function Test_NetrwMarkFileCopy_SameDir(dir = $HOME, symlink = 0) abort
|
||||
const filename = "filename.txt"
|
||||
const file = netrw#fs#PathJoin(a:dir, filename)
|
||||
|
||||
const newfilename = "newfilename.txt"
|
||||
const newfile = netrw#fs#PathJoin(a:dir, newfilename)
|
||||
|
||||
if a:symlink
|
||||
" Plainly delegate, this device is necessary because feedkeys() can't
|
||||
" access script functions directly.
|
||||
" set up
|
||||
new
|
||||
let b:netrw_curdir = a:dir
|
||||
let s:netrwmftgt = a:dir
|
||||
let s:netrwmarkfilelist_{bufnr("%")} = [filename]
|
||||
let s:netrwmftgt_islocal = 1
|
||||
|
||||
" delegate
|
||||
call s:NetrwMarkFileCopy(1)
|
||||
|
||||
" validate
|
||||
call assert_equalfile(file, newfile, "File copy in same dir failed")
|
||||
|
||||
" tear down
|
||||
call delete(file)
|
||||
call delete(newfile)
|
||||
" wipe out the test buffer
|
||||
bw
|
||||
" reenable the guards
|
||||
let s:inputguards_disabled = 0
|
||||
let s:inputdefaults_disabled = 0
|
||||
else
|
||||
" Use feedkeys() to simulate user input (directory name)
|
||||
let s:inputguards_disabled = 1
|
||||
let s:inputdefaults_disabled = 1
|
||||
|
||||
call writefile([$"NetrwMarkFileCopy test file"], file)
|
||||
|
||||
call feedkeys($"\<Cmd>call Test_NetrwMarkFileCopy_SameDir('{a:dir}', 1)\<CR>{newfilename}\<CR>", "x")
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Test file copy operations via s:NetrwMarkFileMove()
|
||||
function Test_NetrwMarkFileMove(source_dir, target_dir, marked_files) abort
|
||||
" set up
|
||||
new
|
||||
let b:netrw_curdir= a:source_dir
|
||||
let s:netrwmftgt = a:target_dir
|
||||
let s:netrwmarkfilelist_{bufnr("%")} = a:marked_files
|
||||
let s:netrwmftgt_islocal = 1
|
||||
" delegate
|
||||
call s:NetrwMarkFileMove(1)
|
||||
" wipe out the test buffer
|
||||
bw
|
||||
endfunction
|
||||
|
||||
" Test how netrw fixes paths according with settings
|
||||
" (g:netrw_keepdir, g:netrw_cygwin, tree style ...)
|
||||
function Test_NetrwFile(fname) abort
|
||||
return s:NetrwFile(a:fname)
|
||||
endfunction
|
||||
|
||||
" Test hostname validation
|
||||
function Test_NetrwValidateHostname(hostname) abort
|
||||
return s:NetrwValidateHostname(a:hostname)
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
END
|
||||
|
||||
"make copy of netrw script and add function to print local variables"
|
||||
func s:appendDebugToNetrw(netrw_path, netrw_test_path)
|
||||
let netrwScript = readfile(a:netrw_path)
|
||||
|
||||
let netrwScript += [
|
||||
\ '\n',
|
||||
\ '"-- test helpers ---"',
|
||||
\ 'function! TestNetrwCaptureRemotePath(dirname)',
|
||||
\ ' call s:RemotePathAnalysis(a:dirname)',
|
||||
\ ' return {"method": s:method, "user": s:user, "machine": s:machine, "port": s:port, "path": s:path, "fname": s:fname}',
|
||||
\ 'endfunction'
|
||||
\ ]
|
||||
" load the netrw script
|
||||
execute "split" a:netrw_test_path
|
||||
execute "read" a:netrw_path
|
||||
|
||||
" replace input guards for convenient testing versions
|
||||
%substitute@call inputsave()@call s:test_inputsave()@g
|
||||
%substitute@call inputrestore()@call s:test_inputrestore()@g
|
||||
%substitute@\<input(@s:test_input(@g
|
||||
|
||||
call cursor(1,1)
|
||||
let pos = search("Settings Restoration:")-1
|
||||
" insert the test functions before the end guard
|
||||
call assert_false(append(pos, s:testScript))
|
||||
|
||||
" save the modified script content
|
||||
write
|
||||
bwipe!
|
||||
|
||||
call writefile(netrwScript, a:netrw_test_path)
|
||||
execute 'source' a:netrw_test_path
|
||||
endfunction
|
||||
|
||||
func s:setup()
|
||||
func SetUp()
|
||||
|
||||
" prepare modified netrw script
|
||||
call s:appendDebugToNetrw(s:netrw_path, s:netrw_test_path)
|
||||
|
||||
" source the modified script
|
||||
exe "source" s:netrw_test_path
|
||||
|
||||
" Rig the package. The modified script guard prevents loading it again.
|
||||
let &runtimepath=s:runtimedir
|
||||
let &packpath=s:runtimedir
|
||||
packadd netrw
|
||||
|
||||
" use proper path
|
||||
if has('win32')
|
||||
let $HOME = substitute($HOME, '/', '\\', 'g')
|
||||
endif
|
||||
|
||||
endfunction
|
||||
|
||||
func s:cleanup()
|
||||
func TearDown()
|
||||
" cleanup
|
||||
call delete(s:netrw_test_path)
|
||||
endfunction
|
||||
|
||||
func SetShell(shell)
|
||||
" select different shells
|
||||
if a:shell == "default"
|
||||
set shell& shellcmdflag& shellxquote& shellpipe& shellredir&
|
||||
if has("win32")
|
||||
" Nvim: default 'shell' is "sh" due to $SHELL being set in Makefile,
|
||||
" but here 'shell' should be cmd.exe.
|
||||
set shell=cmd.exe
|
||||
endif
|
||||
elseif a:shell == "powershell" " help dos-powershell
|
||||
" powershell desktop is windows only
|
||||
if !has("win32")
|
||||
throw 'Skipped: powershell desktop is missing'
|
||||
endif
|
||||
set shell=powershell shellcmdflag=-NoProfile\ -Command shellxquote=\"
|
||||
set shellpipe=2>&1\ \|\ Out-File\ -Encoding\ default shellredir=2>&1\ \|\ Out-File\ -Encoding\ default
|
||||
elseif a:shell == "pwsh" " help dos-powershell
|
||||
" powershell core works crossplatform
|
||||
if !executable("pwsh")
|
||||
throw 'Skipped: powershell core is missing'
|
||||
endif
|
||||
set shell=pwsh shellcmdflag=-NoProfile\ -c shellpipe=>%s\ 2>&1 shellredir=>%s\ 2>&1
|
||||
if has("win32")
|
||||
set shellxquote=\"
|
||||
else
|
||||
set shellxquote=
|
||||
endif
|
||||
else
|
||||
call assert_report("Trying to select an unknown shell")
|
||||
endif
|
||||
" Nvim: 'shellxquote' needs to be empty for the tests for work.
|
||||
set shellxquote=
|
||||
endfunc
|
||||
|
||||
func s:combine
|
||||
\( usernames
|
||||
\, methods
|
||||
@@ -62,19 +258,16 @@ endfunction
|
||||
|
||||
|
||||
func Test_netrw_parse_remote_simple()
|
||||
call s:setup()
|
||||
let result = TestNetrwCaptureRemotePath('scp://user@localhost:2222/test.txt')
|
||||
call assert_equal(result.method, 'scp')
|
||||
call assert_equal(result.user, 'user')
|
||||
call assert_equal(result.machine, 'localhost')
|
||||
call assert_equal(result.port, '2222')
|
||||
call assert_equal(result.path, 'test.txt')
|
||||
call s:cleanup()
|
||||
endfunction
|
||||
|
||||
"testing different combinations"
|
||||
func Test_netrw_parse_regular_usernames()
|
||||
call s:setup()
|
||||
|
||||
" --- sample data for combinations ---"
|
||||
let usernames = ["root", "toor", "user01", "skillIssue"]
|
||||
@@ -86,47 +279,377 @@ func Test_netrw_parse_regular_usernames()
|
||||
|
||||
call s:combine(usernames, methods, hosts, ports, dirs, files)
|
||||
|
||||
call s:cleanup()
|
||||
endfunc
|
||||
|
||||
"Host myserver
|
||||
" HostName 192.168.1.42
|
||||
" User alice
|
||||
func Test_netrw_parse_ssh_config_entries()
|
||||
call s:setup()
|
||||
let result = TestNetrwCaptureRemotePath('scp://myserver//etc/nginx/nginx.conf')
|
||||
call assert_equal(result.method, 'scp')
|
||||
call assert_equal(result.user, '')
|
||||
call assert_equal(result.machine, 'myserver')
|
||||
call assert_equal(result.port, '')
|
||||
call assert_equal(result.path, '/etc/nginx/nginx.conf')
|
||||
call s:cleanup()
|
||||
endfunction
|
||||
|
||||
"username containing special-chars"
|
||||
func Test_netrw_parse_special_char_user()
|
||||
call s:setup()
|
||||
let result = TestNetrwCaptureRemotePath('scp://user-01@localhost:2222/test.txt')
|
||||
call assert_equal(result.method, 'scp')
|
||||
call assert_equal(result.user, 'user-01')
|
||||
call assert_equal(result.machine, 'localhost')
|
||||
call assert_equal(result.port, '2222')
|
||||
call assert_equal(result.path, 'test.txt')
|
||||
call s:cleanup()
|
||||
endfunction
|
||||
|
||||
func Test_netrw_wipe_empty_buffer_fastpath()
|
||||
func Test_netrw_empty_buffer_fastpath_wipe()
|
||||
" SetUp() may have opened some buffers
|
||||
let previous = bufnr('$')
|
||||
let g:netrw_fastbrowse=0
|
||||
packadd netrw
|
||||
call setline(1, 'foobar')
|
||||
let bufnr = bufnr('%')
|
||||
tabnew
|
||||
Explore
|
||||
call search('README.txt', 'W')
|
||||
exe ":norm \<cr>"
|
||||
call assert_equal(4, bufnr('$'))
|
||||
call assert_equal(previous + 2, bufnr('$'))
|
||||
call assert_true(bufexists(bufnr))
|
||||
bw
|
||||
|
||||
unlet! netrw_fastbrowse
|
||||
endfunction
|
||||
|
||||
" Test UNC paths on windows
|
||||
func Test_netrw_check_UNC_paths()
|
||||
CheckMSWindows
|
||||
|
||||
let test_paths = [
|
||||
\ '\\Server2\Share\Test\Foo.txt',
|
||||
\ '//Server2/Share/Test/Foo.txt',
|
||||
\ '\\Server2\Share\Test\',
|
||||
\ '//Server2/Share/Test/',
|
||||
\ '\\wsl.localhost\Ubuntu\home\user\_vimrc',
|
||||
\ '//wsl.localhost/Ubuntu/home/user/_vimrc',
|
||||
\ '\\wsl.localhost\Ubuntu\home\user',
|
||||
\ '//wsl.localhost/Ubuntu/home/user']
|
||||
|
||||
" The paths must be interpreted as absolute ones
|
||||
for path in test_paths
|
||||
call assert_equal(
|
||||
\ path,
|
||||
\ Test_NetrwFile(path),
|
||||
\ $"UNC path: {path} misinterpreted")
|
||||
endfor
|
||||
|
||||
endfunction
|
||||
|
||||
" ---------------------------------
|
||||
" Testing file management functions
|
||||
" ---------------------------------
|
||||
|
||||
" Browser directory creation
|
||||
func s:netrw_mkdir()
|
||||
|
||||
" create a testdir in the fake $HOME
|
||||
call Test_NetrwMakeDir($HOME, "NetrwMakeDir")
|
||||
|
||||
" Check the test directory was created
|
||||
let test_dir = netrw#fs#PathJoin($HOME, "NetrwMakeDir")
|
||||
call WaitForAssert({-> assert_true(
|
||||
\ isdirectory(test_dir),
|
||||
\ "Unable to create a dir via s:NetrwMakeDir()")
|
||||
\ })
|
||||
|
||||
" remove the test directory
|
||||
call delete(test_dir, 'd')
|
||||
endfunc
|
||||
|
||||
func Test_netrw_mkdir_default()
|
||||
call SetShell('default')
|
||||
call s:netrw_mkdir()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_mkdir_powershell()
|
||||
call SetShell('powershell')
|
||||
call s:netrw_mkdir()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_mkdir_pwsh()
|
||||
call SetShell('pwsh')
|
||||
call s:netrw_mkdir()
|
||||
endfunc
|
||||
|
||||
func s:netrw_filecopy(count = 1)
|
||||
" setup
|
||||
let marked_files = []
|
||||
let source_dir = netrw#fs#PathJoin($HOME, "src")
|
||||
let target_dir = netrw#fs#PathJoin($HOME, "target")
|
||||
|
||||
call mkdir(source_dir, "R")
|
||||
call mkdir(target_dir, "R")
|
||||
|
||||
for i in range(a:count)
|
||||
call add(marked_files, $"testfile{i}.txt")
|
||||
call writefile(
|
||||
\ [$"NetrwMarkFileCopy test file {i}"],
|
||||
\ netrw#fs#PathJoin(source_dir, marked_files[-1]))
|
||||
endfor
|
||||
|
||||
" delegate
|
||||
call Test_NetrwMarkFileCopy(source_dir, target_dir, marked_files)
|
||||
|
||||
" verify
|
||||
for file in marked_files
|
||||
call assert_equalfile(
|
||||
\ netrw#fs#PathJoin(source_dir, file),
|
||||
\ netrw#fs#PathJoin(target_dir, file),
|
||||
\ "File copy failed for " . file)
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
" Browser file copy
|
||||
func s:test_netrw_filecopy()
|
||||
|
||||
" if shellslash is available, check both settings
|
||||
if exists('+shellslash')
|
||||
set shellslash&
|
||||
call s:netrw_filecopy(1)
|
||||
call s:netrw_filecopy(10)
|
||||
set shellslash!
|
||||
endif
|
||||
|
||||
call s:netrw_filecopy(1)
|
||||
call s:netrw_filecopy(10)
|
||||
|
||||
endfunc
|
||||
|
||||
func Test_netrw_filecopy_default()
|
||||
call SetShell('default')
|
||||
call s:test_netrw_filecopy()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_filecopy_powershell()
|
||||
call SetShell('powershell')
|
||||
call s:test_netrw_filecopy()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_filecopy_pwsh()
|
||||
call SetShell('pwsh')
|
||||
call s:test_netrw_filecopy()
|
||||
endfunc
|
||||
|
||||
" Browser recursive directory copy
|
||||
func s:netrw_dircopy(count = 1)
|
||||
|
||||
" setup
|
||||
let marked_dirname = "test_dir"
|
||||
let marked_dir = netrw#fs#PathJoin($HOME, marked_dirname)
|
||||
let target_dir = netrw#fs#PathJoin($HOME, "target")
|
||||
|
||||
call mkdir(marked_dir, "R")
|
||||
call mkdir(target_dir, "R")
|
||||
|
||||
let dir_content = []
|
||||
for i in range(a:count)
|
||||
call add(dir_content, $"testfile{i}.txt")
|
||||
call writefile(
|
||||
\ [$"NetrwMarkFileCopy test dir content {i}"],
|
||||
\ netrw#fs#PathJoin(marked_dir, dir_content[-1]))
|
||||
endfor
|
||||
|
||||
" delegate
|
||||
call Test_NetrwMarkFileCopy($HOME, target_dir, [marked_dirname])
|
||||
|
||||
" verify
|
||||
for file in dir_content
|
||||
call assert_equalfile(
|
||||
\ netrw#fs#PathJoin(marked_dir, file),
|
||||
\ netrw#fs#PathJoin(target_dir, marked_dirname, file),
|
||||
\ "File copy failed for " . file)
|
||||
endfor
|
||||
|
||||
endfunc
|
||||
|
||||
func s:test_netrw_dircopy()
|
||||
|
||||
" if shellslash is available, check both settings
|
||||
if exists('+shellslash')
|
||||
set shellslash&
|
||||
call s:netrw_dircopy(10)
|
||||
set shellslash!
|
||||
endif
|
||||
|
||||
call s:netrw_dircopy(10)
|
||||
|
||||
endfunc
|
||||
|
||||
func Test_netrw_dircopy_default()
|
||||
call SetShell('default')
|
||||
call s:test_netrw_dircopy()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_dircopy_powershell()
|
||||
call SetShell('powershell')
|
||||
call s:test_netrw_dircopy()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_dircopy_pwsh()
|
||||
call SetShell('pwsh')
|
||||
call s:test_netrw_dircopy()
|
||||
endfunc
|
||||
|
||||
" Copy file into the same directory with a different name
|
||||
func Test_netrw_dircopy_rename_default()
|
||||
call SetShell('default')
|
||||
call Test_NetrwMarkFileCopy_SameDir()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_dircopy_rename_powershell()
|
||||
call SetShell('powershell')
|
||||
call Test_NetrwMarkFileCopy_SameDir()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_dircopy_rename_pwsh()
|
||||
call SetShell('pwsh')
|
||||
call Test_NetrwMarkFileCopy_SameDir()
|
||||
endfunc
|
||||
|
||||
" Browser file move
|
||||
func s:netrw_filemove(count = 1)
|
||||
" setup
|
||||
let marked_files = []
|
||||
let source_dir = netrw#fs#PathJoin($HOME, "src")
|
||||
let target_dir = netrw#fs#PathJoin($HOME, "target")
|
||||
|
||||
call mkdir(source_dir, "R")
|
||||
call mkdir(target_dir, "R")
|
||||
|
||||
for i in range(a:count)
|
||||
call add(marked_files, $"testfile{i}.txt")
|
||||
call writefile(
|
||||
\ [$"NetrwMarkFileMove test file {i}"],
|
||||
\ netrw#fs#PathJoin(source_dir, marked_files[-1]))
|
||||
endfor
|
||||
|
||||
" delegate
|
||||
call Test_NetrwMarkFileMove(source_dir, target_dir, marked_files)
|
||||
|
||||
" verify
|
||||
for i in range(a:count)
|
||||
call assert_equal(
|
||||
\ [$"NetrwMarkFileMove test file {i}"],
|
||||
\ readfile(netrw#fs#PathJoin(target_dir, $"testfile{i}.txt")),
|
||||
\ $"File move failed for testfile{i}.txt")
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
func s:test_netrw_filemove()
|
||||
|
||||
" if shellslash is available, check both settings
|
||||
if exists('+shellslash')
|
||||
set shellslash&
|
||||
call s:netrw_filemove(10)
|
||||
set shellslash!
|
||||
endif
|
||||
|
||||
call s:netrw_filemove(10)
|
||||
|
||||
endfunc
|
||||
|
||||
func Test_netrw_filemove_default()
|
||||
call SetShell('default')
|
||||
call s:test_netrw_filemove()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_filemove_powershell()
|
||||
call SetShell('powershell')
|
||||
call s:test_netrw_filemove()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_filemove_pwsh()
|
||||
call SetShell('pwsh')
|
||||
call s:test_netrw_filemove()
|
||||
endfunc
|
||||
|
||||
func Test_netrw_reject_evil_hostname()
|
||||
let msg = execute(':e scp://x;touch RCE;x/dir/')
|
||||
let msg = split(msg, "\n")[-1]
|
||||
call assert_match('Rejecting invalid hostname', msg)
|
||||
endfunc
|
||||
|
||||
func Test_netrw_hostname()
|
||||
let valid_hostnames = [
|
||||
\ 'localhost',
|
||||
\ '127.0.0.1',
|
||||
\ '::1',
|
||||
\ '0:0:0:0:0:0:0:1',
|
||||
\ 'user@localhost',
|
||||
\ 'usuario@127.0.0.1',
|
||||
\ 'utilisateur@::1',
|
||||
\ 'benutzer@0:0:0:0:0:0:0:1',
|
||||
\ 'localhost:22',
|
||||
\ '127.0.0.1:80',
|
||||
\ '[::1]:443',
|
||||
\ '[0:0:0:0:0:0:0:1]:5432',
|
||||
\ 'user@localhost:22',
|
||||
\ 'usuario@127.0.0.1:80',
|
||||
\ 'utilisateur@[::1]:443',
|
||||
\ 'benutzer@[0:0:0:0:0:0:0:1]:5432']
|
||||
|
||||
for hostname in valid_hostnames
|
||||
call assert_true(Test_NetrwValidateHostname(hostname), $"Valid hostname {hostname} was rejected")
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
func Test_netrw_FileUrlEdit_pipe_injection()
|
||||
CheckUnix
|
||||
CheckExecutable id
|
||||
let fname = 'Xtestfile'
|
||||
let url = 'file:///tmp/file.md%7C!id>'..fname
|
||||
sil call netrw#FileUrlEdit(url)
|
||||
call assert_false(filereadable(fname), 'Command injection via pipe in file URL')
|
||||
endfunc
|
||||
|
||||
" The remote filename after '.' was allowed to contain shell metacharacters
|
||||
" and rode unescaped into the tempfile name passed to sftp/file_cmd, giving a
|
||||
" shell injection on :e sftp://host/foo.txt;<cmd>.
|
||||
func Test_netrw_tempfile_suffix_injection()
|
||||
CheckUnix
|
||||
CheckExecutable id
|
||||
let save_sftp = g:netrw_sftp_cmd
|
||||
let save_file = exists('g:netrw_file_cmd') ? g:netrw_file_cmd : v:null
|
||||
let g:netrw_sftp_cmd = 'true'
|
||||
let g:netrw_file_cmd = 'true'
|
||||
let fname = 'Xrce_marker'
|
||||
try
|
||||
call delete(fname)
|
||||
sil! call netrw#NetRead(2, 'sftp://localhost/foo.txt;id>'..fname)
|
||||
call assert_false(filereadable(fname), 'Command injection via sftp:// tempfile suffix')
|
||||
|
||||
call delete(fname)
|
||||
sil! call netrw#NetRead(2, 'file://localhost/foo.txt;id>'..fname)
|
||||
call assert_false(filereadable(fname), 'Command injection via file:// tempfile suffix')
|
||||
finally
|
||||
call delete(fname)
|
||||
let g:netrw_sftp_cmd = save_sftp
|
||||
if save_file is v:null
|
||||
unlet! g:netrw_file_cmd
|
||||
else
|
||||
let g:netrw_file_cmd = save_file
|
||||
endif
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func Test_netrw_RFC2396()
|
||||
let fname = 'a%20b'
|
||||
call assert_equal('a b', netrw#RFC2396(fname))
|
||||
endfunc
|
||||
|
||||
func Test_netrw_Home_tilde()
|
||||
Explore ~
|
||||
call assert_match('Netrw Directory Listing', getline(2))
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
" vim:ts=8 sts=2 sw=2 et
|
||||
|
||||
Reference in New Issue
Block a user