# HG changeset patch # User brett # Date 1167611243 18000 # Node ID 29794d4d41aa3e1712441903672283833b46fae1 # Parent 28dbd52a8bb84feb88acb4e406eb5eb03432aa6f [svn] There's now an entirely new object hierarchy for handlers, because the number of corner cases, switches, etc. was starting to get out of hand. This makes it easier to tell what's going on in a given case. diff -r 28dbd52a8bb8 -r 29794d4d41aa scripts/x --- a/scripts/x Sun Dec 31 19:25:16 2006 -0500 +++ b/scripts/x Sun Dec 31 19:27:23 2006 -0500 @@ -47,7 +47,7 @@ # ONE_DIRECTORY = 2 BOMB = 3 EMPTY = 4 -DECOMPRESSED = 5 +COMPRESSED = 5 mimetypes.encodings_map.setdefault('.bz2', 'bzip2') mimetypes.types_map['.exe'] = 'application/x-msdos-program' @@ -308,7 +308,7 @@ yield self.basename() def check_contents(self): - return DECOMPRESSED + return COMPRESSED def extract(self, path): output = open(path, 'w') @@ -323,25 +323,27 @@ self.contents = contents self.options = options +## def extract(self): +## raise NotImplementedError +## checker = self.extractor.name_checker(self.extractor.basename()) +## if self.options.flat: +## self.target = '.' +## return self.do_extract('.') +## elif self.options.overwrite or checker.is_free(): +## self.target = self.extractor.basename() +## return self.overwrite() +## else: +## self.target = checker.check() +## return self.safe_extract() + def extract(self): - checker = self.extractor.name_checker(self.extractor.basename()) - if self.options.flat: - self.target = '.' - return self.do_extract('.') - elif self.options.overwrite or checker.is_free(): - self.target = self.extractor.basename() - return self.overwrite() - else: - self.target = checker.check() - return self.safe_extract() - - def do_extract(self, directory): try: - self.extractor.extract(directory) + self.extractor.extract(self.target) except ExtractorError, error: return str(error) def cleanup(self): + raise NotImplementedError if self.options.flat: self.cleanup_files() else: @@ -366,35 +368,90 @@ return "%s returned with exit status %s" % (command, status) -class MatchHandler(BaseHandler): - def overwrite(self): - return self.do_extract('.') +# The "where to extract" table, with options and archive types. +# This dictates the contents of each can_handle method. +# +# Flat Overwrite None +# File basename basename FilenameChecked +# Match . . tempdir + checked +# Bomb . basename DirectoryChecked + + +class FlatHandler(BaseHandler): + def can_handle(contents, options): + return ((options.flat and (contents != COMPRESSED)) or + (options.overwrite and (contents == MATCHING_DIRECTORY))) + can_handle = staticmethod(can_handle) + + target = '.' + cleanup = BaseHandler.cleanup_files + - def safe_extract(self): - tempdir = tempfile.mkdtemp() - result = self.do_extract(tempdir) +class OverwriteHandler(BaseHandler): + def can_handle(contents, options): + return ((options.flat and (contents == COMPRESSED)) or + (options.overwrite and (contents != MATCHING_DIRECTORY))) + can_handle = staticmethod(can_handle) + + def __init__(self, extractor, contents, options): + BaseHandler.__init__(self, extractor, contents, options) + self.target = self.extractor.basename() + + cleanup = BaseHandler.cleanup_directory + + +class MatchHandler(BaseHandler): + def can_handle(contents, options): + return contents == MATCHING_DIRECTORY + can_handle = staticmethod(can_handle) + + def extract(self): + basename = self.extractor.basename() + self.target = tempfile.mkdtemp() + result = BaseHandler.extract(self) if result is None: - os.rename(os.path.join(tempdir, self.extractor.basename()), - self.target) + tempdir = self.target + checker = self.extractor.name_checker(basename) + self.target = checker.check() + os.rename(os.path.join(tempdir, basename), self.target) os.rmdir(tempdir) return result - - -class BombHandler(BaseHandler): - def safe_extract(self): - return self.do_extract(self.target) - def overwrite(self): - self.target = self.extractor.basename() - return self.do_extract(self.target) + cleanup = BaseHandler.cleanup_directory class EmptyHandler(object): + def can_handle(contents, options): + return contents == EMPTY + can_handle = staticmethod(can_handle) + def __init__(self, extractor, contents, options): pass def extract(self): pass def cleanup(self): pass +class BombHandler(BaseHandler): + def can_handle(contents, options): + return True + can_handle = staticmethod(can_handle) + + def __init__(self, extractor, contents, options): + BaseHandler.__init__(self, extractor, contents, options) + checker = self.extractor.name_checker(self.extractor.basename()) + self.target = checker.check() + + cleanup = BaseHandler.cleanup_directory + + +## class BombHandler(BaseHandler): +## def safe_extract(self): +## return self.do_extract(self.target) + +## def overwrite(self): +## self.target = self.extractor.basename() +## return self.do_extract(self.target) + + extractor_map = {'application/x-tar': TarExtractor, 'application/zip': ZipExtractor, 'application/x-msdos-program': ZipExtractor, @@ -403,9 +460,8 @@ 'application/x-rpm': RPMExtractor, 'application/x-cpio': CpioExtractor} -handler_map = {EMPTY: EmptyHandler, - MATCHING_DIRECTORY: MatchHandler, - DECOMPRESSED: BombHandler} +handlers = [FlatHandler, OverwriteHandler, MatchHandler, EmptyHandler, + BombHandler] class ExtractorApplication(object): def __init__(self, arguments): @@ -459,15 +515,18 @@ except KeyError: if encoding: extractor = CompressionExtractor + contents = COMPRESSED else: return "not a known archive type" try: self.current_extractor = extractor(self.current_filename, mimetype, encoding) content = self.current_extractor.check_contents() - handler = handler_map.get(content, BombHandler) - self.current_handler = handler(self.current_extractor, content, - self.options) + for handler in handlers: + if handler.can_handle(content, self.options): + self.current_handler = handler(self.current_extractor, + content, self.options) + break except ExtractorError, error: return str(error) diff -r 28dbd52a8bb8 -r 29794d4d41aa tests/tests.yml --- a/tests/tests.yml Sun Dec 31 19:25:16 2006 -0500 +++ b/tests/tests.yml Sun Dec 31 19:27:23 2006 -0500 @@ -61,6 +61,13 @@ baseline: | zcat $1 >test-text +- name: decompression with -fr + directory: inside-dir + filename: ../test-text.gz + options: -fr + baseline: | + zcat $1 >test-text + - name: overwrite protection filename: test-1.23.tar.bz2 baseline: |