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
39 struct 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
48 struct suffix_code_t {
49 const char *suffix;
50 const char *form;
51 };
52
53 static const char *
get_suffix_code(const struct suffix_code_t * tbl,const char * suffix)54 get_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
67 static const char *
get_filter_code(const char * suffix)68 get_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
90 static const char *
get_format_code(const char * suffix)91 get_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", "iso9660" },
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
111 static const char *
decompose_alias(const char * suffix)112 decompose_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
132 static void
_cset_add_filter(struct creation_set * cset,int program,const char * filter)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
151 void
cset_add_filter(struct creation_set * cset,const char * filter)152 cset_add_filter(struct creation_set *cset, const char *filter)
153 {
154 _cset_add_filter(cset, 0, filter);
155 }
156
157 void
cset_add_filter_program(struct creation_set * cset,const char * filter)158 cset_add_filter_program(struct creation_set *cset, const char *filter)
159 {
160 _cset_add_filter(cset, 1, filter);
161 }
162
163 int
cset_read_support_filter_program(struct creation_set * cset,struct archive * a)164 cset_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
178 int
cset_write_add_filters(struct creation_set * cset,struct archive * a,const void ** filter_name)179 cset_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
200 void
cset_set_format(struct creation_set * cset,const char * format)201 cset_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
212 const char *
cset_get_format(struct creation_set * cset)213 cset_get_format(struct creation_set *cset)
214 {
215 return (cset->create_format);
216 }
217
218 static void
_cleanup_filters(struct filter_set * filters,int count)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 */
231 void
cset_free(struct creation_set * cset)232 cset_free(struct creation_set *cset)
233 {
234 _cleanup_filters(cset->filters, cset->filter_count);
235 free(cset->create_format);
236 free(cset);
237 }
238
239 struct creation_set *
cset_new(void)240 cset_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 */
248 int
cset_auto_compress(struct creation_set * cset,const char * filename)249 cset_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