| /*- |
| * 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" }, |
| { ".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 previous 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 previous filters back. */ |
| cset->filters = old_filters; |
| cset->filter_count = old_filter_count; |
| return (0); |
| } |
| } |