From: Aaron Burrow Date: Thu, 9 Apr 2026 19:11:16 +0000 (+0000) Subject: patch 9.2.0326: runtime(tar): but with dotted path X-Git-Tag: v9.2.0326^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a1bcc67b4b6fc2dfe564ab4faca490f8afac857;p=thirdparty%2Fvim.git patch 9.2.0326: runtime(tar): but with dotted path Problem: runtime(tar): but with dotted path Solution: Do not strip everything after the first dot (Aaron Burrow) tar#Extract was getting the extensionless basename by stripping away everything starting with the leftmost dot. So if a directory had a dot or the file had an 'extra' dot then the code did the wrong thing. For example, if it was given: /tmp/foo.bar/baz.tar.gz Then it would treat /tmp/foo as the extensionless basename, but it actually should have grabbed: /tmp/foo.bar/baz This patch fixes the issue by instead looking at the rightmost dot(s). This bug was discovered by ChatGPT 5.4. I wrote the patch and tested vim. closes: #19930 Signed-off-by: Aaron Burrow Signed-off-by: Christian Brabandt --- diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index b068bd181b..722a0ab680 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -22,6 +22,7 @@ " 2026 Feb 07 by Vim Project: make the path traversal detection more robust (#19341) " 2026 Apr 06 by Vim Project: fix bugs with lz4 support (#19925) " 2026 Apr 09 by Vim Project: fix bugs with zstd support (#19930) +" 2026 Apr 09 by Vim Project: fix bug with dotted filename (#19930) " " Contains many ideas from Michael Toren's " @@ -612,117 +613,120 @@ fun! tar#Extract() return endif + let extractcmd= s:WinPath(g:tar_extractcmd) let tarball = expand("%") - let tarbase = substitute(tarball,'\..*$','','') + if !filereadable(tarball) + let &report= repkeep + return + endif - let extractcmd= s:WinPath(g:tar_extractcmd) - if filereadable(tarbase.".tar") - call system(extractcmd." ".shellescape(tarbase).".tar ".shellescape(fname)) + if tarball =~# "\.tar$" + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ". fname endif - elseif filereadable(tarbase.".tgz") + elseif tarball =~# "\.tgz$" let extractcmd= substitute(extractcmd,"-","-z","") - call system(extractcmd." ".shellescape(tarbase).".tgz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tgz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.gz") + elseif tarball =~# "\.tar\.gz$" let extractcmd= substitute(extractcmd,"-","-z","") - call system(extractcmd." ".shellescape(tarbase).".tar.gz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.gz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tbz") + elseif tarball =~# "\.tbz$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarbase).".tbz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tbz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.bz2") + elseif tarball =~# "\.tar\.bz2$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarbase).".tar.bz2 ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.bz2 {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.bz3") + elseif tarball =~# "\.tar\.bz3$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarbase).".tar.bz3 ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.bz3 {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".txz") + elseif tarball =~# "\.txz$" let extractcmd= substitute(extractcmd,"-","-J","") - call system(extractcmd." ".shellescape(tarbase).".txz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.txz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.xz") + elseif tarball =~# "\.tar\.xz$" let extractcmd= substitute(extractcmd,"-","-J","") - call system(extractcmd." ".shellescape(tarbase).".tar.xz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.xz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tzst") + elseif tarball =~# "\.tzst$" let extractcmd= substitute(extractcmd,"-","--zstd -","") - call system(extractcmd." ".shellescape(tarbase).".tzst ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tzst {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.zst") + elseif tarball =~# "\.tar\.zst$" let extractcmd= substitute(extractcmd,"-","--zstd -","") - call system(extractcmd." ".shellescape(tarbase).".tar.zst ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.zst {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tlz4") + elseif tarball =~# "\.tlz4$" if has("linux") let extractcmd= substitute(extractcmd,"-","-I lz4 -","") endif - call system(extractcmd." ".shellescape(tarbase).".tlz4 ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tlz4 {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.lz4") + elseif tarball =~# "\.tar\.lz4$" if has("linux") let extractcmd= substitute(extractcmd,"-","-I lz4 -","") endif - call system(extractcmd." ".shellescape(tarbase).".tar.lz4 ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.lz4 {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index 28cf709f85..bde79052dd 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -263,3 +263,53 @@ def g:Test_extraction() bw! endfor enddef + +def g:Test_extract_with_dotted_dir() + delete('X.txt') + writefile(['when they kiss they spit white noise'], 'X.txt') + + var dirname = tempname() + mkdir(dirname, 'R') + dirname = dirname .. '/foo.bar' + mkdir(dirname, 'R') + var tarpath = dirname .. '/Xarchive.tar.gz' + system('tar -czf ' .. tarpath .. ' X.txt') + assert_true(filereadable(tarpath)) + assert_equal(0, v:shell_error) + + delete('X.txt') + defer delete(tarpath) + + execute 'e ' .. tarpath + assert_match('X.txt', getline(5)) + :5 + normal x + assert_true(filereadable('X.txt')) + assert_equal(['when they kiss they spit white noise'], readfile('X.txt')) + delete('X.txt') + bw! +enddef + +def g:Test_extract_with_dotted_filename() + delete('X.txt') + writefile(['holiday inn'], 'X.txt') + + var dirname = tempname() + mkdir(dirname, 'R') + var tarpath = dirname .. '/Xarchive.foo.tar.gz' + system('tar -czf ' .. tarpath .. ' X.txt') + assert_true(filereadable(tarpath)) + assert_equal(0, v:shell_error) + + delete('X.txt') + defer delete(tarpath) + + execute 'e ' .. tarpath + assert_match('X.txt', getline(5)) + :5 + normal x + assert_true(filereadable('X.txt')) + assert_equal(['holiday inn'], readfile('X.txt')) + delete('X.txt') + bw! +enddef diff --git a/src/version.c b/src/version.c index 5fc6428c2c..70bca36892 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 326, /**/ 325, /**/