From 327739f4e2da8a9dde0f6f7f5ecd4fe65393ad90 Mon Sep 17 00:00:00 2001 From: Aaron Burrow Date: Sun, 5 Apr 2026 19:50:06 -0400 Subject: [PATCH 1/4] Fix three bugs with extracting lz4'd tar archives. The tar plugin allows users to extract files from tar archives that are compressed with lz4. But, tar#Extract() builds malformed extraction commands for lz4-compressed tar archives. This commit fixes three issues in that code. The first affects archives with a .tlz4 extension and the other two affect archives with .tar.lz4 extension (but one of these is symmetric to the issue that .tlz4 archives had). (1) When trying to extract .tlz4 archives the command created by tar#Extract looked like this: tar -I lz4pxf foo.tlz4 foo This isn't right. It should be something like this: tar -I lz4 -pxf foo.tlz4 foo This was happening because tar.plugin is just substituting on the first - in "tar -pxf". This works fine if we just add a simple flag for extraction (eg, z for .tgz), but for lz4 we need to add "-I lz4". I don't believe that there is an obvious good way to fix this without reworking the way the command is generated. Probably we should collect the command and flags separately and the flags should be stored in a set. Then put everything together into a string just before issuing it as an extraction command. Unfortunately, this might break things for users because they have access to tar_extractcmd. This patch just makes the substitution a little bit more clever so that it does the right thing when substituting on a string like "tar -pxf". (2) .tar.lz4 extractions had the same issue, which my patch fixes in the same way. (3) .tar.lz4 extractions had another issue. There was a space missing in the command generated by tar#Extract. This meant that commands looked like this (notice the lack of space between the archive and output file names): tar -I lz4pxf foo.tar.lz4foo This patch just puts a space where it should be. Finally, I should note that ChatGPT 5.4 initially identified this issue in the code and generated the test cases. I reviewed the test cases, wrote the patch, and actually ran vim against the tests (both with and without the patch). Signed-off-by: Aaron Burrow --- runtime/autoload/tar.vim | 6 ++-- src/testdir/test_plugin_tar.vim | 54 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 73829a2d5c6e2d..126619eb0a6516 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -704,7 +704,7 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tlz4") - let extractcmd= substitute(extractcmd,"-","-I lz4","") + let extractcmd= substitute(extractcmd,"-","-I lz4 -","") call system(extractcmd." ".shellescape(tarbase).".tlz4 ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tlz4 {fname}: failed!") @@ -713,8 +713,8 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tar.lz4") - let extractcmd= substitute(extractcmd,"-","-I lz4","") - call system(extractcmd." ".shellescape(tarbase).".tar.lz4".shellescape(fname)) + let extractcmd= substitute(extractcmd,"-","-I lz4 -","") + call system(extractcmd." ".shellescape(tarbase).".tar.lz4 ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.lz4 {fname}: failed!") else diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index 8d34ce11a78a2b..d6dbdd7613fa6c 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -147,3 +147,57 @@ def g:Test_tar_path_traversal_with_nowrapscan() bw! enddef + +def g:Test_tar_lz4_extract() + CheckExecutable lz4 + + delete('X.txt') + delete('Xarchive.tar') + delete('Xarchive.tar.lz4') + call writefile(['hello'], 'X.txt') + call system('tar -cf Xarchive.tar X.txt') + assert_equal(0, v:shell_error) + + call system('lz4 -z Xarchive.tar Xarchive.tar.lz4') + assert_equal(0, v:shell_error) + + delete('X.txt') + delete('Xarchive.tar') + defer delete('Xarchive.tar.lz4') + + e Xarchive.tar.lz4 + assert_match('X.txt', getline(5)) + :5 + normal x + assert_true(filereadable('X.txt')) + assert_equal(['hello'], readfile('X.txt')) + delete('X.txt') + bw! +enddef + +def g:Test_tlz4_extract() + CheckExecutable lz4 + + delete('X.txt') + delete('Xarchive.tar') + delete('Xarchive.tlz4') + call writefile(['goodbye'], 'X.txt') + call system('tar -cf Xarchive.tar X.txt') + assert_equal(0, v:shell_error) + + call system('lz4 -z Xarchive.tar Xarchive.tlz4') + assert_equal(0, v:shell_error) + + delete('X.txt') + delete('Xarchive.tar') + defer delete('Xarchive.tlz4') + + e Xarchive.tlz4 + assert_match('X.txt', getline(5)) + :5 + normal x + assert_true(filereadable('X.txt')) + assert_equal(['goodbye'], readfile('X.txt')) + delete('X.txt') + bw! +enddef From d783af1d9670aa28a569bb5087eefb91944759c5 Mon Sep 17 00:00:00 2001 From: Aaron Burrow Date: Sun, 5 Apr 2026 21:22:43 -0400 Subject: [PATCH 2/4] Try --use-compress-program instead of -I. --- runtime/autoload/tar.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 126619eb0a6516..d40617dc0ecd7e 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -704,7 +704,7 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tlz4") - let extractcmd= substitute(extractcmd,"-","-I lz4 -","") + let extractcmd= substitute(extractcmd,"-","--use-compress-program=lz4 -","") call system(extractcmd." ".shellescape(tarbase).".tlz4 ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tlz4 {fname}: failed!") @@ -713,7 +713,7 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tar.lz4") - let extractcmd= substitute(extractcmd,"-","-I lz4 -","") + let extractcmd= substitute(extractcmd,"-","--use-compress-program=lz4 -","") call system(extractcmd." ".shellescape(tarbase).".tar.lz4 ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.lz4 {fname}: failed!") From 0924637f7c86a8cdf0488846f2fa2c8761d3696c Mon Sep 17 00:00:00 2001 From: Aaron Burrow Date: Sun, 5 Apr 2026 21:41:52 -0400 Subject: [PATCH 3/4] --use-compress-program didn't help on FreeBSD or OS X. Just disable the tests for them. Signed-off-by: Aaron Burrow --- runtime/autoload/tar.vim | 4 ++-- src/testdir/test_plugin_tar.vim | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index d40617dc0ecd7e..126619eb0a6516 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -704,7 +704,7 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tlz4") - let extractcmd= substitute(extractcmd,"-","--use-compress-program=lz4 -","") + let extractcmd= substitute(extractcmd,"-","-I lz4 -","") call system(extractcmd." ".shellescape(tarbase).".tlz4 ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tlz4 {fname}: failed!") @@ -713,7 +713,7 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tar.lz4") - let extractcmd= substitute(extractcmd,"-","--use-compress-program=lz4 -","") + let extractcmd= substitute(extractcmd,"-","-I lz4 -","") call system(extractcmd." ".shellescape(tarbase).".tar.lz4 ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.lz4 {fname}: failed!") diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index d6dbdd7613fa6c..de2e429a86d228 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -149,6 +149,10 @@ def g:Test_tar_path_traversal_with_nowrapscan() enddef def g:Test_tar_lz4_extract() + if has("mac") || has("bsd") + return + endif + CheckExecutable lz4 delete('X.txt') @@ -176,6 +180,10 @@ def g:Test_tar_lz4_extract() enddef def g:Test_tlz4_extract() + if has("mac") || has("bsd") + return + endif + CheckExecutable lz4 delete('X.txt') From 3e8879784ba24c61add7d3714dc5b5dadee253aa Mon Sep 17 00:00:00 2001 From: Aaron Burrow Date: Mon, 6 Apr 2026 05:20:57 -0400 Subject: [PATCH 4/4] Only make the lz4 sub on Linux because FreeBSD's tar automatically handles lz4. --- runtime/autoload/tar.vim | 8 ++++++-- src/testdir/test_plugin_tar.vim | 8 -------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 126619eb0a6516..2a5f21fba79392 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -704,7 +704,9 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tlz4") - let extractcmd= substitute(extractcmd,"-","-I lz4 -","") + if has("linux") + let extractcmd= substitute(extractcmd,"-","-I lz4 -","") + endif call system(extractcmd." ".shellescape(tarbase).".tlz4 ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tlz4 {fname}: failed!") @@ -713,7 +715,9 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tar.lz4") - let extractcmd= substitute(extractcmd,"-","-I lz4 -","") + if has("linux") + let extractcmd= substitute(extractcmd,"-","-I lz4 -","") + endif call system(extractcmd." ".shellescape(tarbase).".tar.lz4 ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.lz4 {fname}: failed!") diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index de2e429a86d228..d6dbdd7613fa6c 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -149,10 +149,6 @@ def g:Test_tar_path_traversal_with_nowrapscan() enddef def g:Test_tar_lz4_extract() - if has("mac") || has("bsd") - return - endif - CheckExecutable lz4 delete('X.txt') @@ -180,10 +176,6 @@ def g:Test_tar_lz4_extract() enddef def g:Test_tlz4_extract() - if has("mac") || has("bsd") - return - endif - CheckExecutable lz4 delete('X.txt')