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