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 *
get_suffix_code(const struct suffix_code_t * tbl,const char * suffix)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 *
get_filter_code(const char * suffix)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 *
get_format_code(const char * suffix)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 *
decompose_alias(const char * suffix)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
_cset_add_filter(struct creation_set * cset,int program,const char * filter)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
cset_add_filter(struct creation_set * cset,const char * filter)133 cset_add_filter(struct creation_set *cset, const char *filter)
134 {
135 _cset_add_filter(cset, 0, filter);
136 }
137
138 void
cset_add_filter_program(struct creation_set * cset,const char * filter)139 cset_add_filter_program(struct creation_set *cset, const char *filter)
140 {
141 _cset_add_filter(cset, 1, filter);
142 }
143
144 int
cset_read_support_filter_program(struct creation_set * cset,struct archive * a)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
cset_write_add_filters(struct creation_set * cset,struct archive * a,const void ** filter_name)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
cset_set_format(struct creation_set * cset,const char * format)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 *
cset_get_format(struct creation_set * cset)194 cset_get_format(struct creation_set *cset)
195 {
196 return (cset->create_format);
197 }
198
199 static void
_cleanup_filters(struct filter_set * filters,int count)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
cset_free(struct creation_set * cset)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 *
cset_new(void)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
cset_auto_compress(struct creation_set * cset,const char * filename)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