creation_set.c revision 324417
1/*-
2 * Copyright (c) 2012 Michihiro NAKAJIMA
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "bsdtar_platform.h"
27__FBSDID("$FreeBSD$");
28
29#ifdef HAVE_STDLIB_H
30#include <stdlib.h>
31#endif
32#ifdef HAVE_STRING_H
33#include <string.h>
34#endif
35
36#include "bsdtar.h"
37#include "err.h"
38
39struct creation_set {
40	char		 *create_format;
41	struct filter_set {
42		int	  program;	/* Set 1 if filter is a program name */
43		char	 *filter_name;
44	}		 *filters;
45	int		  filter_count;
46};
47
48struct suffix_code_t {
49	const char *suffix;
50	const char *form;
51};
52
53static const char *
54get_suffix_code(const struct suffix_code_t *tbl, const char *suffix)
55{
56	int i;
57
58	if (suffix == NULL)
59		return (NULL);
60	for (i = 0; tbl[i].suffix != NULL; i++) {
61		if (strcmp(tbl[i].suffix, suffix) == 0)
62			return (tbl[i].form);
63	}
64	return (NULL);
65}
66
67static const char *
68get_filter_code(const char *suffix)
69{
70	/* A pair of suffix and compression/filter. */
71	static const struct suffix_code_t filters[] = {
72		{ ".Z",		"compress" },
73		{ ".bz2",	"bzip2" },
74		{ ".gz",	"gzip" },
75		{ ".grz",	"grzip" },
76		{ ".lrz",	"lrzip" },
77		{ ".lz",	"lzip" },
78		{ ".lz4",	"lz4" },
79		{ ".lzo",	"lzop" },
80		{ ".lzma",	"lzma" },
81		{ ".uu",	"uuencode" },
82		{ ".xz",	"xz" },
83		{ ".zst",	"zstd"},
84		{ NULL,		NULL }
85	};
86
87	return get_suffix_code(filters, suffix);
88}
89
90static const char *
91get_format_code(const char *suffix)
92{
93	/* A pair of suffix and format. */
94	static const struct suffix_code_t formats[] = {
95		{ ".7z",	"7zip" },
96		{ ".ar",	"arbsd" },
97		{ ".cpio",	"cpio" },
98		{ ".iso",	"iso9960" },
99		{ ".mtree",	"mtree" },
100		{ ".shar",	"shar" },
101		{ ".tar",	"paxr" },
102		{ ".warc",	"warc" },
103		{ ".xar",	"xar" },
104		{ ".zip",	"zip" },
105		{ NULL,		NULL }
106	};
107
108	return get_suffix_code(formats, suffix);
109}
110
111static const char *
112decompose_alias(const char *suffix)
113{
114	static const struct suffix_code_t alias[] = {
115		{ ".taz",	".tar.gz" },
116		{ ".tgz",	".tar.gz" },
117		{ ".tbz",	".tar.bz2" },
118		{ ".tbz2",	".tar.bz2" },
119		{ ".tz2",	".tar.bz2" },
120		{ ".tlz",	".tar.lzma" },
121		{ ".txz",	".tar.xz" },
122		{ ".tzo",	".tar.lzo" },
123		{ ".taZ",	".tar.Z" },
124		{ ".tZ",	".tar.Z" },
125		{ ".tzst",	".tar.zst" },
126		{ NULL,		NULL }
127	};
128
129	return get_suffix_code(alias, suffix);
130}
131
132static void
133_cset_add_filter(struct creation_set *cset, int program, const char *filter)
134{
135	struct filter_set *new_ptr;
136	char *new_filter;
137
138	new_ptr = (struct filter_set *)realloc(cset->filters,
139	    sizeof(*cset->filters) * (cset->filter_count + 1));
140	if (new_ptr == NULL)
141		lafe_errc(1, 0, "No memory");
142	new_filter = strdup(filter);
143	if (new_filter == NULL)
144		lafe_errc(1, 0, "No memory");
145	cset->filters = new_ptr;
146	cset->filters[cset->filter_count].program = program;
147	cset->filters[cset->filter_count].filter_name = new_filter;
148	cset->filter_count++;
149}
150
151void
152cset_add_filter(struct creation_set *cset, const char *filter)
153{
154	_cset_add_filter(cset, 0, filter);
155}
156
157void
158cset_add_filter_program(struct creation_set *cset, const char *filter)
159{
160	_cset_add_filter(cset, 1, filter);
161}
162
163int
164cset_read_support_filter_program(struct creation_set *cset, struct archive *a)
165{
166	int cnt = 0, i;
167
168	for (i = 0; i < cset->filter_count; i++) {
169		if (cset->filters[i].program) {
170			archive_read_support_filter_program(a,
171			    cset->filters[i].filter_name);
172			++cnt;
173		}
174	}
175	return (cnt);
176}
177
178int
179cset_write_add_filters(struct creation_set *cset, struct archive *a,
180    const void **filter_name)
181{
182	int cnt = 0, i, r;
183
184	for (i = 0; i < cset->filter_count; i++) {
185		if (cset->filters[i].program)
186			r = archive_write_add_filter_program(a,
187				cset->filters[i].filter_name);
188		else
189			r = archive_write_add_filter_by_name(a,
190				cset->filters[i].filter_name);
191		if (r < ARCHIVE_WARN) {
192			*filter_name = cset->filters[i].filter_name;
193			return (r);
194		}
195		++cnt;
196	}
197	return (cnt);
198}
199
200void
201cset_set_format(struct creation_set *cset, const char *format)
202{
203	char *f;
204
205	f = strdup(format);
206	if (f == NULL)
207		lafe_errc(1, 0, "No memory");
208	free(cset->create_format);
209	cset->create_format = f;
210}
211
212const char *
213cset_get_format(struct creation_set *cset)
214{
215	return (cset->create_format);
216}
217
218static void
219_cleanup_filters(struct filter_set *filters, int count)
220{
221	int i;
222
223	for (i = 0; i < count; i++)
224		free(filters[i].filter_name);
225	free(filters);
226}
227
228/*
229 * Clean up a creation set.
230 */
231void
232cset_free(struct creation_set *cset)
233{
234	_cleanup_filters(cset->filters, cset->filter_count);
235	free(cset->create_format);
236	free(cset);
237}
238
239struct creation_set *
240cset_new(void)
241{
242	return calloc(1, sizeof(struct creation_set));
243}
244
245/*
246 * Build a creation set by a file name suffix.
247 */
248int
249cset_auto_compress(struct creation_set *cset, const char *filename)
250{
251	struct filter_set *old_filters;
252	char *name, *p;
253	const char *code;
254	int old_filter_count;
255
256	name = strdup(filename);
257	if (name == NULL)
258		lafe_errc(1, 0, "No memory");
259	/* Save previous filters. */
260	old_filters = cset->filters;
261	old_filter_count = cset->filter_count;
262	cset->filters = NULL;
263	cset->filter_count = 0;
264
265	for (;;) {
266		/* Get the suffix. */
267		p = strrchr(name, '.');
268		if (p == NULL)
269			break;
270		/* Suppose it indicates compression/filter type
271		 * such as ".gz". */
272		code = get_filter_code(p);
273		if (code != NULL) {
274			cset_add_filter(cset, code);
275			*p = '\0';
276			continue;
277		}
278		/* Suppose it indicates format type such as ".tar". */
279		code = get_format_code(p);
280		if (code != NULL) {
281			cset_set_format(cset, code);
282			break;
283		}
284		/* Suppose it indicates alias such as ".tgz". */
285		code = decompose_alias(p);
286		if (code == NULL)
287			break;
288		/* Replace the suffix. */
289		*p = '\0';
290		name = realloc(name, strlen(name) + strlen(code) + 1);
291		if (name == NULL)
292			lafe_errc(1, 0, "No memory");
293		strcat(name, code);
294	}
295	free(name);
296	if (cset->filters) {
297		struct filter_set *v;
298		int i, r;
299
300		/* Release previous filters. */
301		_cleanup_filters(old_filters, old_filter_count);
302
303		v = malloc(sizeof(*v) * cset->filter_count);
304		if (v == NULL)
305			lafe_errc(1, 0, "No memory");
306		/* Reverse filter sequence. */
307		for (i = 0, r = cset->filter_count; r > 0; )
308			v[i++] = cset->filters[--r];
309		free(cset->filters);
310		cset->filters = v;
311		return (1);
312	} else {
313		/* Put previous filters back. */
314		cset->filters = old_filters;
315		cset->filter_count = old_filter_count;
316		return (0);
317	}
318}
319