1248590Smm/*-
2248590Smm * Copyright (c) 2012 Michihiro NAKAJIMA
3248590Smm * All rights reserved.
4248590Smm *
5248590Smm * Redistribution and use in source and binary forms, with or without
6248590Smm * modification, are permitted provided that the following conditions
7248590Smm * are met:
8248590Smm * 1. Redistributions of source code must retain the above copyright
9248590Smm *    notice, this list of conditions and the following disclaimer.
10248590Smm * 2. Redistributions in binary form must reproduce the above copyright
11248590Smm *    notice, this list of conditions and the following disclaimer in the
12248590Smm *    documentation and/or other materials provided with the distribution.
13248590Smm *
14248590Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15248590Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16248590Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17248590Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18248590Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19248590Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20248590Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21248590Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22248590Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23248590Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24248590Smm */
25248590Smm
26248590Smm#include "bsdtar_platform.h"
27248590Smm__FBSDID("$FreeBSD$");
28248590Smm
29248590Smm#ifdef HAVE_STDLIB_H
30248590Smm#include <stdlib.h>
31248590Smm#endif
32248590Smm#ifdef HAVE_STRING_H
33248590Smm#include <string.h>
34248590Smm#endif
35248590Smm
36248590Smm#include "bsdtar.h"
37248590Smm#include "err.h"
38248590Smm
39248590Smmstruct creation_set {
40248590Smm	char		 *create_format;
41248590Smm	struct filter_set {
42248590Smm		int	  program;	/* Set 1 if filter is a program name */
43248590Smm		char	 *filter_name;
44248590Smm	}		 *filters;
45248590Smm	int		  filter_count;
46248590Smm};
47248590Smm
48248590Smmstruct suffix_code_t {
49248590Smm	const char *suffix;
50248590Smm	const char *form;
51248590Smm};
52248590Smm
53248590Smmstatic const char *
54248590Smmget_suffix_code(const struct suffix_code_t *tbl, const char *suffix)
55248590Smm{
56248590Smm	int i;
57248590Smm
58248590Smm	if (suffix == NULL)
59248590Smm		return (NULL);
60248590Smm	for (i = 0; tbl[i].suffix != NULL; i++) {
61248590Smm		if (strcmp(tbl[i].suffix, suffix) == 0)
62248590Smm			return (tbl[i].form);
63248590Smm	}
64248590Smm	return (NULL);
65248590Smm}
66248590Smm
67248590Smmstatic const char *
68248590Smmget_filter_code(const char *suffix)
69248590Smm{
70248590Smm	/* A pair of suffix and compression/filter. */
71248590Smm	static const struct suffix_code_t filters[] = {
72248590Smm		{ ".Z",		"compress" },
73248590Smm		{ ".bz2",	"bzip2" },
74248590Smm		{ ".gz",	"gzip" },
75248590Smm		{ ".grz",	"grzip" },
76248590Smm		{ ".lrz",	"lrzip" },
77248590Smm		{ ".lz",	"lzip" },
78299529Smm		{ ".lz4",	"lz4" },
79248590Smm		{ ".lzo",	"lzop" },
80248590Smm		{ ".lzma",	"lzma" },
81248590Smm		{ ".uu",	"uuencode" },
82248590Smm		{ ".xz",	"xz" },
83324417Smm		{ ".zst",	"zstd"},
84248590Smm		{ NULL,		NULL }
85248590Smm	};
86324417Smm
87248590Smm	return get_suffix_code(filters, suffix);
88248590Smm}
89248590Smm
90248590Smmstatic const char *
91248590Smmget_format_code(const char *suffix)
92248590Smm{
93248590Smm	/* A pair of suffix and format. */
94248590Smm	static const struct suffix_code_t formats[] = {
95248590Smm		{ ".7z",	"7zip" },
96248590Smm		{ ".ar",	"arbsd" },
97248590Smm		{ ".cpio",	"cpio" },
98248590Smm		{ ".iso",	"iso9960" },
99248590Smm		{ ".mtree",	"mtree" },
100248590Smm		{ ".shar",	"shar" },
101248590Smm		{ ".tar",	"paxr" },
102299529Smm		{ ".warc",	"warc" },
103248590Smm		{ ".xar",	"xar" },
104248590Smm		{ ".zip",	"zip" },
105248590Smm		{ NULL,		NULL }
106248590Smm	};
107248590Smm
108248590Smm	return get_suffix_code(formats, suffix);
109248590Smm}
110248590Smm
111248590Smmstatic const char *
112248590Smmdecompose_alias(const char *suffix)
113248590Smm{
114248590Smm	static const struct suffix_code_t alias[] = {
115248590Smm		{ ".taz",	".tar.gz" },
116248590Smm		{ ".tgz",	".tar.gz" },
117248590Smm		{ ".tbz",	".tar.bz2" },
118248590Smm		{ ".tbz2",	".tar.bz2" },
119248590Smm		{ ".tz2",	".tar.bz2" },
120248590Smm		{ ".tlz",	".tar.lzma" },
121248590Smm		{ ".txz",	".tar.xz" },
122248590Smm		{ ".tzo",	".tar.lzo" },
123248590Smm		{ ".taZ",	".tar.Z" },
124248590Smm		{ ".tZ",	".tar.Z" },
125324417Smm		{ ".tzst",	".tar.zst" },
126248590Smm		{ NULL,		NULL }
127248590Smm	};
128248590Smm
129248590Smm	return get_suffix_code(alias, suffix);
130248590Smm}
131248590Smm
132248590Smmstatic void
133248590Smm_cset_add_filter(struct creation_set *cset, int program, const char *filter)
134248590Smm{
135248590Smm	struct filter_set *new_ptr;
136248590Smm	char *new_filter;
137248590Smm
138248590Smm	new_ptr = (struct filter_set *)realloc(cset->filters,
139248590Smm	    sizeof(*cset->filters) * (cset->filter_count + 1));
140248590Smm	if (new_ptr == NULL)
141248590Smm		lafe_errc(1, 0, "No memory");
142248590Smm	new_filter = strdup(filter);
143248590Smm	if (new_filter == NULL)
144248590Smm		lafe_errc(1, 0, "No memory");
145248590Smm	cset->filters = new_ptr;
146248590Smm	cset->filters[cset->filter_count].program = program;
147248590Smm	cset->filters[cset->filter_count].filter_name = new_filter;
148248590Smm	cset->filter_count++;
149248590Smm}
150248590Smm
151248590Smmvoid
152248590Smmcset_add_filter(struct creation_set *cset, const char *filter)
153248590Smm{
154248590Smm	_cset_add_filter(cset, 0, filter);
155248590Smm}
156248590Smm
157248590Smmvoid
158248590Smmcset_add_filter_program(struct creation_set *cset, const char *filter)
159248590Smm{
160248590Smm	_cset_add_filter(cset, 1, filter);
161248590Smm}
162248590Smm
163248590Smmint
164248590Smmcset_read_support_filter_program(struct creation_set *cset, struct archive *a)
165248590Smm{
166248590Smm	int cnt = 0, i;
167248590Smm
168248590Smm	for (i = 0; i < cset->filter_count; i++) {
169248590Smm		if (cset->filters[i].program) {
170248590Smm			archive_read_support_filter_program(a,
171248590Smm			    cset->filters[i].filter_name);
172248590Smm			++cnt;
173248590Smm		}
174248590Smm	}
175248590Smm	return (cnt);
176248590Smm}
177248590Smm
178248590Smmint
179248590Smmcset_write_add_filters(struct creation_set *cset, struct archive *a,
180248590Smm    const void **filter_name)
181248590Smm{
182248590Smm	int cnt = 0, i, r;
183248590Smm
184248590Smm	for (i = 0; i < cset->filter_count; i++) {
185248590Smm		if (cset->filters[i].program)
186248590Smm			r = archive_write_add_filter_program(a,
187248590Smm				cset->filters[i].filter_name);
188248590Smm		else
189248590Smm			r = archive_write_add_filter_by_name(a,
190248590Smm				cset->filters[i].filter_name);
191248590Smm		if (r < ARCHIVE_WARN) {
192248590Smm			*filter_name = cset->filters[i].filter_name;
193248590Smm			return (r);
194248590Smm		}
195248590Smm		++cnt;
196248590Smm	}
197248590Smm	return (cnt);
198248590Smm}
199248590Smm
200248590Smmvoid
201248590Smmcset_set_format(struct creation_set *cset, const char *format)
202248590Smm{
203248590Smm	char *f;
204248590Smm
205248590Smm	f = strdup(format);
206248590Smm	if (f == NULL)
207248590Smm		lafe_errc(1, 0, "No memory");
208248590Smm	free(cset->create_format);
209248590Smm	cset->create_format = f;
210248590Smm}
211248590Smm
212248590Smmconst char *
213248590Smmcset_get_format(struct creation_set *cset)
214248590Smm{
215248590Smm	return (cset->create_format);
216248590Smm}
217248590Smm
218248590Smmstatic void
219248590Smm_cleanup_filters(struct filter_set *filters, int count)
220248590Smm{
221248590Smm	int i;
222248590Smm
223248590Smm	for (i = 0; i < count; i++)
224248590Smm		free(filters[i].filter_name);
225248590Smm	free(filters);
226248590Smm}
227248590Smm
228248590Smm/*
229248590Smm * Clean up a creation set.
230248590Smm */
231248590Smmvoid
232248590Smmcset_free(struct creation_set *cset)
233248590Smm{
234248590Smm	_cleanup_filters(cset->filters, cset->filter_count);
235248590Smm	free(cset->create_format);
236248590Smm	free(cset);
237248590Smm}
238248590Smm
239248590Smmstruct creation_set *
240248590Smmcset_new(void)
241248590Smm{
242248590Smm	return calloc(1, sizeof(struct creation_set));
243248590Smm}
244248590Smm
245248590Smm/*
246248590Smm * Build a creation set by a file name suffix.
247248590Smm */
248248590Smmint
249248590Smmcset_auto_compress(struct creation_set *cset, const char *filename)
250248590Smm{
251248590Smm	struct filter_set *old_filters;
252248590Smm	char *name, *p;
253248590Smm	const char *code;
254248590Smm	int old_filter_count;
255248590Smm
256248590Smm	name = strdup(filename);
257248590Smm	if (name == NULL)
258248590Smm		lafe_errc(1, 0, "No memory");
259248590Smm	/* Save previous filters. */
260248590Smm	old_filters = cset->filters;
261248590Smm	old_filter_count = cset->filter_count;
262248590Smm	cset->filters = NULL;
263248590Smm	cset->filter_count = 0;
264248590Smm
265248590Smm	for (;;) {
266248590Smm		/* Get the suffix. */
267248590Smm		p = strrchr(name, '.');
268248590Smm		if (p == NULL)
269248590Smm			break;
270248590Smm		/* Suppose it indicates compression/filter type
271248590Smm		 * such as ".gz". */
272248590Smm		code = get_filter_code(p);
273248590Smm		if (code != NULL) {
274248590Smm			cset_add_filter(cset, code);
275248590Smm			*p = '\0';
276248590Smm			continue;
277248590Smm		}
278248590Smm		/* Suppose it indicates format type such as ".tar". */
279248590Smm		code = get_format_code(p);
280248590Smm		if (code != NULL) {
281248590Smm			cset_set_format(cset, code);
282248590Smm			break;
283248590Smm		}
284248590Smm		/* Suppose it indicates alias such as ".tgz". */
285248590Smm		code = decompose_alias(p);
286248590Smm		if (code == NULL)
287248590Smm			break;
288248590Smm		/* Replace the suffix. */
289248590Smm		*p = '\0';
290248590Smm		name = realloc(name, strlen(name) + strlen(code) + 1);
291248590Smm		if (name == NULL)
292248590Smm			lafe_errc(1, 0, "No memory");
293248590Smm		strcat(name, code);
294248590Smm	}
295248590Smm	free(name);
296248590Smm	if (cset->filters) {
297248590Smm		struct filter_set *v;
298248590Smm		int i, r;
299248590Smm
300311041Smm		/* Release previous filters. */
301248590Smm		_cleanup_filters(old_filters, old_filter_count);
302248590Smm
303248590Smm		v = malloc(sizeof(*v) * cset->filter_count);
304248590Smm		if (v == NULL)
305248590Smm			lafe_errc(1, 0, "No memory");
306248590Smm		/* Reverse filter sequence. */
307248590Smm		for (i = 0, r = cset->filter_count; r > 0; )
308248590Smm			v[i++] = cset->filters[--r];
309248590Smm		free(cset->filters);
310248590Smm		cset->filters = v;
311248590Smm		return (1);
312248590Smm	} else {
313311041Smm		/* Put previous filters back. */
314248590Smm		cset->filters = old_filters;
315248590Smm		cset->filter_count = old_filter_count;
316248590Smm		return (0);
317248590Smm	}
318248590Smm}
319