Browse Source

Run alternative decompression programs if the principal one is not available.

Some compression programs are able to handle various compression formats
(e.g. `gzip' can decompress files created by `compress', `xz' is able
to handle lzma, etc.)  Tar tries to use such programs for decompression
if the principal decompressor cannot be started.

* src/buffer.c (compress_type): Swap ct_none and ct_tar.
(archive_compression_type): New static variable.
(zip_magic): Remove program and option fields.
(zip_program): New structure and static.
(compress_program): Remove macro.
(find_zip_program): New static function.
(first_decompress_program,next_decompress_program): New functions.
(open_compressed_archive): Set archive_compression_type instead of
use_compress_program_option.
* src/common.h (first_decompress_program)
(next_decompress_program): New functions.
(WARN_DECOMPRESS_PROGRAM): New flag.
(WARN_VERBOSE_WARNINGS): Include WARN_DECOMPRESS_PROGRAM.
* src/warning.c (warning_args): Add "decompress-program".
(warning_types): Add WARN_DECOMPRESS_PROGRAM.
* src/system.c (run_decompress_program): New function.
(sys_child_open_for_uncompress): Use run_decompress_program
instead of calling execlp directly.
Sergey Poznyakoff 14 years ago
parent
commit
f1fed3996a
5 changed files with 152 additions and 20 deletions
  1. 35 0
      doc/tar.texi
  2. 83 12
      src/buffer.c
  3. 6 1
      src/common.h
  4. 25 6
      src/system.c
  5. 3 1
      src/warning.c

+ 35 - 0
doc/tar.texi

@@ -4166,6 +4166,23 @@ Disable all warning messages.
 @cindex @samp{Ignoring unknown extended header keyword `%s'}, warning message
 @item unknown-keyword
 @samp{Ignoring unknown extended header keyword `%s'}
+@kwindex decompress-program
+@item decompress-program
+Controls verbose messages describing execution failures when trying
+alternative decompressor programs (@pxref{alternative decompression
+programs}).  This warning is disabled by default (unless
+@option{--verbose} is used).  A common example of what you can get
+when using this warning is:
+
+@smallexample
+$ @kbd{tar --warning=decompress-program -x -f archive.Z}
+tar (child): cannot run compress: No such file or directory
+tar (child): trying gzip
+@end smallexample
+
+This means that @command{tar} first tried to decompress
+@file{archive.Z} using @command{compress}, and, when that
+failed, switched to @command{gzip}.
 @end table
 
 @subheading Keywords controlling incremental extraction:
@@ -8755,6 +8772,24 @@ certain compression formats.  If this approach fails, @command{tar}
 falls back to using archive name suffix to determine its format
 (@pxref{auto-compress}, for a list of recognized suffixes).
 
+@anchor{alternative decompression programs}
+@cindex alternative decompression programs
+Some compression programs are able to handle different compression
+formats.  @GNUTAR{} uses this, if the principal decompressor for the
+given format is not available.  For example, if @command{compress} is
+not installed, @command{tar} will try to use @command{gzip}.  As of
+version @value{VERSION} the following alternatives are
+tried@footnote{To verbosely trace the decompressor selection, use the
+@option{--warning=decompress-program} option
+(@pxref{warnings,decompress-program}).}:
+
+@multitable @columnfractions 0.3 0.3 0.3 
+@headitem Format @tab Main decompressor @tab Alternatives
+@item compress @tab compress @tab gzip
+@item lzma     @tab lzma     @tab xz
+@item bzip2    @tab bzip2    @tab lbzip2
+@end multitable
+
 The only case when you have to specify a decompression option while
 reading the archive is when reading from a pipe or from a tape drive
 that does not support random access.  However, in this case @GNUTAR{}

+ 83 - 12
src/buffer.c

@@ -261,8 +261,8 @@ compute_duration ()
 /* Compression detection */
 
 enum compress_type {
-  ct_tar,              /* Plain tar file */
   ct_none,             /* Unknown compression type */
+  ct_tar,              /* Plain tar file */
   ct_compress,
   ct_gzip,
   ct_bzip2,
@@ -272,31 +272,102 @@ enum compress_type {
   ct_xz
 };
 
+static enum compress_type archive_compression_type = ct_none;
+
 struct zip_magic
 {
   enum compress_type type;
   size_t length;
   char const *magic;
+};
+
+struct zip_program
+{
+  enum compress_type type;
   char const *program;
   char const *option;
 };
 
 static struct zip_magic const magic[] = {
-  { ct_tar },
   { ct_none, },
-  { ct_compress, 2, "\037\235",  COMPRESS_PROGRAM, "-Z" },
-  { ct_gzip,     2, "\037\213",  GZIP_PROGRAM,     "-z"  },
-  { ct_bzip2,    3, "BZh",       BZIP2_PROGRAM,    "-j" },
-  { ct_lzip,     4, "LZIP",      LZIP_PROGRAM,     "--lzip" },
-  { ct_lzma,     6, "\xFFLZMA",  LZMA_PROGRAM,     "--lzma" },
-  { ct_lzop,     4, "\211LZO",   LZOP_PROGRAM,     "--lzop" },
-  { ct_xz,       6, "\xFD" "7zXZ",  XZ_PROGRAM,       "-J" },
+  { ct_tar },
+  { ct_compress, 2, "\037\235" },
+  { ct_gzip,     2, "\037\213" },
+  { ct_bzip2,    3, "BZh" },
+  { ct_lzip,     4, "LZIP" },
+  { ct_lzma,     6, "\xFFLZMA" },
+  { ct_lzop,     4, "\211LZO" },
+  { ct_xz,       6, "\xFD" "7zXZ" },
 };
 
 #define NMAGIC (sizeof(magic)/sizeof(magic[0]))
 
-#define compress_option(t) magic[t].option
-#define compress_program(t) magic[t].program
+static struct zip_program zip_program[] = {
+  { ct_compress, COMPRESS_PROGRAM, "-Z" },
+  { ct_compress, GZIP_PROGRAM,     "-z" },
+  { ct_gzip,     GZIP_PROGRAM,     "-z" },
+  { ct_bzip2,    BZIP2_PROGRAM,    "-j" },
+  { ct_bzip2,    "lbzip2",         "-j" },
+  { ct_lzip,     LZIP_PROGRAM,     "--lzip" },
+  { ct_lzma,     LZMA_PROGRAM,     "--lzma" },
+  { ct_lzma,     XZ_PROGRAM,       "-J" },
+  { ct_lzop,     LZOP_PROGRAM,     "--lzop" },
+  { ct_xz,       XZ_PROGRAM,       "-J" },
+  { ct_none }
+};
+
+static struct zip_program const *
+find_zip_program (enum compress_type type, int *pstate)
+{
+  int i;
+
+  for (i = *pstate; zip_program[i].type != ct_none; i++)
+    {
+      if (zip_program[i].type == type)
+	{
+	  *pstate = i + 1;
+	  return zip_program + i;
+	}
+    }
+  *pstate = i;
+  return NULL;
+}
+
+const char *
+first_decompress_program (int *pstate)
+{
+  struct zip_program const *zp;
+  
+  if (use_compress_program_option)
+    return use_compress_program_option;
+
+  if (archive_compression_type == ct_none)
+    return NULL;
+
+  *pstate = 0; 
+  zp = find_zip_program (archive_compression_type, pstate);
+  return zp ? zp->program : NULL;
+}
+    
+const char *
+next_decompress_program (int *pstate)
+{
+  struct zip_program const *zp;
+  
+  if (use_compress_program_option)
+    return NULL;
+  zp = find_zip_program (archive_compression_type, pstate);
+  return zp ? zp->program : NULL;
+}
+
+static const char *
+compress_option (enum compress_type type)
+{
+  struct zip_program const *zp;
+  int i = 0;
+  zp = find_zip_program (type, &i);
+  return zp ? zp->option : NULL;
+}
 
 /* Check if the file ARCHIVE is a compressed archive. */
 static enum compress_type
@@ -395,7 +466,7 @@ open_compressed_archive (void)
               break;
 
             default:
-              use_compress_program_option = compress_program (type);
+              archive_compression_type = type;
               break;
             }
         }

+ 6 - 1
src/common.h

@@ -440,6 +440,9 @@ void mv_size_left (off_t size);
 
 void buffer_write_global_xheader (void);
 
+const char *first_decompress_program (int *pstate);
+const char *next_decompress_program (int *pstate);
+
 /* Module create.c.  */
 
 enum dump_status
@@ -805,10 +808,12 @@ void checkpoint_run (bool do_write);
 #define WARN_UNKNOWN_CAST        0x00010000
 #define WARN_UNKNOWN_KEYWORD     0x00020000
 #define WARN_XDEV                0x00040000
+#define WARN_DECOMPRESS_PROGRAM  0x00080000
 
 /* The warnings composing WARN_VERBOSE_WARNINGS are enabled by default
    in verbose mode */
-#define WARN_VERBOSE_WARNINGS    (WARN_RENAME_DIRECTORY|WARN_NEW_DIRECTORY)
+#define WARN_VERBOSE_WARNINGS    (WARN_RENAME_DIRECTORY|WARN_NEW_DIRECTORY|\
+				  WARN_DECOMPRESS_PROGRAM)
 #define WARN_ALL                 (~WARN_VERBOSE_WARNINGS)
 
 void set_warning_option (const char *arg);

+ 25 - 6
src/system.c

@@ -455,6 +455,29 @@ sys_child_open_for_compress (void)
   wait_for_grandchild (grandchild_pid);
 }
 
+static void
+run_decompress_program (void)
+{
+  int i;
+  const char *p, *prog = NULL;
+
+  for (p = first_decompress_program (&i); p; p = next_decompress_program (&i))
+    {
+      if (prog)
+	{
+	  WARNOPT (WARN_DECOMPRESS_PROGRAM,
+		   (0, errno, _("cannot run %s"), prog));
+	  WARNOPT (WARN_DECOMPRESS_PROGRAM,
+		   (0, 0, _("trying %s"), p));
+	}
+      prog = p;
+      execlp (p, p, "-d", NULL);
+    }
+  if (!prog)
+    FATAL_ERROR ((0, 0, _("unable to run decompression program")));
+  exec_fatal (prog);
+}
+
 /* Set ARCHIVE for uncompressing, then reading an archive.  */
 pid_t
 sys_child_open_for_uncompress (void)
@@ -501,9 +524,7 @@ sys_child_open_for_uncompress (void)
 	open_fatal (archive_name_array[0]);
       xdup2 (archive, STDIN_FILENO);
       priv_set_restore_linkdir ();
-      execlp (use_compress_program_option, use_compress_program_option,
-	      "-d", (char *) 0);
-      exec_fatal (use_compress_program_option);
+      run_decompress_program ();
     }
 
   /* We do need a grandchild tar.  */
@@ -520,9 +541,7 @@ sys_child_open_for_uncompress (void)
       xdup2 (child_pipe[PREAD], STDIN_FILENO);
       xclose (child_pipe[PWRITE]);
       priv_set_restore_linkdir ();
-      execlp (use_compress_program_option, use_compress_program_option,
-	      "-d", (char *) 0);
-      exec_fatal (use_compress_program_option);
+      run_decompress_program ();
     }
 
   /* The child tar is still here!  */

+ 3 - 1
src/warning.c

@@ -41,6 +41,7 @@ static char const *const warning_args[] = {
   "unknown-cast",
   "unknown-keyword",
   "xdev",
+  "decompress-program",
   NULL
 };
 
@@ -64,7 +65,8 @@ static int warning_types[] = {
   WARN_TIMESTAMP,
   WARN_UNKNOWN_CAST,
   WARN_UNKNOWN_KEYWORD,
-  WARN_XDEV
+  WARN_XDEV,
+  WARN_DECOMPRESS_PROGRAM
 };
 
 ARGMATCH_VERIFY (warning_args, warning_types);