007dbc1f18
Sync libarchive with vendor. Small improvements, style fixes, bugfixes. Restores compatibility with tar archives created with Perl Archive::Tar (1) MFC after: 1 week Reported by: Matthew Seaman <matthew@freebsd.org> (1)
317 lines
7.2 KiB
C
317 lines
7.2 KiB
C
/*-
|
|
* 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);
|
|
}
|
|
}
|