creation_set.c revision 299529
119370Spst/*-
219370Spst * Copyright (c) 2012 Michihiro NAKAJIMA
319370Spst * All rights reserved.
419370Spst *
519370Spst * Redistribution and use in source and binary forms, with or without
619370Spst * modification, are permitted provided that the following conditions
719370Spst * are met:
819370Spst * 1. Redistributions of source code must retain the above copyright
919370Spst *    notice, this list of conditions and the following disclaimer.
1019370Spst * 2. Redistributions in binary form must reproduce the above copyright
1119370Spst *    notice, this list of conditions and the following disclaimer in the
1219370Spst *    documentation and/or other materials provided with the distribution.
1319370Spst *
1419370Spst * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
1519370Spst * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1619370Spst * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1719370Spst * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
1819370Spst * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1919370Spst * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2019370Spst * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2119370Spst * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2219370Spst * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2319370Spst * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2419370Spst */
2519370Spst
2619370Spst#include "bsdtar_platform.h"
2719370Spst__FBSDID("$FreeBSD$");
2819370Spst
2919370Spst#ifdef HAVE_STDLIB_H
3019370Spst#include <stdlib.h>
3119370Spst#endif
3219370Spst#ifdef HAVE_STRING_H
3319370Spst#include <string.h>
3419370Spst#endif
3519370Spst
3619370Spst#include "bsdtar.h"
3719370Spst#include "err.h"
3819370Spst
3919370Spststruct creation_set {
4019370Spst	char		 *create_format;
4119370Spst	struct filter_set {
4219370Spst		int	  program;	/* Set 1 if filter is a program name */
4319370Spst		char	 *filter_name;
4419370Spst	}		 *filters;
4519370Spst	int		  filter_count;
4619370Spst};
4798944Sobrien
4898944Sobrienstruct suffix_code_t {
4998944Sobrien	const char *suffix;
5098944Sobrien	const char *form;
5198944Sobrien};
5298944Sobrien
5398944Sobrienstatic const char *
5498944Sobrienget_suffix_code(const struct suffix_code_t *tbl, const char *suffix)
5519370Spst{
5619370Spst	int i;
5719370Spst
5819370Spst	if (suffix == NULL)
5919370Spst		return (NULL);
6019370Spst	for (i = 0; tbl[i].suffix != NULL; i++) {
6119370Spst		if (strcmp(tbl[i].suffix, suffix) == 0)
6219370Spst			return (tbl[i].form);
6319370Spst	}
6419370Spst	return (NULL);
6519370Spst}
6619370Spst
6719370Spststatic const char *
6819370Spstget_filter_code(const char *suffix)
6919370Spst{
7019370Spst	/* A pair of suffix and compression/filter. */
7119370Spst	static const struct suffix_code_t filters[] = {
7219370Spst		{ ".Z",		"compress" },
7319370Spst		{ ".bz2",	"bzip2" },
7419370Spst		{ ".gz",	"gzip" },
7519370Spst		{ ".grz",	"grzip" },
7619370Spst		{ ".lrz",	"lrzip" },
7719370Spst		{ ".lz",	"lzip" },
7898944Sobrien		{ ".lz4",	"lz4" },
7919370Spst		{ ".lzo",	"lzop" },
8098944Sobrien		{ ".lzma",	"lzma" },
8198944Sobrien		{ ".uu",	"uuencode" },
8298944Sobrien		{ ".xz",	"xz" },
8398944Sobrien		{ NULL,		NULL }
8498944Sobrien	};
8598944Sobrien
8698944Sobrien	return get_suffix_code(filters, suffix);
8798944Sobrien}
8898944Sobrien
8919370Spststatic const char *
9019370Spstget_format_code(const char *suffix)
9119370Spst{
9219370Spst	/* A pair of suffix and format. */
9319370Spst	static const struct suffix_code_t formats[] = {
9419370Spst		{ ".7z",	"7zip" },
9519370Spst		{ ".ar",	"arbsd" },
9698944Sobrien		{ ".cpio",	"cpio" },
9798944Sobrien		{ ".iso",	"iso9960" },
9898944Sobrien		{ ".mtree",	"mtree" },
9919370Spst		{ ".shar",	"shar" },
10098944Sobrien		{ ".tar",	"paxr" },
10198944Sobrien		{ ".warc",	"warc" },
10298944Sobrien		{ ".xar",	"xar" },
10398944Sobrien		{ ".zip",	"zip" },
10419370Spst		{ NULL,		NULL }
10598944Sobrien	};
10698944Sobrien
10719370Spst	return get_suffix_code(formats, suffix);
10898944Sobrien}
10998944Sobrien
11098944Sobrienstatic const char *
11119370Spstdecompose_alias(const char *suffix)
11219370Spst{
11319370Spst	static const struct suffix_code_t alias[] = {
11419370Spst		{ ".taz",	".tar.gz" },
11519370Spst		{ ".tgz",	".tar.gz" },
11619370Spst		{ ".tbz",	".tar.bz2" },
11719370Spst		{ ".tbz2",	".tar.bz2" },
11819370Spst		{ ".tz2",	".tar.bz2" },
11919370Spst		{ ".tlz",	".tar.lzma" },
12019370Spst		{ ".txz",	".tar.xz" },
12119370Spst		{ ".tzo",	".tar.lzo" },
12219370Spst		{ ".taZ",	".tar.Z" },
12319370Spst		{ ".tZ",	".tar.Z" },
12419370Spst		{ NULL,		NULL }
12519370Spst	};
12619370Spst
12719370Spst	return get_suffix_code(alias, suffix);
12819370Spst}
12919370Spst
13019370Spststatic void
13119370Spst_cset_add_filter(struct creation_set *cset, int program, const char *filter)
13219370Spst{
13319370Spst	struct filter_set *new_ptr;
13419370Spst	char *new_filter;
13519370Spst
13619370Spst	new_ptr = (struct filter_set *)realloc(cset->filters,
13719370Spst	    sizeof(*cset->filters) * (cset->filter_count + 1));
13819370Spst	if (new_ptr == NULL)
139		lafe_errc(1, 0, "No memory");
140	new_filter = strdup(filter);
141	if (new_filter == NULL)
142		lafe_errc(1, 0, "No memory");
143	cset->filters = new_ptr;
144	cset->filters[cset->filter_count].program = program;
145	cset->filters[cset->filter_count].filter_name = new_filter;
146	cset->filter_count++;
147}
148
149void
150cset_add_filter(struct creation_set *cset, const char *filter)
151{
152	_cset_add_filter(cset, 0, filter);
153}
154
155void
156cset_add_filter_program(struct creation_set *cset, const char *filter)
157{
158	_cset_add_filter(cset, 1, filter);
159}
160
161int
162cset_read_support_filter_program(struct creation_set *cset, struct archive *a)
163{
164	int cnt = 0, i;
165
166	for (i = 0; i < cset->filter_count; i++) {
167		if (cset->filters[i].program) {
168			archive_read_support_filter_program(a,
169			    cset->filters[i].filter_name);
170			++cnt;
171		}
172	}
173	return (cnt);
174}
175
176int
177cset_write_add_filters(struct creation_set *cset, struct archive *a,
178    const void **filter_name)
179{
180	int cnt = 0, i, r;
181
182	for (i = 0; i < cset->filter_count; i++) {
183		if (cset->filters[i].program)
184			r = archive_write_add_filter_program(a,
185				cset->filters[i].filter_name);
186		else
187			r = archive_write_add_filter_by_name(a,
188				cset->filters[i].filter_name);
189		if (r < ARCHIVE_WARN) {
190			*filter_name = cset->filters[i].filter_name;
191			return (r);
192		}
193		++cnt;
194	}
195	return (cnt);
196}
197
198void
199cset_set_format(struct creation_set *cset, const char *format)
200{
201	char *f;
202
203	f = strdup(format);
204	if (f == NULL)
205		lafe_errc(1, 0, "No memory");
206	free(cset->create_format);
207	cset->create_format = f;
208}
209
210const char *
211cset_get_format(struct creation_set *cset)
212{
213	return (cset->create_format);
214}
215
216static void
217_cleanup_filters(struct filter_set *filters, int count)
218{
219	int i;
220
221	for (i = 0; i < count; i++)
222		free(filters[i].filter_name);
223	free(filters);
224}
225
226/*
227 * Clean up a creation set.
228 */
229void
230cset_free(struct creation_set *cset)
231{
232	_cleanup_filters(cset->filters, cset->filter_count);
233	free(cset->create_format);
234	free(cset);
235}
236
237struct creation_set *
238cset_new(void)
239{
240	return calloc(1, sizeof(struct creation_set));
241}
242
243/*
244 * Build a creation set by a file name suffix.
245 */
246int
247cset_auto_compress(struct creation_set *cset, const char *filename)
248{
249	struct filter_set *old_filters;
250	char *name, *p;
251	const char *code;
252	int old_filter_count;
253
254	name = strdup(filename);
255	if (name == NULL)
256		lafe_errc(1, 0, "No memory");
257	/* Save previous filters. */
258	old_filters = cset->filters;
259	old_filter_count = cset->filter_count;
260	cset->filters = NULL;
261	cset->filter_count = 0;
262
263	for (;;) {
264		/* Get the suffix. */
265		p = strrchr(name, '.');
266		if (p == NULL)
267			break;
268		/* Suppose it indicates compression/filter type
269		 * such as ".gz". */
270		code = get_filter_code(p);
271		if (code != NULL) {
272			cset_add_filter(cset, code);
273			*p = '\0';
274			continue;
275		}
276		/* Suppose it indicates format type such as ".tar". */
277		code = get_format_code(p);
278		if (code != NULL) {
279			cset_set_format(cset, code);
280			break;
281		}
282		/* Suppose it indicates alias such as ".tgz". */
283		code = decompose_alias(p);
284		if (code == NULL)
285			break;
286		/* Replace the suffix. */
287		*p = '\0';
288		name = realloc(name, strlen(name) + strlen(code) + 1);
289		if (name == NULL)
290			lafe_errc(1, 0, "No memory");
291		strcat(name, code);
292	}
293	free(name);
294	if (cset->filters) {
295		struct filter_set *v;
296		int i, r;
297
298		/* Release previos filters. */
299		_cleanup_filters(old_filters, old_filter_count);
300
301		v = malloc(sizeof(*v) * cset->filter_count);
302		if (v == NULL)
303			lafe_errc(1, 0, "No memory");
304		/* Reverse filter sequence. */
305		for (i = 0, r = cset->filter_count; r > 0; )
306			v[i++] = cset->filters[--r];
307		free(cset->filters);
308		cset->filters = v;
309		return (1);
310	} else {
311		/* Put previos filters back. */
312		cset->filters = old_filters;
313		cset->filter_count = old_filter_count;
314		return (0);
315	}
316}
317