diff --git a/doc/changelog.txt b/doc/changelog.txt index 37f1a4b..b076e0f 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -3,6 +3,8 @@ * Fix path error by using absolute pathname for archive when changing the current working directory to the unpack directory. * Fix parsing of the "-v" short option. + * Added new command "patool diff" to show differences between two + archives. 0.7 "3000 Miles to Graceland" (released 8.3.2010) diff --git a/doc/patool.1 b/doc/patool.1 index db98a9e..8383a6e 100644 --- a/doc/patool.1 +++ b/doc/patool.1 @@ -17,6 +17,7 @@ patool - simple manager for file archives of various types .SH SYNOPSIS \fBpatool\fP (\fBextract\fP|\fBlist\fP|\fBtest\fP) [\fIoptions\fP] <\fIarchive-file\fP>... \fBpatool\fP \fBcreate\fP [\fIoptions\fP] <\fIarchive-file\fP> [\fIfiles\fP...] + \fBpatool\fP \fBdiff\fP [\fIoptions\fP] <\fIarchive1\fP> <\fIarchive2\fP> \fBpatool\fP \fBformats\fP [\fIoptions\fP] .SH DESCRIPTION Various archive types can be created, extracted, tested and listed by @@ -38,6 +39,7 @@ It relies on helper applications to handle those archive formats \fBpatool test --verbose dist.tar.gz\fP \fBpatool list package.deb\fP \fPpatool create --verbose myfiles.zip file1.txt dir/\fP + \fBpatool diff release1.0.tar.gz release2.0.zip\fP .SH COMMANDS Several commands and options are available. .SS \fBextract\fP @@ -85,6 +87,11 @@ Verbose archive testing (if the helper application supports it). .TP \fB\-\-help\fP Show help for this command. +.SS \fBdiff\fP +Show differences between two archives. +.TP +\fB\-\-help\fP +Show help for this command. .SS \fBformats\fP Show all supported archive formats. .TP diff --git a/doc/patool.txt b/doc/patool.txt index 4964e1c..17cedaf 100644 --- a/doc/patool.txt +++ b/doc/patool.txt @@ -8,6 +8,7 @@ NAME SYNOPSIS patool (extract|list|test) [options] ... patool create [options] [files...] + patool diff [options] patool formats [options] DESCRIPTION @@ -31,6 +32,7 @@ EXAMPLES patool test --verbose dist.tar.gz patool list package.deb patool create --verbose myfiles.zip file1.txt dir/ + patool diff release1.0.tar.gz release2.0.zip COMMANDS Several commands and options are available. @@ -78,6 +80,11 @@ COMMANDS --help Show help for this command. + diff + Show differences between two archives. + + --help Show help for this command. + formats Show all supported archive formats. diff --git a/patool b/patool index d7a5c85..6d03370 100755 --- a/patool +++ b/patool @@ -70,6 +70,10 @@ def create (archive, file1, *files, **kwargs): allfiles = (file1,)+files return handle_archive(archive, 'create', *allfiles, **kwargs) +@baker.command +def diff (archive1, archive2): + """Show differences between two archives.""" + return handle_archive(archive1, "diff", archive2) @baker.command def formats (): diff --git a/patool.bash-completion b/patool.bash-completion index 3cf781c..57e45a7 100644 --- a/patool.bash-completion +++ b/patool.bash-completion @@ -9,7 +9,7 @@ _patool() COMPREPLY=() cur=`_get_cword` - commands='extract list test create formats' + commands='extract list test create diff formats' if [[ $COMP_CWORD -eq 1 ]] ; then if [[ "$cur" == -* ]]; then diff --git a/patoolib/__init__.py b/patoolib/__init__.py index e9ccbab..39a4255 100644 --- a/patoolib/__init__.py +++ b/patoolib/__init__.py @@ -160,7 +160,6 @@ ArchivePrograms = { 'create': ('cpio',), }, 'rpm': { - # XXX rpm2cpio depends on cpio whose availability is not checked 'extract': ('rpm2cpio', '7z'), 'list': ('rpm', '7z'), 'test': ('rpm', '7z'), @@ -367,19 +366,17 @@ def make_user_readable (directory): make_dir_readable(os.path.join(root, dirname)) -def cleanup_outdir (archive, outdir): - """Cleanup outdir after extraction and return target file name.""" +def cleanup_outdir (outdir): + """Cleanup outdir after extraction and return target file name and + result string.""" make_user_readable(outdir) - if outdir: - # move single directory or file in outdir - (res, msg) = move_outdir_orphan(outdir) - if res: - target = "`%s'" % msg - else: - target = "`%s' (%s)" % (outdir, msg) - else: - target = "`%s'" % util.stripext(archive) - return target + # move single directory or file in outdir + (success, msg) = move_outdir_orphan(outdir) + if success: + # msg is a single directory or filename + return msg, "`%s'" % msg + # outdir remains unchanged + return outdir, "`%s' (%s)" % (outdir, msg) def _handle_archive (archive, command, *args, **kwargs): @@ -404,30 +401,37 @@ def _handle_archive (archive, command, *args, **kwargs): # import archive handler (eg. patoolib.programs.star.extract_tar()) exec "from patoolib.programs.%s import %s_%s as func" % (module, command, format) get_archive_cmdlist = locals()['func'] - # prepare func() call arguments - kwargs = dict(verbose=config['verbose']) - outdir = None + # prepare keyword arguments for command list + cmd_kwargs = dict(verbose=config['verbose']) origarchive = None if command == 'extract': - outdir = util.tmpdir(dir=os.getcwd()) - kwargs['outdir'] = outdir + if "outdir" in kwargs: + cmd_kwargs["outdir"] = kwargs["outdir"] + do_cleanup_outdir = False + else: + cmd_kwargs['outdir'] = util.tmpdir(dir=os.getcwd()) + do_cleanup_outdir = True elif command == 'create' and os.path.basename(program) == 'arc' and \ ".arc" in archive and not archive.endswith(".arc"): # the arc program mangles the archive name if it contains ".arc" origarchive = archive archive = util.tmpfile(dir=os.path.dirname(archive), suffix=".arc") try: - cmdlist = get_archive_cmdlist(archive, encoding, program, *args, **kwargs) + cmdlist = get_archive_cmdlist(archive, encoding, program, *args, **cmd_kwargs) run_archive_cmdlist(cmdlist) if command == 'extract': - target = cleanup_outdir(archive, outdir) - print "%s: extracted to %s" % (archive, target) + if do_cleanup_outdir: + target, msg = cleanup_outdir(cmd_kwargs["outdir"]) + print "%s: extracted to %s" % (archive, msg) + else: + target, msg = cmd_kwargs["outdir"], "`%s'" % cmd_kwargs["outdir"] + return target elif command == 'create' and origarchive: shutil.move(archive, origarchive) finally: - if outdir: + if command == "extract": try: - os.rmdir(outdir) + os.rmdir(cmd_kwargs["outdir"]) except OSError: pass @@ -435,7 +439,10 @@ def _handle_archive (archive, command, *args, **kwargs): def handle_archive (archive, command, *args, **kwargs): """Handle archive file command; with nice error reporting.""" try: - _handle_archive(archive, command, *args, **kwargs) + if command == "diff": + _diff_archives(archive, args[0]) + else: + _handle_archive(archive, command, *args, **kwargs) res = 0 except util.PatoolError, msg: util.log_error(msg) @@ -444,3 +451,18 @@ def handle_archive (archive, command, *args, **kwargs): util.log_internal_error() res = 1 return res + + +def _diff_archives (archive1, archive2): + diff = util.find_program("diff") + if not diff: + raise util.PatoolError("diff(1) is required for showing archive differences, please install it") + tmpdir1 = util.tmpdir(dir=os.getcwd()) + tmpdir2 = util.tmpdir(dir=os.getcwd()) + try: + dir1 = _handle_archive(archive1, 'extract', outdir=tmpdir1) + dir2 = _handle_archive(archive2, 'extract', outdir=tmpdir2) + util.run([diff, "-BurN", dir1, dir2]) + finally: + shutil.rmtree(tmpdir1, onerror=util.log_error) + shutil.rmtree(tmpdir2, onerror=util.log_error)