Sat, 10 Feb 2007 20:43:00 -0500
[svn] If the archive contains one directory with the "wrong" name, ask the user
what they want to do to extract it.
Also add a switch to prevent being asked this question, for use in scripts
and the like.
scripts/dtrx | file | annotate | diff | comparison | revisions | |
tests/compare.py | file | annotate | diff | comparison | revisions | |
tests/test-onedir.tar.gz | file | annotate | diff | comparison | revisions | |
tests/tests.yml | file | annotate | diff | comparison | revisions |
--- a/scripts/dtrx Sat Feb 10 16:43:44 2007 -0500 +++ b/scripts/dtrx Sat Feb 10 20:43:00 2007 -0500 @@ -26,6 +26,7 @@ import subprocess import sys import tempfile +import textwrap from cStringIO import StringIO @@ -49,6 +50,10 @@ EMPTY = 4 COMPRESSED = 5 +EXTRACT_HERE = 1 +EXTRACT_WRAP = 2 +EXTRACT_RENAME = 3 + mimetypes.encodings_map.setdefault('.bz2', 'bzip2') mimetypes.types_map['.exe'] = 'application/x-msdos-program' @@ -378,16 +383,23 @@ class MatchHandler(BaseHandler): def can_handle(contents, options): - return contents == MATCHING_DIRECTORY + return ((contents == MATCHING_DIRECTORY) or + (hasattr(contents, 'encode') and + (options.onedir_policy in (EXTRACT_RENAME, EXTRACT_HERE)))) can_handle = staticmethod(can_handle) def extract(self): - basename = self.extractor.basename() - self.target = tempfile.mkdtemp(dir='.') + if self.contents == MATCHING_DIRECTORY: + basename = destination = self.extractor.basename() + elif self.options.onedir_policy == EXTRACT_HERE: + basename = destination = self.contents.rstrip('/') + else: + basename = self.contents.rstrip('/') + destination = self.extractor.basename() + self.target = tempdir = tempfile.mkdtemp(dir='.') result = BaseHandler.extract(self) if result is None: - tempdir = self.target - checker = self.extractor.name_checker(basename) + checker = self.extractor.name_checker(destination) self.target = checker.check() os.rename(os.path.join(tempdir, basename), self.target) os.rmdir(tempdir) @@ -427,6 +439,9 @@ BombHandler] class ExtractorApplication(object): + policy_answers = {'h': EXTRACT_HERE, 'i': EXTRACT_WRAP, + 'r': EXTRACT_RENAME, '': EXTRACT_WRAP} + def __init__(self, arguments): self.parse_options(arguments) self.setup_logger() @@ -457,10 +472,11 @@ parser.add_option('-l', '-t', '--list', '--table', dest='show_list', action='store_true', default=False, help="list contents of archives on standard output") -## parser.add_option('-n', '--noninteractive', dest='batch', -## action='store_true', default=False, -## help="don't ask how to handle special cases") + parser.add_option('-n', '--noninteractive', dest='batch', + action='store_true', default=False, + help="don't ask how to handle special cases") self.options, filenames = parser.parse_args(arguments) + self.options.onedir_policy = self.policy_answers[''] if not filenames: parser.error("you did not list any archives") self.archives = {os.path.realpath(os.curdir): filenames} @@ -474,6 +490,18 @@ handler.setFormatter(formatter) self.logger.addHandler(handler) + def ask_question(self, question, answers): + if self.options.batch: + return answers[''] + last_line = question.pop() + while True: + print "\n".join(question) + answer = raw_input(last_line) + try: + return answers[answer.lower()] + except KeyError: + print + def get_extractor(self): mimetype, encoding = mimetypes.guess_type(self.current_filename) try: @@ -481,13 +509,28 @@ 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) + except ExtractorError, error: + return str(error) + + def get_handler(self): + try: content = self.current_extractor.check_contents() + if hasattr(content, 'encode'): # Archive contains one directory. + question = textwrap.wrap("%s contains one directory: %s. %s" % + (self.current_filename, content, + "You can:")) + question.extend(["You can:", + " * extract it Inside another directory", + " * extract it and Rename the directory", + " * extract it Here", + "What do you want to do? (I/r/h) "]) + self.options.onedir_policy = \ + self.ask_question(question, self.policy_answers) for handler in handlers: if handler.can_handle(content, self.options): self.current_handler = handler(self.current_extractor, @@ -527,7 +570,8 @@ for filename in filenames: os.chdir(self.current_directory) self.current_filename = filename - success = self.report(self.get_extractor) + success = (self.report(self.get_extractor) and + self.report(self.get_handler)) if success: for name in 'extract', 'cleanup': success = (self.report(getattr(self.current_handler,
--- a/tests/compare.py Sat Feb 10 16:43:44 2007 -0500 +++ b/tests/compare.py Sat Feb 10 20:43:00 2007 -0500 @@ -42,6 +42,7 @@ set -e """ +input_buffer = tempfile.TemporaryFile() output_buffer = tempfile.TemporaryFile() class ExtractorTestError(Exception): @@ -50,19 +51,18 @@ class ExtractorTest(object): def __init__(self, **kwargs): - for key in ('name',): - setattr(self, key, kwargs[key]) + setattr(self, 'name', kwargs['name']) + setattr(self, 'options', kwargs.get('options', '-n').split()) + setattr(self, 'filenames', kwargs.get('filenames', '').split()) for key in ('directory', 'prerun', 'posttest', 'baseline', 'error', - 'grep', 'antigrep', 'output'): + 'grep', 'antigrep', 'input', 'output'): setattr(self, key, kwargs.get(key, None)) - for key in ('options', 'filenames'): - setattr(self, key, kwargs.get(key, '').split()) - def get_results(self, commands): + def get_results(self, commands, stdin=None): print >>output_buffer, "Output from %s:" % (' '.join(commands),) output_buffer.flush() status = subprocess.call(commands, stdout=output_buffer, - stderr=output_buffer) + stderr=output_buffer, stdin=stdin) process = subprocess.Popen(['find', '!', '-name', TESTSCRIPT_NAME], stdout=subprocess.PIPE) process.wait() @@ -84,7 +84,16 @@ if self.prerun: self.write_script(self.prerun) subprocess.call(['sh', TESTSCRIPT_NAME]) - return self.get_results([X_SCRIPT] + self.options + self.filenames) + input_buffer.seek(0, 0) + input_buffer.truncate() + if self.input: + input_buffer.write(self.input) + if not self.input.endswith('\n'): + input_buffer.write('\n') + input_buffer.seek(0, 0) + input_buffer.flush() + return self.get_results([X_SCRIPT] + self.options + self.filenames, + input_buffer) def get_posttest_result(self): if not self.posttest: @@ -139,9 +148,9 @@ def have_error_mismatch(self, status): if self.error and (status == 0): - return "x did not return expected error" + return "dtrx did not return expected error" elif (not self.error) and (status != 0): - return "x returned error code %s" % (status,) + return "dtrx returned error code %s" % (status,) return None def grep_output(self, output): @@ -210,4 +219,5 @@ for result in results: counts[result] += 1 print " Totals:", ', '.join(["%s %s" % (counts[key], key) for key in OUTCOMES]) +input_buffer.close() output_buffer.close()
--- a/tests/tests.yml Sat Feb 10 16:43:44 2007 -0500 +++ b/tests/tests.yml Sat Feb 10 20:43:00 2007 -0500 @@ -35,7 +35,7 @@ - name: recursion and permissions filenames: test-recursive-badperms.tar.bz2 - options: -r + options: -n -r baseline: | mkdir test-recursive-badperms cd test-recursive-badperms @@ -57,14 +57,14 @@ - name: decompression with -r directory: inside-dir filenames: ../test-text.gz - options: -r + options: -n -r baseline: | zcat $1 >test-text - name: decompression with -fr directory: inside-dir filenames: ../test-text.gz - options: -fr + options: -n -fr baseline: | zcat $1 >test-text @@ -79,7 +79,7 @@ - name: overwrite option filenames: test-1.23.tar.bz2 - options: -o + options: -n -o baseline: | mkdir test-1.23 cd test-1.23 @@ -90,14 +90,14 @@ - name: flat option directory: inside-dir filenames: ../test-1.23.tar.bz2 - options: -f + options: -n -f baseline: | tar -jxf $1 - name: flat recursion and permissions directory: inside-dir filenames: ../test-recursive-badperms.tar.bz2 - options: -fr + options: -n -fr baseline: | tar -jxf $1 tar -xf test-badperms.tar @@ -118,12 +118,12 @@ filenames: tests.yml - name: bad options - options: --nonexistent-option + options: -n --nonexistent-option filenames: test-1.23.tar error: true - name: --version - options: --version + options: -n --version grep: ersion \d+\.\d+ filenames: test-1.23.tar baseline: | @@ -137,7 +137,7 @@ - name: silence filenames: tests.yml - options: -qq + options: -n -qq error: true antigrep: . @@ -151,7 +151,7 @@ chmod 500 . - name: list contents of one file - options: -l + options: -n -l filenames: test-1.23.tar output: | test-1.23/ @@ -163,7 +163,7 @@ test-1.23/foobar - name: list contents of multiple files - options: --table + options: -n --table filenames: test-1.23_all.deb test-1.23.zip output: | test-1.23_all.deb: @@ -180,6 +180,38 @@ foobar - name: list contents of compressed file - options: -t + options: -n -t filenames: test-text.gz output: test-text + +- name: default behavior with one directory + options: -n + filenames: test-onedir.tar.gz + baseline: | + mkdir test-onedir + cd test-onedir + tar -zxf ../$1 + +- name: one directory extracted inside another + options: "" + filenames: test-onedir.tar.gz + input: i + baseline: | + mkdir test-onedir + cd test-onedir + tar -zxf ../$1 + +- name: one directory extracted with rename + options: "" + filenames: test-onedir.tar.gz + input: r + baseline: | + tar -zxf $1 + mv test test-onedir + +- name: one directory extracted here + options: "" + filenames: test-onedir.tar.gz + input: h + baseline: | + tar -zxf $1