]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Introduce -a/--auto-compress option into bsdtar.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sun, 21 Oct 2012 08:05:28 +0000 (17:05 +0900)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Mon, 22 Oct 2012 10:57:20 +0000 (19:57 +0900)
This automatically decides on a creation format and filters by
the archive suffix.

14 files changed:
Makefile.am
libarchive/CMakeLists.txt
libarchive/archive.h
libarchive/archive_write_add_filter_by_name.c [new file with mode: 0644]
tar/CMakeLists.txt
tar/bsdtar.1
tar/bsdtar.c
tar/bsdtar.h
tar/cmdline.c
tar/creation_set.c [new file with mode: 0644]
tar/read.c
tar/test/CMakeLists.txt
tar/test/test_option_a.c [new file with mode: 0644]
tar/write.c

index 556c315bf6c746cb4a1a985ff689e260c1d8d8a2..b37b7b8de9f58e3300ac385b424dfc32b8126d15 100644 (file)
@@ -177,6 +177,7 @@ libarchive_la_SOURCES=                                              \
        libarchive/archive_write_private.h                      \
        libarchive/archive_write_add_filter.c                   \
        libarchive/archive_write_add_filter_b64encode.c         \
+       libarchive/archive_write_add_filter_by_name.c           \
        libarchive/archive_write_add_filter_bzip2.c             \
        libarchive/archive_write_add_filter_compress.c          \
        libarchive/archive_write_add_filter_grzip.c             \
@@ -673,6 +674,7 @@ bsdtar_SOURCES=                             \
                tar/bsdtar.h            \
                tar/bsdtar_platform.h   \
                tar/cmdline.c           \
+               tar/creation_set.c      \
                tar/read.c              \
                tar/subst.c             \
                tar/util.c              \
@@ -744,6 +746,7 @@ bsdtar_test_SOURCES=                                                \
        tar/test/test_option_T_upper.c                          \
        tar/test/test_option_U_upper.c                          \
        tar/test/test_option_X_upper.c                          \
+       tar/test/test_option_a.c                                \
        tar/test/test_option_b.c                                \
        tar/test/test_option_b64encode.c                        \
        tar/test/test_option_exclude.c                          \
index 8927612cd6c83b464249d507ce1e82c193ceb011..a57244d01f404fda8185ccc94eecb092dc514b38 100644 (file)
@@ -102,6 +102,7 @@ SET(libarchive_SOURCES
   archive_write_open_memory.c
   archive_write_add_filter.c
   archive_write_add_filter_b64encode.c
+  archive_write_add_filter_by_name.c
   archive_write_add_filter_bzip2.c
   archive_write_add_filter_compress.c
   archive_write_add_filter_grzip.c
index 6ee542cedb0699a7f6238de970edf044de664ffe..1503b038ad677ee63f0ddb3801412dedfd7cb454 100644 (file)
@@ -618,6 +618,8 @@ __LA_DECL int archive_write_set_compression_xz(struct archive *)
 
 /* A convenience function to set the filter based on the code. */
 __LA_DECL int archive_write_add_filter(struct archive *, int filter_code);
+__LA_DECL int archive_write_add_filter_by_name(struct archive *,
+                    const char *name);
 __LA_DECL int archive_write_add_filter_b64encode(struct archive *);
 __LA_DECL int archive_write_add_filter_bzip2(struct archive *);
 __LA_DECL int archive_write_add_filter_compress(struct archive *);
diff --git a/libarchive/archive_write_add_filter_by_name.c b/libarchive/archive_write_add_filter_by_name.c
new file mode 100644 (file)
index 0000000..e4cba4a
--- /dev/null
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static
+struct { const char *name; int (*setter)(struct archive *); } names[] =
+{
+       { "b64encode",          archive_write_add_filter_b64encode },
+       { "bzip2",              archive_write_add_filter_bzip2 },
+       { "compress",           archive_write_add_filter_compress },
+       { "grzip",              archive_write_add_filter_grzip },
+       { "gzip",               archive_write_add_filter_gzip },
+       { "lrzip",              archive_write_add_filter_lrzip },
+       { "lzip",               archive_write_add_filter_lzip },
+       { "lzma",               archive_write_add_filter_lzma },
+       { "lzop",               archive_write_add_filter_lzop },
+       { "uuencode",           archive_write_add_filter_uuencode },
+       { "xz",                 archive_write_add_filter_xz },
+       { NULL,                 NULL }
+};
+
+int
+archive_write_add_filter_by_name(struct archive *a, const char *name)
+{
+       int i;
+
+       for (i = 0; names[i].name != NULL; i++) {
+               if (strcmp(name, names[i].name) == 0)
+                       return ((names[i].setter)(a));
+       }
+
+       archive_set_error(a, EINVAL, "No such filter '%s'", name);
+       a->state = ARCHIVE_STATE_FATAL;
+       return (ARCHIVE_FATAL);
+}
index eddf7ecc1ebb2357fe16017c24c9566700ac923d..46ce58b02e2e92db8c174ad8a6214b20b0a67306 100644 (file)
@@ -10,6 +10,7 @@ IF(ENABLE_TAR)
     bsdtar.h
     bsdtar_platform.h
     cmdline.c
+    creation_set.c
     read.c
     subst.c
     util.c
index ff8665a4a4c602fb58542bf7884363637b1136c5..2297c35068e5854d9f42990cbfc133f3f128800a 100644 (file)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 7, 2012
+.Dd October 22, 2012
 .Dt TAR 1
 .Os
 .Sh NAME
@@ -144,6 +144,26 @@ pax-format archive on stdout.
 In this way,
 .Nm
 can be used to convert archives from one format to another.
+.It Fl a , Fl Fl auto-compress
+(c mode only)
+Use the archive suffix to decide a set of the format and
+the compressions.
+As a simple example,
+.Dl Nm Fl a Fl cf Pa archive.tgz source.c source.h
+creates a new archive with restricted pax format and gzip compression,
+.Dl Nm Fl a Fl cf Pa archive.tar.bz2.uu source.c source.h
+creates a new archive with restricted pax format and bzip2 compression
+and uuencode compression,
+.Dl Nm Fl a Fl cf Pa archive.zip source.c source.h
+creates a new archive with zip format,
+.Dl Nm Fl a Fl jcf Pa archive.tgz source.c source.h
+ignores the
+.Dq -j
+option, and creates a new archive with restricted pax format
+and gzip compression,
+.Dl Nm Fl a Fl jcf Pa archive.xxx source.c source.h
+if it is unknown suffix or no suffix, creates a new archive with
+restricted pax format and bzip2 compression.
 .It Fl B , Fl Fl read-full-blocks
 Ignored for compatibility with other
 .Xr tar 1
index 7f19d837620a28ddad64ffcd718c0b8b6a560f62..99ce7644139e2f0e117d70d2e598b0eb64d2f3e3 100644 (file)
@@ -134,7 +134,10 @@ main(int argc, char **argv)
 {
        struct bsdtar           *bsdtar, bsdtar_storage;
        int                      opt, t;
-       char                     option_o;
+       char                     compression, compression2;
+       const char              *compression_name, *compression2_name;
+       const char              *compress_program;
+       char                     option_a, option_o;
        char                     possible_help_request;
        char                     buff[16];
 
@@ -147,7 +150,10 @@ main(int argc, char **argv)
        bsdtar->fd = -1; /* Mark as "unused" */
        bsdtar->gid = -1;
        bsdtar->uid = -1;
-       option_o = 0;
+       option_a = option_o = 0;
+       compression = compression2 = '\0';
+       compression_name = compression2_name = NULL;
+       compress_program = NULL;
 
 #if defined(HAVE_SIGACTION)
        { /* Set up signal handling. */
@@ -243,6 +249,9 @@ main(int argc, char **argv)
        bsdtar->matching = archive_match_new();
        if (bsdtar->matching == NULL)
                lafe_errc(1, errno, "Out of memory");
+       bsdtar->cset = cset_new();
+       if (bsdtar->cset == NULL)
+               lafe_errc(1, errno, "Out of memory");
 
        bsdtar->argv = argv;
        bsdtar->argc = argc;
@@ -255,6 +264,9 @@ main(int argc, char **argv)
         */
        while ((opt = bsdtar_getopt(bsdtar)) != -1) {
                switch (opt) {
+               case 'a': /* GNU tar */
+                       option_a = 1; /* Record it and resolve it later. */
+                       break;
                case 'B': /* GNU tar */
                        /* libarchive doesn't need this; just ignore it. */
                        break;
@@ -268,11 +280,12 @@ main(int argc, char **argv)
                        bsdtar->bytes_in_last_block = bsdtar->bytes_per_block;
                        break;
                case OPTION_B64ENCODE:
-                       if (bsdtar->add_filter != '\0')
+                       if (compression2 != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both --uuencode and "
                                    "--b64encode");
-                       bsdtar->add_filter = opt;
+                       compression2 = opt;
+                       compression2_name = "b64encode";
                        break;
                case 'C': /* GNU tar */
                        if (strlen(bsdtar->argument) == 0)
@@ -300,7 +313,7 @@ main(int argc, char **argv)
                                    "Couldn't exclude %s\n", bsdtar->argument);
                        break;
                case OPTION_FORMAT: /* GNU tar, others */
-                       bsdtar->create_format = bsdtar->argument;
+                       cset_set_format(bsdtar->cset, bsdtar->argument);
                        break;
                case 'f': /* SUSv2 */
                        bsdtar->filename = bsdtar->argument;
@@ -316,11 +329,12 @@ main(int argc, char **argv)
                        bsdtar->gname = bsdtar->argument;
                        break;
                case OPTION_GRZIP:
-                       if (bsdtar->create_compression != '\0')
+                       if (compression != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both -%c and -%c", opt,
-                                   bsdtar->create_compression);
-                       bsdtar->create_compression = opt;
+                                   compression);
+                       compression = opt;
+                       compression_name = "grzip";
                        break;
                case 'H': /* BSD convention */
                        bsdtar->symlink_mode = 'H';
@@ -360,18 +374,20 @@ main(int argc, char **argv)
                                    bsdtar->argument);
                        break;
                case 'j': /* GNU tar */
-                       if (bsdtar->create_compression != '\0')
+                       if (compression != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both -%c and -%c", opt,
-                                   bsdtar->create_compression);
-                       bsdtar->create_compression = opt;
+                                   compression);
+                       compression = opt;
+                       compression_name = "bzip2";
                        break;
                case 'J': /* GNU tar 1.21 and later */
-                       if (bsdtar->create_compression != '\0')
+                       if (compression != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both -%c and -%c", opt,
-                                   bsdtar->create_compression);
-                       bsdtar->create_compression = opt;
+                                   compression);
+                       compression = opt;
+                       compression_name = "xz";
                        break;
                case 'k': /* GNU tar */
                        bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
@@ -390,11 +406,17 @@ main(int argc, char **argv)
                case OPTION_LZIP: /* GNU tar beginning with 1.23 */
                case OPTION_LZMA: /* GNU tar beginning with 1.20 */
                case OPTION_LZOP: /* GNU tar beginning with 1.21 */
-                       if (bsdtar->create_compression != '\0')
+                       if (compression != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both -%c and -%c", opt,
-                                   bsdtar->create_compression);
-                       bsdtar->create_compression = opt;
+                                   compression);
+                       compression = opt;
+                       switch (opt) {
+                       case OPTION_LRZIP: compression_name = "lrzip"; break;
+                       case OPTION_LZIP: compression_name = "lzip"; break; 
+                       case OPTION_LZMA: compression_name = "lzma"; break; 
+                       case OPTION_LZOP: compression_name = "lzop"; break; 
+                       }
                        break;
                case 'm': /* SUSv2 */
                        bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
@@ -527,7 +549,7 @@ main(int argc, char **argv)
                        bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
                        break;
                case OPTION_POSIX: /* GNU tar */
-                       bsdtar->create_format = "pax";
+                       cset_set_format(bsdtar->cset, "pax");
                        break;
                case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */
                        bsdtar->option_fast_read = 1;
@@ -587,11 +609,12 @@ main(int argc, char **argv)
                        bsdtar->uname = bsdtar->argument;
                        break;
                case OPTION_UUENCODE:
-                       if (bsdtar->add_filter != '\0')
+                       if (compression2 != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both --uuencode and "
                                    "--b64encode");
-                       bsdtar->add_filter = opt;
+                       compression2 = opt;
+                       compression2_name = "uuencode";
                        break;
                case 'v': /* SUSv2 */
                        bsdtar->verbose++;
@@ -621,28 +644,31 @@ main(int argc, char **argv)
                        set_mode(bsdtar, opt);
                        break;
                case 'y': /* FreeBSD version of GNU tar */
-                       if (bsdtar->create_compression != '\0')
+                       if (compression != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both -%c and -%c", opt,
-                                   bsdtar->create_compression);
-                       bsdtar->create_compression = opt;
+                                   compression);
+                       compression = opt;
+                       compression_name = "bzip2";
                        break;
                case 'Z': /* GNU tar */
-                       if (bsdtar->create_compression != '\0')
+                       if (compression != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both -%c and -%c", opt,
-                                   bsdtar->create_compression);
-                       bsdtar->create_compression = opt;
+                                   compression);
+                       compression = opt;
+                       compression_name = "compress";
                        break;
                case 'z': /* GNU tar, star, many others */
-                       if (bsdtar->create_compression != '\0')
+                       if (compression != '\0')
                                lafe_errc(1, 0,
                                    "Can't specify both -%c and -%c", opt,
-                                   bsdtar->create_compression);
-                       bsdtar->create_compression = opt;
+                                   compression);
+                       compression = opt;
+                       compression_name = "gzip";
                        break;
                case OPTION_USE_COMPRESS_PROGRAM:
-                       bsdtar->compress_program = bsdtar->argument;
+                       compress_program = bsdtar->argument;
                        break;
                default:
                        usage();
@@ -665,6 +691,8 @@ main(int argc, char **argv)
                    "Must specify one of -c, -r, -t, -u, -x");
 
        /* Check boolean options only permitted in certain modes. */
+       if (option_a)
+               only_mode(bsdtar, "-a", "c");
        if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS)
                only_mode(bsdtar, "--one-file-system", "cru");
        if (bsdtar->option_fast_read)
@@ -679,7 +707,7 @@ main(int argc, char **argv)
                         * "ustar" format is the closest thing
                         * supported by libarchive.
                         */
-                       bsdtar->create_format = "ustar";
+                       cset_set_format(bsdtar->cset, "ustar");
                        /* TODO: bsdtar->create_format = "v7"; */
                        break;
                case 'x':
@@ -701,13 +729,40 @@ main(int argc, char **argv)
        if (bsdtar->option_warn_links)
                only_mode(bsdtar, "--check-links", "cr");
 
+       if (option_a && cset_auto_compress(bsdtar->cset, bsdtar->filename)) {
+               /* Ignore specified compressions if auto-compress works. */
+               compression = '\0';
+               compression2 = '\0';
+       }
        /* Check other parameters only permitted in certain modes. */
-       if (bsdtar->create_compression != '\0') {
-               strcpy(buff, "-?");
-               buff[1] = bsdtar->create_compression;
+       if (compress_program != NULL) {
+               only_mode(bsdtar, "--use-compress-program", "cxt");
+               cset_add_filter_program(bsdtar->cset, compress_program);
+               /* Ignore specified compressions. */
+               compression = '\0';
+               compression2 = '\0';
+       }
+       if (compression != '\0') {
+               switch (compression) {
+               case 'J': case 'j': case 'y': case 'Z': case 'z':
+                       strcpy(buff, "-?");
+                       buff[1] = compression;
+                       break;
+               default:
+                       strcpy(buff, "--");
+                       strcat(buff, compression_name);
+                       break;
+               }
+               only_mode(bsdtar, buff, "cxt");
+               cset_add_filter(bsdtar->cset, compression_name);
+       }
+       if (compression2 != '\0') {
+               strcpy(buff, "--");
+               strcat(buff, compression2_name);
                only_mode(bsdtar, buff, "cxt");
+               cset_add_filter(bsdtar->cset, compression2_name);
        }
-       if (bsdtar->create_format != NULL)
+       if (cset_get_format(bsdtar->cset) != NULL)
                only_mode(bsdtar, "--format", "cru");
        if (bsdtar->symlink_mode != '\0') {
                strcpy(buff, "-?");
@@ -741,6 +796,7 @@ main(int argc, char **argv)
 #if HAVE_REGEX_H
        cleanup_substitution(bsdtar);
 #endif
+       cset_free(bsdtar->cset);
 
        if (bsdtar->return_value != 0)
                lafe_warnc(0,
index a21be3d0d0b82d8a61cde1101d08e5094027976f..8ee465d5030dfb137a561c1c2ff226bac787b315 100644 (file)
@@ -30,6 +30,7 @@
 
 #define        DEFAULT_BYTES_PER_BLOCK (20*512)
 
+struct creation_set;
 /*
  * The internal state for the "bsdtar" program.
  *
@@ -41,7 +42,6 @@
 struct bsdtar {
        /* Options */
        const char       *filename; /* -f filename */
-       const char       *create_format; /* -F format */
        char             *pending_chdir; /* -C dir */
        const char       *names_from_file; /* -T file */
        int               bytes_per_block; /* -b block_size */
@@ -56,9 +56,6 @@ struct bsdtar {
        const char       *uname; /* --uname */
        char              mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */
        char              symlink_mode; /* H or L, per BSD conventions */
-       char              create_compression; /* j, y, or z */
-       const char       *compress_program;
-       char              add_filter; /* uuencode */
        char              option_absolute_paths; /* -P */
        char              option_chroot; /* --chroot */
        char              option_fast_read; /* --fast-read */
@@ -73,6 +70,7 @@ struct bsdtar {
        char              option_unlink_first; /* -U */
        char              option_warn_links; /* --check-links */
        char              day_first; /* show day before month in -tv output */
+       struct creation_set *cset;
 
        /* Option parser state */
        int               getopt_state;
@@ -175,3 +173,16 @@ void       add_substitution(struct bsdtar *, const char *);
 int    apply_substitution(struct bsdtar *, const char *, char **, int, int);
 void   cleanup_substitution(struct bsdtar *);
 #endif
+
+void           cset_add_filter(struct creation_set *, const char *);
+void           cset_add_filter_program(struct creation_set *, const char *);
+int            cset_auto_compress(struct creation_set *, const char *);
+void           cset_free(struct creation_set *);
+const char *   cset_get_format(struct creation_set *);
+struct creation_set *cset_new(void);
+int            cset_read_support_filter_program(struct creation_set *,
+                   struct archive *);
+void           cset_set_format(struct creation_set *, const char *);
+int            cset_write_add_filters(struct creation_set *,
+                   struct archive *, const void **);
+
index e658ec8673182e840826c2b062906319eed80261..659ae9546fc2bd7df78ccc1142ea97442ecbeb9a 100644 (file)
@@ -47,7 +47,7 @@ __FBSDID("$FreeBSD$");
  * Short options for tar.  Please keep this sorted.
  */
 static const char *short_options
-       = "Bb:C:cf:HhI:JjkLlmnOoPpqrSs:T:tUuvW:wX:xyZz";
+       = "aBb:C:cf:HhI:JjkLlmnOoPpqrSs:T:tUuvW:wX:xyZz";
 
 /*
  * Long options for tar.  Please keep this list sorted.
@@ -65,6 +65,7 @@ static const struct bsdtar_option {
 } tar_longopts[] = {
        { "absolute-paths",       0, 'P' },
        { "append",               0, 'r' },
+       { "auto-compress",        0, 'a' },
        { "b64encode",            0, OPTION_B64ENCODE },
        { "block-size",           1, 'b' },
        { "bunzip2",              0, 'j' },
diff --git a/tar/creation_set.c b/tar/creation_set.c
new file mode 100644 (file)
index 0000000..f5cb75f
--- /dev/null
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bsdtar_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "bsdtar.h"
+#include "err.h"
+
+struct creation_set {
+       char             *create_format;
+       struct filter_set {
+               int       program;      /* Set 1 if filter is a program name */
+               char     *filter_name;
+       }                *filters;
+       int               filter_count;
+};
+
+struct suffix_code_t {
+       const char *suffix;
+       const char *form;
+};
+
+static const char *
+get_suffix_code(const struct suffix_code_t *tbl, const char *suffix)
+{
+       int i;
+
+       if (suffix == NULL)
+               return (NULL);
+       for (i = 0; tbl[i].suffix != NULL; i++) {
+               if (strcmp(tbl[i].suffix, suffix) == 0)
+                       return (tbl[i].form);
+       }
+       return (NULL);
+}
+
+static const char *
+get_filter_code(const char *suffix)
+{
+       /* A pair of suffix and compression/filter. */
+       static const struct suffix_code_t filters[] = {
+               { ".Z",         "compress" },
+               { ".bz2",       "bzip2" },
+               { ".gz",        "gzip" },
+               { ".grz",       "grzip" },
+               { ".lrz",       "lrzip" },
+               { ".lz",        "lzip" },
+               { ".lzo",       "lzop" },
+               { ".lzma",      "lzma" },
+               { ".uu",        "uuencode" },
+               { ".xz",        "xz" },
+               { NULL,         NULL }
+       };
+       
+       return get_suffix_code(filters, suffix);
+}
+
+static const char *
+get_format_code(const char *suffix)
+{
+       /* A pair of suffix and format. */
+       static const struct suffix_code_t formats[] = {
+               { ".7z",        "7zip" },
+               { ".ar",        "arbsd" },
+               { ".cpio",      "cpio" },
+               { ".iso",       "iso9960" },
+               { ".mtree",     "mtree" },
+               { ".shar",      "shar" },
+               { ".tar",       "paxr" },
+               { ".xar",       "xar" },
+               { ".zip",       "zip" },
+               { NULL,         NULL }
+       };
+
+       return get_suffix_code(formats, suffix);
+}
+
+static const char *
+decompose_alias(const char *suffix)
+{
+       static const struct suffix_code_t alias[] = {
+               { ".taz",       ".tar.gz" },
+               { ".tgz",       ".tar.gz" },
+               { ".tbz",       ".tar.bz2" },
+               { ".tbz2",      ".tar.bz2" },
+               { ".tz2",       ".tar.bz2" },
+               { ".tlz",       ".tar.lzma" },
+               { ".txz",       ".tar.xz" },
+               { ".tzo",       ".tar.lzo" },
+               { ".taZ",       ".tar.Z" },
+               { ".tZ",        ".tar.Z" },
+               { NULL,         NULL }
+       };
+
+       return get_suffix_code(alias, suffix);
+}
+
+static void
+_cset_add_filter(struct creation_set *cset, int program, const char *filter)
+{
+       struct filter_set *new_ptr;
+       char *new_filter;
+
+       new_ptr = (struct filter_set *)realloc(cset->filters,
+           sizeof(*cset->filters) * (cset->filter_count + 1));
+       if (new_ptr == NULL)
+               lafe_errc(1, 0, "No memory");
+       new_filter = strdup(filter);
+       if (new_filter == NULL)
+               lafe_errc(1, 0, "No memory");
+       cset->filters = new_ptr;
+       cset->filters[cset->filter_count].program = program;
+       cset->filters[cset->filter_count].filter_name = new_filter;
+       cset->filter_count++;
+}
+
+void
+cset_add_filter(struct creation_set *cset, const char *filter)
+{
+       _cset_add_filter(cset, 0, filter);
+}
+
+void
+cset_add_filter_program(struct creation_set *cset, const char *filter)
+{
+       _cset_add_filter(cset, 1, filter);
+}
+
+int
+cset_read_support_filter_program(struct creation_set *cset, struct archive *a)
+{
+       int cnt = 0, i;
+
+       for (i = 0; i < cset->filter_count; i++) {
+               if (cset->filters[i].program) {
+                       archive_read_support_filter_program(a,
+                           cset->filters[i].filter_name);
+                       ++cnt;
+               }
+       }
+       return (cnt);
+}
+
+int
+cset_write_add_filters(struct creation_set *cset, struct archive *a,
+    const void **filter_name)
+{
+       int cnt = 0, i, r;
+
+       for (i = 0; i < cset->filter_count; i++) {
+               if (cset->filters[i].program)
+                       r = archive_write_add_filter_program(a,
+                               cset->filters[i].filter_name);
+               else
+                       r = archive_write_add_filter_by_name(a,
+                               cset->filters[i].filter_name);
+               if (r < ARCHIVE_WARN) {
+                       *filter_name = cset->filters[i].filter_name;
+                       return (r);
+               }
+               ++cnt;
+       }
+       return (cnt);
+}
+
+void
+cset_set_format(struct creation_set *cset, const char *format)
+{
+       char *f;
+
+       f = strdup(format);
+       if (f == NULL)
+               lafe_errc(1, 0, "No memory");
+       free(cset->create_format);
+       cset->create_format = f;
+}
+
+const char *
+cset_get_format(struct creation_set *cset)
+{
+       return (cset->create_format);
+}
+
+static void
+_cleanup_filters(struct filter_set *filters, int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               free(filters[i].filter_name);
+       free(filters);
+}
+
+/*
+ * Clean up a creation set.
+ */
+void
+cset_free(struct creation_set *cset)
+{
+       _cleanup_filters(cset->filters, cset->filter_count);
+       free(cset->create_format);
+       free(cset);
+}
+
+struct creation_set *
+cset_new(void)
+{
+       return calloc(1, sizeof(struct creation_set));
+}
+
+/*
+ * Build a creation set by a file name suffix.
+ */
+int
+cset_auto_compress(struct creation_set *cset, const char *filename)
+{
+       struct filter_set *old_filters;
+       char *name, *p;
+       const char *code;
+       int old_filter_count;
+
+       name = strdup(filename);
+       if (name == NULL)
+               lafe_errc(1, 0, "No memory");
+       /* Save previous filters. */
+       old_filters = cset->filters;
+       old_filter_count = cset->filter_count;
+       cset->filters = NULL;
+       cset->filter_count = 0;
+
+       for (;;) {
+               /* Get the suffix. */
+               p = strrchr(name, '.');
+               if (p == NULL)
+                       break;
+               /* Suppose it indicates compression/filter type
+                * such as ".gz". */
+               code = get_filter_code(p);
+               if (code != NULL) {
+                       cset_add_filter(cset, code);
+                       *p = '\0';
+                       continue;
+               }
+               /* Suppose it indicates format type such as ".tar". */
+               code = get_format_code(p);
+               if (code != NULL) {
+                       cset_set_format(cset, code);
+                       break;
+               }
+               /* Suppose it indicates alias such as ".tgz". */
+               code = decompose_alias(p);
+               if (code == NULL)
+                       break;
+               /* Replace the suffix. */
+               *p = '\0';
+               name = realloc(name, strlen(name) + strlen(code) + 1);
+               if (name == NULL)
+                       lafe_errc(1, 0, "No memory");
+               strcat(name, code);
+       }
+       if (cset->filters) {
+               struct filter_set *v;
+               int i, r;
+
+               /* Release previos filters. */
+               _cleanup_filters(old_filters, old_filter_count);
+
+               v = malloc(sizeof(*v) * cset->filter_count);
+               if (v == NULL)
+                       lafe_errc(1, 0, "No memory");
+               /* Reverse filter sequence. */
+               for (i = 0, r = cset->filter_count; r > 0; )
+                       v[i++] = cset->filters[--r];
+               free(cset->filters);
+               cset->filters = v;
+               return (1);
+       } else {
+               /* Put previos filters back. */
+               cset->filters = old_filters;
+               cset->filter_count = old_filter_count;
+               return (0);
+       }
+}
index 706077200d6ac2c09c9d4effecc8c20e434b69d5..ba5c2e33584d8d012bf751f51970b21ad31b7fed 100644 (file)
@@ -173,9 +173,7 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer)
                            archive_error_string(bsdtar->matching));
 
        a = archive_read_new();
-       if (bsdtar->compress_program != NULL)
-               archive_read_support_filter_program(a, bsdtar->compress_program);
-       else
+       if (cset_read_support_filter_program(bsdtar->cset, a) == 0)
                archive_read_support_filter_all(a);
        archive_read_support_format_all(a);
        if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options))
index a51dbbaa38e7f89e28e85ea588c22103430d27de..98f49e29298b638189e07231c72259de97bb43db 100644 (file)
@@ -30,6 +30,7 @@ IF(ENABLE_TAR AND ENABLE_TEST)
     test_option_T_upper.c
     test_option_U_upper.c
     test_option_X_upper.c
+    test_option_a.c
     test_option_b.c
     test_option_b64encode.c
     test_option_exclude.c
diff --git a/tar/test/test_option_a.c b/tar/test/test_option_a.c
new file mode 100644 (file)
index 0000000..a000621
--- /dev/null
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+DEFINE_TEST(test_option_a)
+{
+       size_t s;
+       char *p;
+
+       /* Create a file. */
+       assertMakeFile("f", 0644, "a");
+
+       /* Test1: archive it with .tar.Z suffix. */
+       assertEqualInt(0,
+           systemf("%s -acf test1.tar.Z f 2>test1.err", testprog));
+       assertEmptyFile("test1.err");
+       /* Check that the archive file has a compress signature. */
+       p = slurpfile(&s, "test1.tar.Z");
+       assert(s > 2);
+       failure("The archive should be compressed");
+       assertEqualMem(p, "\x1f\x9d", 2);
+
+       /* Test2: archive it with .taZ suffix. */
+       assertEqualInt(0,
+           systemf("%s -acf test2.taZ f 2>test2.err", testprog));
+       assertEmptyFile("test2.err");
+       /* Check that the archive file has a compress signature. */
+       p = slurpfile(&s, "test2.taZ");
+       assert(s > 2);
+       failure("The archive should be compressed");
+       assertEqualMem(p, "\x1f\x9d", 2);
+
+       /* Test3: archive it with .tar.Z.uu suffix. */
+       assertEqualInt(0,
+           systemf("%s -acf test3.tar.Z.uu f 2>test3.err", testprog));
+       assertEmptyFile("test3.err");
+       /* Check that the archive file has a compress signature. */
+       p = slurpfile(&s, "test3.tar.Z.uu");
+       assert(s > 12);
+       failure("The archive should be uuencoded");
+       assertEqualMem(p, "begin 644 -\n", 12);
+
+       /* Test4: archive it with .zip suffix. */
+       assertEqualInt(0,
+           systemf("%s -acf test4.zip f 2>test4.err", testprog));
+       assertEmptyFile("test4.err");
+       /* Check that the archive file has a compress signature. */
+       p = slurpfile(&s, "test4.zip");
+       assert(s > 4);
+       failure("The archive should be zipped");
+       assertEqualMem(p, "\x50\x4b\x03\x04", 4);
+
+       /* Test5: archive it with .tar.Z suffix and --uuencode option. */
+       assertEqualInt(0,
+           systemf("%s -acf test5.tar.Z --uuencode f 2>test5.err",
+               testprog));
+       assertEmptyFile("test5.err");
+       /* Check that the archive file has a compress signature. */
+       p = slurpfile(&s, "test5.tar.Z");
+       assert(s > 2);
+       failure("The archive should be compressed, ignoring --uuencode option");
+       assertEqualMem(p, "\x1f\x9d", 2);
+
+       /* Test6: archive it with .xxx suffix(unknown suffix) and
+        * --uuencode option. */
+       assertEqualInt(0,
+           systemf("%s -acf test6.xxx --uuencode f 2>test6.err",
+               testprog));
+       assertEmptyFile("test6.err");
+       /* Check that the archive file has a compress signature. */
+       p = slurpfile(&s, "test6.xxx");
+       assert(s > 12);
+       failure("The archive should be uuencoded");
+       assertEqualMem(p, "begin 644 -\n", 12);
+
+       /* Test7: archive it with .tar.Z suffix using a long-name option. */
+       assertEqualInt(0,
+           systemf("%s --auto-compress -cf test7.tar.Z f 2>test7.err",
+               testprog));
+       assertEmptyFile("test7.err");
+       /* Check that the archive file has a compress signature. */
+       p = slurpfile(&s, "test7.tar.Z");
+       assert(s > 2);
+       failure("The archive should be compressed");
+       assertEqualMem(p, "\x1f\x9d", 2);
+}
index 077f3625dff764de09795ecc78d3eefa87a2e7c0..a82b7fc06fc1a87e84a7777904455a0835a3561a 100644 (file)
@@ -141,6 +141,7 @@ void
 tar_mode_c(struct bsdtar *bsdtar)
 {
        struct archive *a;
+       const void *filter_name;
        int r;
 
        if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL)
@@ -149,15 +150,16 @@ tar_mode_c(struct bsdtar *bsdtar)
        a = archive_write_new();
 
        /* Support any format that the library supports. */
-       if (bsdtar->create_format == NULL) {
+       if (cset_get_format(bsdtar->cset) == NULL) {
                r = archive_write_set_format_pax_restricted(a);
-               bsdtar->create_format = "pax restricted";
+               cset_set_format(bsdtar->cset, "pax restricted");
        } else {
-               r = archive_write_set_format_by_name(a, bsdtar->create_format);
+               r = archive_write_set_format_by_name(a,
+                       cset_get_format(bsdtar->cset));
        }
        if (r != ARCHIVE_OK) {
                fprintf(stderr, "Can't use format %s: %s\n",
-                   bsdtar->create_format,
+                   cset_get_format(bsdtar->cset),
                    archive_error_string(a));
                usage();
        }
@@ -165,73 +167,10 @@ tar_mode_c(struct bsdtar *bsdtar)
        archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block);
        archive_write_set_bytes_in_last_block(a, bsdtar->bytes_in_last_block);
 
-       if (bsdtar->compress_program) {
-               archive_write_add_filter_program(a, bsdtar->compress_program);
-       } else {
-               const char *name = "?";
-
-               switch (bsdtar->create_compression) {
-               case 0:
-                       r = ARCHIVE_OK;
-                       break;
-               case OPTION_GRZIP:
-                       r = archive_write_add_filter_grzip(a);
-                       break;
-               case 'j': case 'y':
-                       r = archive_write_add_filter_bzip2(a);
-                       break;
-               case 'J':
-                       r = archive_write_add_filter_xz(a);
-                       break;
-               case OPTION_LRZIP:
-                       r = archive_write_add_filter_lrzip(a);
-                       break;
-               case OPTION_LZIP:
-                       r = archive_write_add_filter_lzip(a);
-                       break;
-               case OPTION_LZMA:
-                       r = archive_write_add_filter_lzma(a);
-                       break;
-               case OPTION_LZOP:
-                       r = archive_write_add_filter_lzop(a);
-                       break;
-               case 'z':
-                       r = archive_write_add_filter_gzip(a);
-                       break;
-               case 'Z':
-                       r = archive_write_add_filter_compress(a);
-                       break;
-               default:
-                       lafe_errc(1, 0,
-                           "Unrecognized compression option -%c",
-                           bsdtar->create_compression);
-               }
-               if (r < ARCHIVE_WARN) {
-                       lafe_errc(1, 0,
-                           "Unsupported compression option -%c",
-                           bsdtar->create_compression);
-               }
-               switch (bsdtar->add_filter) {
-               case 0:
-                       r = ARCHIVE_OK;
-                       break;
-               case OPTION_B64ENCODE:
-                       r = archive_write_add_filter_b64encode(a);
-                       name = "b64encode";
-                       break;
-               case OPTION_UUENCODE:
-                       r = archive_write_add_filter_uuencode(a);
-                       name = "uuencode";
-                       break;
-               default:
-                       lafe_errc(1, 0,
-                           "Unrecognized compression option -%c",
-                           bsdtar->add_filter);
-               }
-               if (r < ARCHIVE_WARN) {
-                       lafe_errc(1, 0,
-                           "Unsupported filter option --%s", name);
-               }
+       r = cset_write_add_filters(bsdtar->cset, a, &filter_name);
+       if (r < ARCHIVE_WARN) {
+               lafe_errc(1, 0, "Unsupported compression option --%s",
+                   (const char *)filter_name);
        }
 
        if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options))
@@ -302,17 +241,17 @@ tar_mode_r(struct bsdtar *bsdtar)
         * of arcane ugliness.
         */
 
-       if (bsdtar->create_format != NULL) {
+       if (cset_get_format(bsdtar->cset) != NULL) {
                /* If the user requested a format, use that, but ... */
                archive_write_set_format_by_name(a,
-                   bsdtar->create_format);
+                   cset_get_format(bsdtar->cset));
                /* ... complain if it's not compatible. */
                format &= ARCHIVE_FORMAT_BASE_MASK;
                if (format != (int)(archive_format(a) & ARCHIVE_FORMAT_BASE_MASK)
                    && format != ARCHIVE_FORMAT_EMPTY) {
                        lafe_errc(1, 0,
                            "Format %s is incompatible with the archive %s.",
-                           bsdtar->create_format, bsdtar->filename);
+                           cset_get_format(bsdtar->cset), bsdtar->filename);
                }
        } else {
                /*
@@ -1007,10 +946,6 @@ test_for_append(struct bsdtar *bsdtar)
        if (bsdtar->filename == NULL)
                lafe_errc(1, 0, "Cannot append to stdout.");
 
-       if (bsdtar->create_compression != 0)
-               lafe_errc(1, 0,
-                   "Cannot append to %s with compression", bsdtar->filename);
-
        if (stat(bsdtar->filename, &s) != 0)
                return;