From: Michihiro NAKAJIMA Date: Sun, 21 Oct 2012 08:05:28 +0000 (+0900) Subject: Introduce -a/--auto-compress option into bsdtar. X-Git-Tag: v3.1.0~40^2~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d06fc9e5e4a47d022188d4b10202873c5d995b6e;p=thirdparty%2Flibarchive.git Introduce -a/--auto-compress option into bsdtar. This automatically decides on a creation format and filters by the archive suffix. --- diff --git a/Makefile.am b/Makefile.am index 556c315bf..b37b7b8de 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 8927612cd..a57244d01 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -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 diff --git a/libarchive/archive.h b/libarchive/archive.h index 6ee542ced..1503b038a 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -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 index 000000000..e4cba4afa --- /dev/null +++ b/libarchive/archive_write_add_filter_by_name.c @@ -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 +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#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); +} diff --git a/tar/CMakeLists.txt b/tar/CMakeLists.txt index eddf7ecc1..46ce58b02 100644 --- a/tar/CMakeLists.txt +++ b/tar/CMakeLists.txt @@ -10,6 +10,7 @@ IF(ENABLE_TAR) bsdtar.h bsdtar_platform.h cmdline.c + creation_set.c read.c subst.c util.c diff --git a/tar/bsdtar.1 b/tar/bsdtar.1 index ff8665a4a..2297c3506 100644 --- a/tar/bsdtar.1 +++ b/tar/bsdtar.1 @@ -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 diff --git a/tar/bsdtar.c b/tar/bsdtar.c index 7f19d8376..99ce76441 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -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, diff --git a/tar/bsdtar.h b/tar/bsdtar.h index a21be3d0d..8ee465d50 100644 --- a/tar/bsdtar.h +++ b/tar/bsdtar.h @@ -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 **); + diff --git a/tar/cmdline.c b/tar/cmdline.c index e658ec867..659ae9546 100644 --- a/tar/cmdline.c +++ b/tar/cmdline.c @@ -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 index 000000000..f5cb75faf --- /dev/null +++ b/tar/creation_set.c @@ -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 +#endif +#ifdef HAVE_STRING_H +#include +#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); + } +} diff --git a/tar/read.c b/tar/read.c index 706077200..ba5c2e335 100644 --- a/tar/read.c +++ b/tar/read.c @@ -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)) diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt index a51dbbaa3..98f49e292 100644 --- a/tar/test/CMakeLists.txt +++ b/tar/test/CMakeLists.txt @@ -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 index 000000000..a000621cb --- /dev/null +++ b/tar/test/test_option_a.c @@ -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); +} diff --git a/tar/write.c b/tar/write.c index 077f3625d..a82b7fc06 100644 --- a/tar/write.c +++ b/tar/write.c @@ -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;