From 6790380c7024f89d88173de005407ed8ffce8884 Mon Sep 17 00:00:00 2001 From: Bastian Kleineidam Date: Sun, 21 Feb 2010 15:48:52 +0100 Subject: [PATCH] Implemented create command --- doc/todo.txt | 4 ++-- patool | 24 +++++++++++------------ patoolib/__init__.py | 22 +++++++++++++++------ patoolib/arj.py | 15 ++++++++++++++ patoolib/bzip2.py | 15 ++++++++++++++ patoolib/compress.py | 30 ++++++++++++++++++++++++++++ patoolib/cpio.py | 17 ++++++++++++++++ patoolib/gzip.py | 13 +++++++++++++ patoolib/lzop.py | 10 ++++++++++ patoolib/p7zip.py | 12 ++++++++++++ patoolib/pbzip2.py | 2 +- patoolib/rar.py | 11 +++++++++++ patoolib/star.py | 42 ++++++++++++++++++++++------------------ patoolib/tar.py | 9 +++++++-- patoolib/util.py | 1 + tests/__init__.py | 23 +++++++++++++++++++++- tests/data/foo.txt | 1 + tests/data/foo/bar/t.txt | 1 + tests/data/foo/t.txt | 1 + tests/test_archives.py | 27 +++++++++++++++++--------- 20 files changed, 228 insertions(+), 52 deletions(-) create mode 100644 patoolib/compress.py create mode 100644 tests/data/foo.txt create mode 100644 tests/data/foo/bar/t.txt create mode 100644 tests/data/foo/t.txt diff --git a/doc/todo.txt b/doc/todo.txt index e749024..a7306e5 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -1,2 +1,2 @@ -- add create mode? -- add test mode? +- add create command +- move all program modules into subpackage diff --git a/patool b/patool index 35a49a2..9e95d70 100755 --- a/patool +++ b/patool @@ -20,39 +20,39 @@ patool [extract|list] [sub-command-options] import sys if not hasattr(sys, "version_info") or sys.version_info < (2, 5, 0, "final", 0): raise SystemExit("Error, this script needs Python >= 2.5 installed") -import patoolib +from patoolib import handle_archive, list_formats, baker -@patoolib.baker.command +@baker.command def extract (archive, verbose=False, force=False): """Extract files from an archive.""" - return patoolib.handle_archive(archive, 'extract') + return handle_archive(archive, 'extract') -@patoolib.baker.command +@baker.command def list (archive, verbose=False): """List files in an archive.""" - return patoolib.handle_archive(archive, 'list') + return handle_archive(archive, 'list') @baker.command def create (archive, *args): """Create an archive from given files.""" - return patoolib.handle_archive(archive, 'create', *args) + return handle_archive(archive, 'create', *args) -@patoolib.baker.command +@baker.command def test (archive, verbose=False): """Test files in an archive.""" - return patoolib.handle_archive(archive, 'test', verbose=verbose) + return handle_archive(archive, 'test', verbose=verbose) -@patoolib.baker.command +@baker.command def formats (): - return patoolib.list_formats() + return list_formats() try: - sys.exit(patoolib.baker.run()) -except patoolib.baker.CommandError, msg: + sys.exit(baker.run()) +except baker.CommandError, msg: print >>sys.stderr, "patool error:", msg sys.exit(1) diff --git a/patoolib/__init__.py b/patoolib/__init__.py index e2a0a2b..b8b94b9 100644 --- a/patoolib/__init__.py +++ b/patoolib/__init__.py @@ -16,7 +16,7 @@ import os import shutil from distutils.spawn import find_executable -from . import util, baker +from . import util # Supported archive commands ArchiveCommands = ('list', 'extract', 'test', 'create') @@ -56,6 +56,7 @@ ArchivePrograms = { 'bzip2': { 'extract': ('pbzip2', 'bzip2', '7z'), 'test': ('pbzip2', 'bzip2', '7z'), + 'create': ('pbzip2', 'bzip2', '7z'), 'list': ('7z', 'echo',), }, 'tar': { @@ -73,6 +74,7 @@ ArchivePrograms = { 'extract': ('gzip', '7z', 'uncompress.real'), 'list': ('7z', 'echo',), 'test': ('gzip', '7z'), + 'create': ('compress',), }, '7z': { None: ('7z',), @@ -89,14 +91,16 @@ ArchivePrograms = { 'test': ('cabextract', '7z'), }, 'arj': { - 'extract': ('arj', '7z'), - 'list': ('arj', '7z'), - 'test': ('arj', '7z'), + None: ('arj',), + 'extract': ('7z',), + 'list': ('7z',), + 'test': ('7z',), }, 'cpio': { 'extract': ('cpio', '7z'), 'list': ('cpio', '7z'), 'test': ('7z',), + 'create': ('cpio',), }, 'rpm': { # XXX rpm2cpio depends on cpio which is not checked @@ -125,7 +129,7 @@ ProgramModules = { def get_archive_format (filename): """Detect filename archive format.""" mime, encoding = util.guess_mime(filename) - if not mime: + if not (mime or encoding): raise util.PatoolError("unknown archive format for file `%s'" % filename) if mime in ArchiveMimetypes: format = ArchiveMimetypes[mime] @@ -158,6 +162,8 @@ def find_archive_program (format, command): for key in (None, command): if key in commands: programs.extend(commands[key]) + if not programs: + raise util.PatoolError("%s archive format `%s' is not supported" % (command, format)) # return the first existing program for program in programs: exe = find_executable(program) @@ -272,6 +278,10 @@ def _handle_archive (archive, command, *args, **kwargs): check_archive_format(format, encoding) check_archive_command(command) config = parse_config(format, command, **kwargs) + if command == 'create': + # check if archive already exists + if os.path.exists(archive) and not config['force']: + raise util.PatoolError("archive `%s' already exists, and --force option was not given" % archive) program = config['program'] # get python module for given archive program key = os.path.basename(program).lower() @@ -286,7 +296,7 @@ def _handle_archive (archive, command, *args, **kwargs): outdir = util.tmpdir(dir=os.getcwd()) kwargs['outdir'] = outdir try: - cmdlist = get_archive_cmdlist(archive, encoding, program, **kwargs) + cmdlist = get_archive_cmdlist(archive, encoding, program, *args, **kwargs) run_archive_cmdlist(cmdlist) if command == 'extract': target = cleanup_outdir(archive, outdir, config['force']) diff --git a/patoolib/arj.py b/patoolib/arj.py index c9dd1a4..b4c2510 100644 --- a/patoolib/arj.py +++ b/patoolib/arj.py @@ -26,6 +26,7 @@ def extract_arj (archive, encoding, cmd, **kwargs): cmdlist.extend([archive, kwargs['outdir']]) return cmdlist + def list_arj (archive, encoding, cmd, **kwargs): """List a ARJ archive.""" cmdlist = [cmd] @@ -39,6 +40,7 @@ def list_arj (archive, encoding, cmd, **kwargs): cmdlist.extend([archive]) return cmdlist + def test_arj (archive, encoding, cmd, **kwargs): """Test a ARJ archive.""" cmdlist = [cmd] @@ -49,3 +51,16 @@ def test_arj (archive, encoding, cmd, **kwargs): cmdlist.append('-y') cmdlist.extend([archive]) return cmdlist + + +def create_arj (archive, encoding, cmd, *args, **kwargs): + """Create a ARJ archive.""" + cmdlist = [cmd] + cmdlist.append('a') + cmdlist.append('-r') + cmdlist.append('-y') + if not kwargs['verbose']: + cmdlist.append('-i-') + cmdlist.append(archive) + cmdlist.extend(args) + return cmdlist diff --git a/patoolib/bzip2.py b/patoolib/bzip2.py index 8f3f3ba..e8caac6 100644 --- a/patoolib/bzip2.py +++ b/patoolib/bzip2.py @@ -33,9 +33,24 @@ def extract_bzip2 (archive, encoding, cmd, **kwargs): def test_bzip2 (archive, encoding, cmd, **kwargs): + """Test a BZIP2 archive.""" cmdlist = [cmd] if kwargs['verbose']: cmdlist.append('-v') cmdlist.extend(['-t', '--']) cmdlist.extend([archive]) return cmdlist + + +def create_bzip2 (archive, encoding, cmd, *args, **kwargs): + """Create a BZIP2 archive.""" + cmdlist = [cmd] + if kwargs['verbose']: + cmdlist.append('-v') + cmdlist.extend(['-c', '-z']) + cmdlist.append('--') + cmdlist.extend(args) + cmdlist.extend(['>', archive]) + # note that for shell calls the command must be a string + cmd = " ".join([util.shell_quote(x) for x in cmdlist]) + return (cmd, {'shell': True}) diff --git a/patoolib/compress.py b/patoolib/compress.py new file mode 100644 index 0000000..88dcca4 --- /dev/null +++ b/patoolib/compress.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2010 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Archive commands for the uncompress.real program.""" +from . import util + + +def create_compress (archive, encoding, cmd, *args, **kwargs): + """Create a compressed archive.""" + cmdlist = [cmd] + if kwargs['verbose']: + cmdlist.append('-v') + cmdlist.append('-c') + cmdlist.extend(args) + cmdlist.extend(['>', archive]) + # note that for shell calls the command must be a string + cmd = " ".join([util.shell_quote(x) for x in cmdlist]) + return (cmd, {'shell': True}) diff --git a/patoolib/cpio.py b/patoolib/cpio.py index 0a9aa11..513f9d4 100644 --- a/patoolib/cpio.py +++ b/patoolib/cpio.py @@ -40,3 +40,20 @@ def list_cpio (archive, encoding, cmd, **kwargs): cmdlist.append('-v') cmdlist.extend(['-F', archive]) return cmdlist + +test_cpio = list_cpio + +def create_cpio(archive, encoding, cmd, *args, **kwargs): + """Create a CPIO archive.""" + cmdlist = [cmd, '--create'] + if kwargs['verbose']: + cmdlist.append('-v') + if len(args) != 0: + findcmd = ['find', '-print0'] + findcmd.extend(args) + findcmd.append('|') + cmdlist[0:0] = findcmd + cmdlist.append('-0') + cmdlist.extend([">", archive]) + cmd = " ".join([util.shell_quote(x) for x in cmdlist]) + return (cmd, {'shell': True}) diff --git a/patoolib/gzip.py b/patoolib/gzip.py index 56b8650..9c8a010 100644 --- a/patoolib/gzip.py +++ b/patoolib/gzip.py @@ -55,3 +55,16 @@ def test_gzip (archive, encoding, cmd, **kwargs): return cmdlist test_compress = test_gzip + +def create_gzip (archive, encoding, cmd, *args, **kwargs): + """Create a GZIP archive.""" + cmdlist = [cmd] + if kwargs['verbose']: + cmdlist.append('-v') + cmdlist.append('-c') + cmdlist.append('--') + cmdlist.extend(args) + cmdlist.extend(['>', archive]) + # note that for shell calls the command must be a string + cmd = " ".join([util.shell_quote(x) for x in cmdlist]) + return (cmd, {'shell': True}) diff --git a/patoolib/lzop.py b/patoolib/lzop.py index 8cdf7d1..5d6fefe 100644 --- a/patoolib/lzop.py +++ b/patoolib/lzop.py @@ -41,3 +41,13 @@ def test_lzop (archive, encoding, cmd, **kwargs): cmdlist.append('--verbose') cmdlist.extend(['--', archive]) return cmdlist + +def create_lzop (archive, encoding, cmd, *args, **kwargs): + """Create a LZOP archive.""" + cmdlist = [cmd] + if kwargs['verbose']: + cmdlist.append('-v') + cmdlist.extend(['-o', archive]) + cmdlist.append('--') + cmdlist.extend(args) + return cmdlist diff --git a/patoolib/p7zip.py b/patoolib/p7zip.py index e69ed26..c03fd50 100644 --- a/patoolib/p7zip.py +++ b/patoolib/p7zip.py @@ -82,3 +82,15 @@ test_bzip2 = \ test_rpm = \ test_deb = \ test_7z + + +def create_7z (archive, encoding, cmd, *args, **kwargs): + """Create a 7z archive.""" + cmdlist = [cmd] + cmdlist.append('a') + if not kwargs['verbose']: + cmdlist.append('-bd') + cmdlist.append('--') + cmdlist.append(archive) + cmdlist.extend(args) + return cmdlist diff --git a/patoolib/pbzip2.py b/patoolib/pbzip2.py index a6b66cc..43cf57e 100644 --- a/patoolib/pbzip2.py +++ b/patoolib/pbzip2.py @@ -15,4 +15,4 @@ # along with this program. If not, see . """Archive commands for the pbzip2 program.""" # bzip2 and pbzip2 are compatible -from .bzip2 import extract_bzip2, test_bzip2 +from .bzip2 import extract_bzip2, test_bzip2, create_bzip2 diff --git a/patoolib/rar.py b/patoolib/rar.py index c69a057..f3507dc 100644 --- a/patoolib/rar.py +++ b/patoolib/rar.py @@ -43,3 +43,14 @@ def test_rar (archive, encoding, cmd, **kwargs): cmdlist.append('-c-') cmdlist.extend(['--', archive]) return cmdlist + +def create_rar (archive, encoding, cmd, *args, **kwargs): + """Create a RAR archive.""" + cmdlist = [cmd] + cmdlist.append('a') + if not kwargs['verbose']: + cmdlist.append('-c-') + cmdlist.extend(['-r', '--', archive]) + cmdlist.extend(args) + return cmdlist + diff --git a/patoolib/star.py b/patoolib/star.py index 1fd6095..e7ca5c9 100644 --- a/patoolib/star.py +++ b/patoolib/star.py @@ -18,6 +18,28 @@ def extract_tar (archive, encoding, cmd, **kwargs): """Extract a TAR archive.""" cmdlist = [cmd, '-x'] + add_star_opts(cmdlist, encoding, kwargs['verbose']) + cmdlist.extend(['-C', kwargs['outdir'], 'file=%s' % archive]) + return cmdlist + +def list_tar (archive, encoding, cmd, **kwargs): + """List a TAR archive.""" + cmdlist = [cmd, '-n'] + add_star_opts(cmdlist, encoding, kwargs['verbose']) + cmdlist.append("file=%s" % archive) + return cmdlist + +test_tar = list_tar + +def create_tar (archive, encoding, cmd, *args, **kwargs): + """Create a TAR archive.""" + cmdlist = [cmd, '-c'] + add_star_opts(cmdlist, encoding, kwargs['verbose']) + cmdlist.append("file=%s" % archive) + cmdlist.extend(args) + return cmdlist + +def add_star_opts (cmdlist, encoding, verbose): # Note that star autodetects encoding compression, but displays a warning # which we want to avoie. if encoding == 'gzip': @@ -26,23 +48,5 @@ def extract_tar (archive, encoding, cmd, **kwargs): cmdlist.append('-Z') elif encoding == 'bzip2': cmdlist.append('-bz') - if kwargs['verbose']: + if verbose: cmdlist.append('-v') - cmdlist.extend(['-C', kwargs['outdir'], 'file=%s' % archive]) - return cmdlist - -def list_tar (archive, encoding, cmd, **kwargs): - """List a TAR archive.""" - cmdlist = [cmd, '-n'] - if encoding == 'gzip': - cmdlist.append('-z') - elif encoding == 'compress': - cmdlist.append('-Z') - elif encoding == 'bzip2': - cmdlist.append('-bz') - if kwargs['verbose']: - cmdlist.append('-v') - cmdlist.append("file=%s" % archive) - return cmdlist - -test_tar = list_tar diff --git a/patoolib/tar.py b/patoolib/tar.py index ad360d6..8abccda 100644 --- a/patoolib/tar.py +++ b/patoolib/tar.py @@ -37,6 +37,11 @@ def list_tar (archive, encoding, cmd, **kwargs): test_tar = list_tar -def create_tar (archive, encoding, cmd, **kwargs): +def create_tar (archive, encoding, cmd, *args, **kwargs): """Create a TAR archive.""" - print "XXX create", archive, encoding, cmd, kwargs + cmdlist = [cmd, '--create'] + if encoding: + cmdlist.append('--%s' % encoding) + cmdlist.extend(["--file", archive, '--']) + cmdlist.extend(args) + return cmdlist diff --git a/patoolib/util.py b/patoolib/util.py index 01179ac..4bc090d 100644 --- a/patoolib/util.py +++ b/patoolib/util.py @@ -26,6 +26,7 @@ mimedb = mimetypes.MimeTypes(strict=False) mimedb.encodings_map['.bz2'] = 'bzip2' mimedb.suffix_map['.tbz2'] = '.tar.bz2' mimedb.add_type('application/x-lzop', '.lzo', strict=False) +mimedb.add_type('application/x-arj', '.arj', strict=False) class PatoolError (StandardError): diff --git a/tests/__init__.py b/tests/__init__.py index 986e14c..d953b2f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -26,10 +26,11 @@ datadir = os.path.join(basedir, 'data') class ArchiveTest (unittest.TestCase): """Helper class for achive tests.""" - def archive_commands (self, filename, cmd): + def archive_commands (self, filename, cmd, singlefile=False): self.archive_list(filename, cmd) self.archive_test(filename, cmd) self.archive_extract(filename, cmd) + self.archive_create(filename, cmd, singlefile=singlefile) def archive_extract (self, filename, cmd): archive = os.path.join(datadir, filename) @@ -53,6 +54,26 @@ class ArchiveTest (unittest.TestCase): patoolib._handle_archive(archive, 'test', program=cmd) patoolib._handle_archive(archive, 'test', program=cmd, verbose=True) + def archive_create (self, filename, cmd, singlefile=False): + # the file or directory to pack + if singlefile: + topack = os.path.join(datadir, 'foo.txt') + else: + topack = os.path.join(datadir, 'foo') + # create a temporary directory for creation + tmpdir = patoolib.util.tmpdir(dir=basedir) + archive = os.path.join(tmpdir, filename) + os.chdir(tmpdir) + try: + patoolib._handle_archive(archive, 'create', topack, program=cmd) + # not all programs can test what they create + if cmd == 'compress': + cmd = 'gzip' + patoolib._handle_archive(archive, 'test', program=cmd) + finally: + os.chdir(basedir) + shutil.rmtree(tmpdir) + def needs_cmd (cmd): """Decorator skipping test if given command is not available.""" diff --git a/tests/data/foo.txt b/tests/data/foo.txt new file mode 100644 index 0000000..d81cc07 --- /dev/null +++ b/tests/data/foo.txt @@ -0,0 +1 @@ +42 diff --git a/tests/data/foo/bar/t.txt b/tests/data/foo/bar/t.txt new file mode 100644 index 0000000..d81cc07 --- /dev/null +++ b/tests/data/foo/bar/t.txt @@ -0,0 +1 @@ +42 diff --git a/tests/data/foo/t.txt b/tests/data/foo/t.txt new file mode 100644 index 0000000..d81cc07 --- /dev/null +++ b/tests/data/foo/t.txt @@ -0,0 +1 @@ +42 diff --git a/tests/test_archives.py b/tests/test_archives.py index b0e96cf..c53b1e8 100644 --- a/tests/test_archives.py +++ b/tests/test_archives.py @@ -36,11 +36,13 @@ class TestArchives (ArchiveTest): def test_bzip2 (self): self.archive_extract('t.bz2', 'bzip2') self.archive_test('t.bz2', 'bzip2') + self.archive_create('t.bz2', 'bzip2', singlefile=True) @needs_cmd('pbzip2') def test_pbzip2 (self): self.archive_extract('t.bz2', 'pbzip2') self.archive_test('t.bz2', 'pbzip2') + self.archive_create('t.bz2', 'pbzip2', singlefile=True) @needs_cmd('echo') def test_echo (self): @@ -49,19 +51,27 @@ class TestArchives (ArchiveTest): @needs_cmd('unzip') def test_unzip (self): - self.archive_commands('t.zip', 'unzip') - self.archive_commands('t.jar', 'unzip') + self.archive_extract('t.zip', 'unzip') + self.archive_list('t.zip', 'unzip') + self.archive_test('t.zip', 'unzip') + self.archive_extract('t.jar', 'unzip') + self.archive_list('t.jar', 'unzip') + self.archive_test('t.jar', 'unzip') @needs_cmd('gzip') def test_gzip (self): - self.archive_commands('t.gz', 'gzip') - self.archive_commands('t.txt.gz', 'gzip') + self.archive_commands('t.gz', 'gzip', singlefile=True) + self.archive_commands('t.txt.gz', 'gzip', singlefile=True) self.archive_extract('t.Z', 'gzip') @needs_cmd('uncompress.real') def test_uncompress (self): self.archive_extract('t.Z', 'uncompress.real') + @needs_cmd('compress') + def test_compress (self): + self.archive_create('t.Z', 'compress', singlefile=True) + @needs_cmd('7z') def test_p7zip (self): self.archive_commands('t.7z', '7z') @@ -109,20 +119,19 @@ class TestArchives (ArchiveTest): self.archive_commands('t.rar', 'rar') @needs_cmd('cabextract') - def test_capextract (self): + def test_cabextract (self): self.archive_list('t.cab', 'cabextract') self.archive_extract('t.cab', 'cabextract') @needs_cmd('arj') def test_arj (self): - self.archive_list('t.arj', 'arj') - self.archive_test('t.arj', 'arj') - self.archive_extract('t.arj', 'arj') + self.archive_commands('t.arj', 'arj') @needs_cmd('cpio') def test_cpio (self): self.archive_list('t.cpio', 'cpio') self.archive_extract('t.cpio', 'cpio') + self.archive_create('t.cpio', 'cpio') @needs_cmd('rpm') def test_rpm (self): @@ -143,5 +152,5 @@ class TestArchives (ArchiveTest): @needs_cmd('lzop') def test_lzop (self): - self.archive_commands('t.lzo', 'lzop') + self.archive_commands('t.lzo', 'lzop', singlefile=True)