[svn] If the archive contains one directory with the "wrong" name, ask the user trunk

Sat, 10 Feb 2007 20:43:00 -0500

author
brett
date
Sat, 10 Feb 2007 20:43:00 -0500
branch
trunk
changeset 20
69c93c3e6972
parent 19
bb6e9f4af1a5
child 21
fd3e10410040

[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()
Binary file tests/test-onedir.tar.gz has changed
--- 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

mercurial