[svn] If an archive contains other archives, and the user didn't specify that trunk

Sat, 28 Apr 2007 23:52:36 -0400

author
brett
date
Sat, 28 Apr 2007 23:52:36 -0400
branch
trunk
changeset 23
039dd321a7d0
parent 22
b240777ae53e
child 24
60056f3e3e60

[svn] If an archive contains other archives, and the user didn't specify that
they wanted recursion, prompt them to find out what they want to do.

This required some changes in the way prompting for one-item archives works
too, because if an archive inside another archive is itself a one-item
archive, things get really weird. I'm still not really sure what the right
policy is for that.

I'm not wild about this code. It feels like programming a bunch of special
cases. I need to figure out a better way to abstract it. I'm thinking
some kind of Policy class....

scripts/dtrx file | annotate | diff | comparison | revisions
tests/tests.yml file | annotate | diff | comparison | revisions
--- a/scripts/dtrx	Sat Apr 21 13:09:58 2007 -0400
+++ b/scripts/dtrx	Sat Apr 28 23:52:36 2007 -0400
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 #
 # dtrx -- Intelligently extract various archive types.
-# Copyright (c) 2006 Brett Smith <brettcsmith@brettcsmith.org>.
+# Copyright (c) 2006, 2007 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
@@ -32,7 +32,7 @@
 
 VERSION = "4.0"
 VERSION_BANNER = """dtrx version %s
-Copyright (c) 2006 Brett Smith <brettcsmith@brettcsmith.org>
+Copyright (c) 2006, 2007 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
@@ -54,6 +54,11 @@
 EXTRACT_WRAP = 2
 EXTRACT_RENAME = 3
 
+RECURSE_ALWAYS = 1
+RECURSE_ONCE = 2
+RECURSE_NOT_NOW = 3
+RECURSE_NEVER = 4
+
 mimetypes.encodings_map.setdefault('.bz2', 'bzip2')
 mimetypes.types_map['.exe'] = 'application/x-msdos-program'
 
@@ -468,6 +473,9 @@
 class ExtractorApplication(object):
     policy_answers = {'h': EXTRACT_HERE, 'i': EXTRACT_WRAP,
                       'r': EXTRACT_RENAME, '': EXTRACT_WRAP}
+    recursive_answers = {'o': RECURSE_ONCE, 'a': RECURSE_ALWAYS,
+                         'n': RECURSE_NOT_NOW, 'v': RECURSE_NEVER,
+                         '': RECURSE_NOT_NOW}
 
     def __init__(self, arguments):
         self.parse_options(arguments)
@@ -503,7 +511,6 @@
                           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}
@@ -550,7 +557,7 @@
     def get_handler(self):
         try:
             content, content_name = self.current_extractor.check_contents()
-            if content == ONE_ENTRY:
+            if (content == ONE_ENTRY) and (self.options.onedir_policy is None):
                 question = textwrap.wrap("%s contains one entry: %s." %
                                          (self.current_filename, content_name))
                 question.extend(["You can:",
@@ -569,8 +576,34 @@
         except ExtractorError, error:
             return str(error)
 
+    def get_recursion_policy(self):
+        if len(self.current_extractor.included_archives) > 1:
+            question = ("%s contains %s other archive files." %
+                        (self.current_filename,
+                         len(self.current_extractor.included_archives)))
+        else:
+            question = ("%s contains another archive: %s." %
+                        (self.current_filename,
+                         self.current_extractor.included_archives[0]))
+        question = textwrap.wrap(question)
+        question.extend(["You can:",
+                         " * Always extract included archives",
+                         " * extract included archives this Once",
+                         " * choose Not to extract included archives",
+                         " * neVer extract included archives",
+                         "What do you want to do?  (a/o/N/v) "])
+        self.options.recurse_policy = self.ask_question(question,
+                                                        self.recursive_answers)
+
     def recurse(self):
-        if not self.options.recursive:
+        if not self.current_extractor.included_archives:
+            return
+        if self.options.recurse_policy is None:
+            if self.options.recursive:
+                self.options.recurse_policy = RECURSE_ALWAYS
+            else:
+                self.get_recursion_policy()
+        if self.options.recurse_policy in (RECURSE_NOT_NOW, RECURSE_NEVER):
             return
         for filename in self.current_extractor.included_archives:
             tail_path, basename = os.path.split(filename)
@@ -595,9 +628,17 @@
             self.failures.append(self.current_filename)
 
     def extract(self):
+        self.options.recurse_policy = None
+        first_run = True
         while self.archives:
             self.current_directory, filenames = self.archives.popitem()
+            self.options.onedir_policy = EXTRACT_WRAP
             for filename in filenames:
+                if first_run:
+                    self.options.onedir_policy = None
+                if self.options.recurse_policy not in (RECURSE_ALWAYS,
+                                                       RECURSE_NEVER):
+                    self.options.recurse_policy = None
                 os.chdir(self.current_directory)
                 self.current_filename = filename
                 success = (self.report(self.get_extractor) and
@@ -608,6 +649,7 @@
                                                        name)) and success)
                     self.recurse()
                 self.record_status(success)
+            first_run = False
 
     def show_contents(self):
         for filename in self.current_extractor.get_filenames():
--- a/tests/tests.yml	Sat Apr 21 13:09:58 2007 -0400
+++ b/tests/tests.yml	Sat Apr 28 23:52:36 2007 -0400
@@ -43,7 +43,7 @@
     mkdir test-badperms
     cd test-badperms
     tar -xf ../test-badperms.tar
-    chmod 755 testdir
+    chmod 700 testdir
   posttest: |
     if [ "x`cat test-recursive-badperms/test-badperms/testdir/testfile`" = \
          "xhey" ]; then exit 0; else exit 1; fi
@@ -274,3 +274,73 @@
   input: h
   baseline: |
     tar -zxf $1
+
+- name: two one-item archives with different answers
+  filenames: test-onedir.tar.gz test-onedir.tar.gz
+  options: ""
+  input: |
+    h
+    r
+  baseline: |
+    tar -zxf $1
+    mv test test-onedir
+    tar -zxf $1
+
+- name: interactive recursion (always)
+  filenames: test-recursive-badperms.tar.bz2 test-recursive-badperms.tar.bz2
+  options: ""
+  input: |
+    i
+    a
+    i
+  baseline: |
+    extract() {
+      mkdir test-recursive-badperms$2
+      cd test-recursive-badperms$2
+      tar -jxf ../$1
+      mkdir test-badperms
+      cd test-badperms
+      tar -xf ../test-badperms.tar
+      chmod 700 testdir
+      cd ../..
+    }
+    extract $1
+    extract $1 .1
+
+- name: interactive recursion (once)
+  filenames: test-recursive-badperms.tar.bz2 test-recursive-badperms.tar.bz2
+  options: ""
+  input: |
+    i
+    o
+    i
+    n
+  baseline: |
+    mkdir test-recursive-badperms
+    cd test-recursive-badperms
+    tar -jxf ../$1
+    mkdir test-badperms
+    cd test-badperms
+    tar -xf ../test-badperms.tar
+    chmod 700 testdir
+    cd ../..
+    mkdir test-recursive-badperms.1
+    cd test-recursive-badperms.1
+    tar -jxf ../$1
+
+- name: interactive recursion (never)
+  filenames: test-recursive-badperms.tar.bz2 test-recursive-badperms.tar.bz2
+  options: ""
+  input: |
+    i
+    v
+    i
+  baseline: |
+    extract() {
+      mkdir test-recursive-badperms$2
+      cd test-recursive-badperms$2
+      tar -jxf ../$1
+      cd ..
+    }
+    extract $1
+    extract $1 .1

mercurial