scripts/dtrx

branch
trunk
changeset 45
37d555407334
parent 43
4591a32eedc8
child 46
652871d804ab
--- a/scripts/dtrx	Tue Nov 27 22:43:40 2007 -0500
+++ b/scripts/dtrx	Thu Jan 17 22:36:07 2008 -0500
@@ -22,6 +22,7 @@
 import optparse
 import os
 import re
+import shutil
 import stat
 import subprocess
 import sys
@@ -31,9 +32,9 @@
 
 from sets import Set
 
-VERSION = "5.0"
+VERSION = "6.0"
 VERSION_BANNER = """dtrx version %s
-Copyright (c) 2006, 2007 Brett Smith <brettcsmith@brettcsmith.org>
+Copyright (c) 2006, 2007, 2008 Brett Smith <brettcsmith@brettcsmith.org>
 
 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
@@ -121,6 +122,12 @@
     pass
 
 
+class ExtractorUnusable(Exception):
+    pass
+
+
+EXTRACTION_ERRORS = (ExtractorError, ExtractorUnusable, OSError, IOError)
+
 class BaseExtractor(object):
     decoders = {'bzip2': 'bzcat', 'gzip': 'zcat', 'compress': 'zcat',
                 'lzma': 'lzcat'}
@@ -167,9 +174,14 @@
                 stdout = final_stdout
             else:
                 stdout = subprocess.PIPE
-            processes.append(subprocess.Popen(command, stdin=stdin,
-                                              stdout=stdout,
-                                              stderr=subprocess.PIPE))
+            try:
+                processes.append(subprocess.Popen(command, stdin=stdin,
+                                                  stdout=stdout,
+                                                  stderr=subprocess.PIPE))
+            except OSError, error:
+                if error.errno == errno.ENOENT:
+                    raise ExtractorUnusable("could not run %s" % (command[0],))
+                raise
         exit_codes = [pipe.wait() for pipe in processes]
         self.archive.close()
         for index in range(last_pipe):
@@ -230,9 +242,9 @@
             self.archive.seek(0, 0)
             self.extract_archive()
             self.check_contents()
-        except ExtractorError:
+        except EXTRACTION_ERRORS:
             os.chdir(old_path)
-            subprocess.call(['rm', '-rf', self.target])
+            shutil.rmtree(self.target, ignore_errors=True)
             raise
         os.chdir(old_path)
 
@@ -248,6 +260,7 @@
     
 
 class CompressionExtractor(BaseExtractor):
+    file_type = 'compressed file'
     name_checker = FilenameChecker
 
     def basename(self):
@@ -269,7 +282,7 @@
             raise ExtractorError("cannot extract here: %s" % (error.strerror,))
         try:
             self.run_pipes(output_fd)
-        except ExtractorError:
+        except EXTRACTION_ERRORS:
             os.close(output_fd)
             os.unlink(self.target)
             raise
@@ -277,6 +290,8 @@
         
 
 class TarExtractor(BaseExtractor):
+    file_type = 'tar file'
+
     def get_filenames(self):
         self.pipe(['tar', '-t'], "listing")
         return BaseExtractor.get_filenames(self)
@@ -287,6 +302,8 @@
         
         
 class CpioExtractor(BaseExtractor):
+    file_type = 'cpio file'
+
     def get_filenames(self):
         self.pipe(['cpio', '-t'], "listing")
         return BaseExtractor.get_filenames(self)
@@ -298,6 +315,8 @@
 
 
 class RPMExtractor(CpioExtractor):
+    file_type = 'RPM'
+
     def prepare(self):
         self.pipe(['rpm2cpio', '-'], "rpm2cpio")
 
@@ -320,6 +339,8 @@
 
 
 class DebExtractor(TarExtractor):
+    file_type = 'Debian package'
+
     def prepare(self):
         self.pipe(['ar', 'p', self.filename, 'data.tar.gz'],
                   "data.tar.gz extraction")
@@ -347,6 +368,8 @@
 
 
 class GemExtractor(TarExtractor):
+    file_type = 'Ruby gem'
+
     def prepare(self):
         self.pipe(['tar', '-xO', 'data.tar.gz'], "data.tar.gz extraction")
         self.pipe(['zcat'], "data.tar.gz decompression")
@@ -357,6 +380,8 @@
 
 
 class GemMetadataExtractor(CompressionExtractor):
+    file_type = 'Ruby gem'
+
     def prepare(self):
         self.pipe(['tar', '-xO', 'metadata.gz'], "metadata.gz extraction")
         self.pipe(['zcat'], "metadata.gz decompression")
@@ -383,6 +408,8 @@
 
 
 class ZipExtractor(NoPipeExtractor):
+    file_type = 'Zip file'
+
     def get_filenames(self):
         self.pipe(['zipinfo', '-1', self.filename], "listing")
         return BaseExtractor.get_filenames(self)
@@ -393,6 +420,7 @@
 
 
 class SevenExtractor(NoPipeExtractor):
+    file_type = '7z file'
     border_re = re.compile('^[- ]+$')
 
     def get_filenames(self):
@@ -416,6 +444,7 @@
         
 
 class CABExtractor(NoPipeExtractor):
+    file_type = 'CAB archive'
     border_re = re.compile(r'^[-\+]+$')
 
     def get_filenames(self):
@@ -780,7 +809,7 @@
     def report(self, function, *args):
         try:
             error = function(*args)
-        except (ExtractorError, IOError, OSError), exception:
+        except EXTRACTION_ERRORS, exception:
             error = str(exception)
             logger.debug(''.join(traceback.format_exception(*sys.exc_info())))
         return error
@@ -813,6 +842,7 @@
 class ListAction(BaseAction):
     def __init__(self, options, filenames):
         BaseAction.__init__(self, options, filenames)
+        self.count = 0
 
     def get_list(self, extractor):
         # Note: The reason I'm getting all the filenames up front is
@@ -823,14 +853,14 @@
         self.filelist = list(extractor.get_filenames())
 
     def show_list(self, filename):
+        self.count += 1
         if len(self.filenames) != 1:
-            if filename != self.filenames[0]:
+            if self.count > 1:
                 print
             print "%s:" % (filename,)
         print '\n'.join(self.filelist)
 
     def run(self, filename, extractor):
-        self.current_filename = filename
         return (self.report(self.get_list, extractor) or
                 self.report(self.show_list, filename))
 
@@ -909,21 +939,24 @@
             return "cannot extract a directory"
 
     def try_extractors(self, filename, builder):
-        last_error = "could not find a way to extract this"
-        while True:
-            try:
-                extractor = builder.next()
-            except StopIteration:
-                return last_error
-            except (IOError, OSError, ExtractorError), error:
-                return str(error)
+        errors = []
+        for extractor in builder:
             error = self.action.run(filename, extractor)
             if error:
-                logger.info("%s: %s" % (filename, error))
-                last_error = error
+                errors.append((extractor.file_type, extractor.encoding, error))
             else:
                 self.recurse(filename, extractor, self.action)
                 return
+        logger.error("could not handle %s" % (filename,))
+        if not errors:
+            logger.error("not a known archive type")
+            return True
+        for file_type, encoding, error in errors:
+            message = ["treating as", file_type, "failed:", error]
+            if encoding:
+                message.insert(1, "%s-encoded" % (encoding,))
+            logger.error(' '.join(message))
+        return True
         
     def run(self):
         if self.options.show_list:
@@ -939,7 +972,8 @@
                 error = (self.check_file(filename) or
                          self.try_extractors(filename, builder.get_extractor()))
                 if error:
-                    logger.error("%s: %s" % (filename, error))
+                    if error != True:
+                        logger.error("%s: %s" % (filename, error))
                     self.failures.append(filename)
                 else:
                     self.successes.append(filename)

mercurial