xref: /freebsd/contrib/libarchive/tar/creation_set.c (revision b1879975794772ee51f0b4865753364c7d7626c3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2012 Michihiro NAKAJIMA
5  * All rights reserved.
6  */
7 
8 #include "bsdtar_platform.h"
9 
10 #ifdef HAVE_STDLIB_H
11 #include <stdlib.h>
12 #endif
13 #ifdef HAVE_STRING_H
14 #include <string.h>
15 #endif
16 
17 #include "bsdtar.h"
18 #include "err.h"
19 
20 struct creation_set {
21 	char		 *create_format;
22 	struct filter_set {
23 		int	  program;	/* Set 1 if filter is a program name */
24 		char	 *filter_name;
25 	}		 *filters;
26 	int		  filter_count;
27 };
28 
29 struct suffix_code_t {
30 	const char *suffix;
31 	const char *form;
32 };
33 
34 static const char *
35 get_suffix_code(const struct suffix_code_t *tbl, const char *suffix)
36 {
37 	int i;
38 
39 	if (suffix == NULL)
40 		return (NULL);
41 	for (i = 0; tbl[i].suffix != NULL; i++) {
42 		if (strcmp(tbl[i].suffix, suffix) == 0)
43 			return (tbl[i].form);
44 	}
45 	return (NULL);
46 }
47 
48 static const char *
49 get_filter_code(const char *suffix)
50 {
51 	/* A pair of suffix and compression/filter. */
52 	static const struct suffix_code_t filters[] = {
53 		{ ".Z",		"compress" },
54 		{ ".bz2",	"bzip2" },
55 		{ ".gz",	"gzip" },
56 		{ ".grz",	"grzip" },
57 		{ ".lrz",	"lrzip" },
58 		{ ".lz",	"lzip" },
59 		{ ".lz4",	"lz4" },
60 		{ ".lzo",	"lzop" },
61 		{ ".lzma",	"lzma" },
62 		{ ".uu",	"uuencode" },
63 		{ ".xz",	"xz" },
64 		{ ".zst",	"zstd"},
65 		{ NULL,		NULL }
66 	};
67 
68 	return get_suffix_code(filters, suffix);
69 }
70 
71 static const char *
72 get_format_code(const char *suffix)
73 {
74 	/* A pair of suffix and format. */
75 	static const struct suffix_code_t formats[] = {
76 		{ ".7z",	"7zip" },
77 		{ ".ar",	"arbsd" },
78 		{ ".cpio",	"cpio" },
79 		{ ".iso",	"iso9660" },
80 		{ ".mtree",	"mtree" },
81 		{ ".shar",	"shar" },
82 		{ ".tar",	"paxr" },
83 		{ ".warc",	"warc" },
84 		{ ".xar",	"xar" },
85 		{ ".zip",	"zip" },
86 		{ NULL,		NULL }
87 	};
88 
89 	return get_suffix_code(formats, suffix);
90 }
91 
92 static const char *
93 decompose_alias(const char *suffix)
94 {
95 	static const struct suffix_code_t alias[] = {
96 		{ ".taz",	".tar.gz" },
97 		{ ".tgz",	".tar.gz" },
98 		{ ".tbz",	".tar.bz2" },
99 		{ ".tbz2",	".tar.bz2" },
100 		{ ".tz2",	".tar.bz2" },
101 		{ ".tlz",	".tar.lzma" },
102 		{ ".txz",	".tar.xz" },
103 		{ ".tzo",	".tar.lzo" },
104 		{ ".taZ",	".tar.Z" },
105 		{ ".tZ",	".tar.Z" },
106 		{ ".tzst",	".tar.zst" },
107 		{ NULL,		NULL }
108 	};
109 
110 	return get_suffix_code(alias, suffix);
111 }
112 
113 static void
114 _cset_add_filter(struct creation_set *cset, int program, const char *filter)
115 {
116 	struct filter_set *new_ptr;
117 	char *new_filter;
118 
119 	new_ptr = realloc(cset->filters,
120 	    sizeof(*cset->filters) * (cset->filter_count + 1));
121 	if (new_ptr == NULL)
122 		lafe_errc(1, 0, "No memory");
123 	new_filter = strdup(filter);
124 	if (new_filter == NULL)
125 		lafe_errc(1, 0, "No memory");
126 	cset->filters = new_ptr;
127 	cset->filters[cset->filter_count].program = program;
128 	cset->filters[cset->filter_count].filter_name = new_filter;
129 	cset->filter_count++;
130 }
131 
132 void
133 cset_add_filter(struct creation_set *cset, const char *filter)
134 {
135 	_cset_add_filter(cset, 0, filter);
136 }
137 
138 void
139 cset_add_filter_program(struct creation_set *cset, const char *filter)
140 {
141 	_cset_add_filter(cset, 1, filter);
142 }
143 
144 int
145 cset_read_support_filter_program(struct creation_set *cset, struct archive *a)
146 {
147 	int cnt = 0, i;
148 
149 	for (i = 0; i < cset->filter_count; i++) {
150 		if (cset->filters[i].program) {
151 			archive_read_support_filter_program(a,
152 			    cset->filters[i].filter_name);
153 			++cnt;
154 		}
155 	}
156 	return (cnt);
157 }
158 
159 int
160 cset_write_add_filters(struct creation_set *cset, struct archive *a,
161     const void **filter_name)
162 {
163 	int cnt = 0, i, r;
164 
165 	for (i = 0; i < cset->filter_count; i++) {
166 		if (cset->filters[i].program)
167 			r = archive_write_add_filter_program(a,
168 				cset->filters[i].filter_name);
169 		else
170 			r = archive_write_add_filter_by_name(a,
171 				cset->filters[i].filter_name);
172 		if (r < ARCHIVE_WARN) {
173 			*filter_name = cset->filters[i].filter_name;
174 			return (r);
175 		}
176 		++cnt;
177 	}
178 	return (cnt);
179 }
180 
181 void
182 cset_set_format(struct creation_set *cset, const char *format)
183 {
184 	char *f;
185 
186 	f = strdup(format);
187 	if (f == NULL)
188 		lafe_errc(1, 0, "No memory");
189 	free(cset->create_format);
190 	cset->create_format = f;
191 }
192 
193 const char *
194 cset_get_format(struct creation_set *cset)
195 {
196 	return (cset->create_format);
197 }
198 
199 static void
200 _cleanup_filters(struct filter_set *filters, int count)
201 {
202 	int i;
203 
204 	for (i = 0; i < count; i++)
205 		free(filters[i].filter_name);
206 	free(filters);
207 }
208 
209 /*
210  * Clean up a creation set.
211  */
212 void
213 cset_free(struct creation_set *cset)
214 {
215 	_cleanup_filters(cset->filters, cset->filter_count);
216 	free(cset->create_format);
217 	free(cset);
218 }
219 
220 struct creation_set *
221 cset_new(void)
222 {
223 	return calloc(1, sizeof(struct creation_set));
224 }
225 
226 /*
227  * Build a creation set by a file name suffix.
228  */
229 int
230 cset_auto_compress(struct creation_set *cset, const char *filename)
231 {
232 	struct filter_set *old_filters;
233 	char *name, *p;
234 	const char *code;
235 	int old_filter_count;
236 
237 	name = strdup(filename);
238 	if (name == NULL)
239 		lafe_errc(1, 0, "No memory");
240 	/* Save previous filters. */
241 	old_filters = cset->filters;
242 	old_filter_count = cset->filter_count;
243 	cset->filters = NULL;
244 	cset->filter_count = 0;
245 
246 	for (;;) {
247 		/* Get the suffix. */
248 		p = strrchr(name, '.');
249 		if (p == NULL)
250 			break;
251 		/* Suppose it indicates compression/filter type
252 		 * such as ".gz". */
253 		code = get_filter_code(p);
254 		if (code != NULL) {
255 			cset_add_filter(cset, code);
256 			*p = '\0';
257 			continue;
258 		}
259 		/* Suppose it indicates format type such as ".tar". */
260 		code = get_format_code(p);
261 		if (code != NULL) {
262 			cset_set_format(cset, code);
263 			break;
264 		}
265 		/* Suppose it indicates alias such as ".tgz". */
266 		code = decompose_alias(p);
267 		if (code == NULL)
268 			break;
269 		/* Replace the suffix. */
270 		*p = '\0';
271 		name = realloc(name, strlen(name) + strlen(code) + 1);
272 		if (name == NULL)
273 			lafe_errc(1, 0, "No memory");
274 		strcat(name, code);
275 	}
276 	free(name);
277 	if (cset->filters) {
278 		struct filter_set *v;
279 		int i, r;
280 
281 		/* Release previous filters. */
282 		_cleanup_filters(old_filters, old_filter_count);
283 
284 		v = malloc(sizeof(*v) * cset->filter_count);
285 		if (v == NULL)
286 			lafe_errc(1, 0, "No memory");
287 		/* Reverse filter sequence. */
288 		for (i = 0, r = cset->filter_count; r > 0; )
289 			v[i++] = cset->filters[--r];
290 		free(cset->filters);
291 		cset->filters = v;
292 		return (1);
293 	} else {
294 		/* Put previous filters back. */
295 		cset->filters = old_filters;
296 		cset->filter_count = old_filter_count;
297 		return (0);
298 	}
299 }
300