From e99e95c0985f139e10fdd9272fc11907ea78f38b Mon Sep 17 00:00:00 2001 From: Bastian Germann Date: Tue, 22 Jul 2025 16:01:38 +0200 Subject: [PATCH] Support tar.exe as backend Fixes: #109 --- README.rst | 1 + dumprar.py | 2 +- rarfile.py | 15 ++++++++++++++- test/test_tool.py | 24 ++++++++++++++++++++++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index df670cf..9564a58 100644 --- a/README.rst +++ b/README.rst @@ -45,6 +45,7 @@ Backends: | | | * Requires ``p7zip-rar`` package on Debian/Ubuntu. | +-------------+----------------------+-----------------------------------------------------+ | bsdtar_ | Supported | * Not recommended: limited RAR format support. | +| | | * Available on Windows as tar.exe . | | | | * Does not support multi-volume archives. | | | | * Does not support solid archives. | | | | * Does not support password-protected archives. | diff --git a/dumprar.py b/dumprar.py index 8dca844..c7ffc97 100755 --- a/dumprar.py +++ b/dumprar.py @@ -618,7 +618,7 @@ def main(): if cf_backend: cf_backend = {"7z": "sevenzip", "7zz": "sevenzip2"}.get(cf_backend, cf_backend) - conf = {"unrar": False, "unar": False, "bsdtar": False, "sevenzip": False, "sevenzip2": False} + conf = {"unrar": False, "unar": False, "bsdtar": False, "tarexe": False, "sevenzip": False, "sevenzip2": False} assert cf_backend in conf, f"unknown backend: {cf_backend}" conf[cf_backend] = True rf.tool_setup(force=True, **conf) diff --git a/rarfile.py b/rarfile.py index fc95f7f..338d079 100644 --- a/rarfile.py +++ b/rarfile.py @@ -110,6 +110,9 @@ def __init__(self, key, iv): #: executable for bsdtar tool BSDTAR_TOOL = "bsdtar" +#: executable for tar.exe tool (bsdtar on Windows) +TAREXE_TOOL = "tar.exe" + #: executable for p7zip/7z tool SEVENZIP_TOOL = "7z" @@ -3459,6 +3462,14 @@ def add_password_arg(self, cmdline, pwd): "errmap": [None], } +TAREXE_CONFIG = { + "open_cmd": ("TAREXE_TOOL", "-x", "--to-stdout", "-f"), + "check_cmd": ("TAREXE_TOOL", "--version"), + "password": None, + "no_password": (), + "errmap": [None], +} + SEVENZIP_CONFIG = { "open_cmd": ("SEVENZIP_TOOL", "e", "-so", "-bb0"), "check_cmd": ("SEVENZIP_TOOL", "i"), @@ -3482,7 +3493,7 @@ def add_password_arg(self, cmdline, pwd): CURRENT_SETUP = None -def tool_setup(unrar=True, unar=True, bsdtar=True, sevenzip=True, sevenzip2=True, force=False): +def tool_setup(unrar=True, unar=True, bsdtar=True, tarexe=True, sevenzip=True, sevenzip2=True, force=False): """Pick a tool, return cached ToolSetup. """ global CURRENT_SETUP @@ -3501,6 +3512,8 @@ def tool_setup(unrar=True, unar=True, bsdtar=True, sevenzip=True, sevenzip2=True lst.append(SEVENZIP2_CONFIG) if bsdtar: lst.append(BSDTAR_CONFIG) + if tarexe: + lst.append(TAREXE_CONFIG) for conf in lst: setup = ToolSetup(conf) diff --git a/test/test_tool.py b/test/test_tool.py index 48dc7d5..ade18c5 100644 --- a/test/test_tool.py +++ b/test/test_tool.py @@ -18,9 +18,9 @@ def have_tool(name): return False -def tool_setup(unrar=False, unar=False, bsdtar=False, sevenzip=False, sevenzip2=False): +def tool_setup(unrar=False, unar=False, bsdtar=False, tarexe=False, sevenzip=False, sevenzip2=False): rarfile.FORCE_TOOL = True - rarfile.tool_setup(unrar=unrar, unar=unar, bsdtar=bsdtar, + rarfile.tool_setup(unrar=unrar, unar=unar, bsdtar=bsdtar, tarexe=tarexe, sevenzip=sevenzip, sevenzip2=sevenzip2, force=True) @@ -37,6 +37,10 @@ def install_bsdtar_tool(): tool_setup(bsdtar=True) +def install_tarexe_tool(): + tool_setup(tarexe=True) + + def install_7z_tool(): tool_setup(sevenzip=True) @@ -123,6 +127,22 @@ def test_bsdtar_tool(): uninstall_alt_tool() +@pytest.mark.skipif(not have_tool(rarfile.TAREXE_TOOL), reason="tar.exe not installed") +def test_tarexe_tool(): + install_tarexe_tool() + try: + with rarfile.RarFile("test/files/rar3-comment-plain.rar") as rf: + rf.read("file1.txt") + rf.read("file2.txt") + + with pytest.raises(rarfile.RarCannotExec): + with rarfile.RarFile("test/files/rar3-comment-psw.rar") as rf: + rf.setpassword("password") + rf.read("file1.txt") + finally: + uninstall_alt_tool() + + @pytest.mark.skipif(not have_tool(rarfile.SEVENZIP_TOOL), reason="7z not installed") def test_7z_tool(): install_7z_tool()