/*- * 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" }, { ".lz4", "lz4" }, { ".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" }, { ".warc", "warc" }, { ".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); } free(name); 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); } }