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 * 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 * 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 { 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", "iso9960" }, 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 { NULL, NULL } 125 }; 126 127 return get_suffix_code(alias, suffix); 128 } 129 130 static void 131 _cset_add_filter(struct creation_set *cset, int program, const char *filter) 132 { 133 struct filter_set *new_ptr; 134 char *new_filter; 135 136 new_ptr = (struct filter_set *)realloc(cset->filters, 137 sizeof(*cset->filters) * (cset->filter_count + 1)); 138 if (new_ptr == NULL) 139 lafe_errc(1, 0, "No memory"); 140 new_filter = strdup(filter); 141 if (new_filter == NULL) 142 lafe_errc(1, 0, "No memory"); 143 cset->filters = new_ptr; 144 cset->filters[cset->filter_count].program = program; 145 cset->filters[cset->filter_count].filter_name = new_filter; 146 cset->filter_count++; 147 } 148 149 void 150 cset_add_filter(struct creation_set *cset, const char *filter) 151 { 152 _cset_add_filter(cset, 0, filter); 153 } 154 155 void 156 cset_add_filter_program(struct creation_set *cset, const char *filter) 157 { 158 _cset_add_filter(cset, 1, filter); 159 } 160 161 int 162 cset_read_support_filter_program(struct creation_set *cset, struct archive *a) 163 { 164 int cnt = 0, i; 165 166 for (i = 0; i < cset->filter_count; i++) { 167 if (cset->filters[i].program) { 168 archive_read_support_filter_program(a, 169 cset->filters[i].filter_name); 170 ++cnt; 171 } 172 } 173 return (cnt); 174 } 175 176 int 177 cset_write_add_filters(struct creation_set *cset, struct archive *a, 178 const void **filter_name) 179 { 180 int cnt = 0, i, r; 181 182 for (i = 0; i < cset->filter_count; i++) { 183 if (cset->filters[i].program) 184 r = archive_write_add_filter_program(a, 185 cset->filters[i].filter_name); 186 else 187 r = archive_write_add_filter_by_name(a, 188 cset->filters[i].filter_name); 189 if (r < ARCHIVE_WARN) { 190 *filter_name = cset->filters[i].filter_name; 191 return (r); 192 } 193 ++cnt; 194 } 195 return (cnt); 196 } 197 198 void 199 cset_set_format(struct creation_set *cset, const char *format) 200 { 201 char *f; 202 203 f = strdup(format); 204 if (f == NULL) 205 lafe_errc(1, 0, "No memory"); 206 free(cset->create_format); 207 cset->create_format = f; 208 } 209 210 const char * 211 cset_get_format(struct creation_set *cset) 212 { 213 return (cset->create_format); 214 } 215 216 static void 217 _cleanup_filters(struct filter_set *filters, int count) 218 { 219 int i; 220 221 for (i = 0; i < count; i++) 222 free(filters[i].filter_name); 223 free(filters); 224 } 225 226 /* 227 * Clean up a creation set. 228 */ 229 void 230 cset_free(struct creation_set *cset) 231 { 232 _cleanup_filters(cset->filters, cset->filter_count); 233 free(cset->create_format); 234 free(cset); 235 } 236 237 struct creation_set * 238 cset_new(void) 239 { 240 return calloc(1, sizeof(struct creation_set)); 241 } 242 243 /* 244 * Build a creation set by a file name suffix. 245 */ 246 int 247 cset_auto_compress(struct creation_set *cset, const char *filename) 248 { 249 struct filter_set *old_filters; 250 char *name, *p; 251 const char *code; 252 int old_filter_count; 253 254 name = strdup(filename); 255 if (name == NULL) 256 lafe_errc(1, 0, "No memory"); 257 /* Save previous filters. */ 258 old_filters = cset->filters; 259 old_filter_count = cset->filter_count; 260 cset->filters = NULL; 261 cset->filter_count = 0; 262 263 for (;;) { 264 /* Get the suffix. */ 265 p = strrchr(name, '.'); 266 if (p == NULL) 267 break; 268 /* Suppose it indicates compression/filter type 269 * such as ".gz". */ 270 code = get_filter_code(p); 271 if (code != NULL) { 272 cset_add_filter(cset, code); 273 *p = '\0'; 274 continue; 275 } 276 /* Suppose it indicates format type such as ".tar". */ 277 code = get_format_code(p); 278 if (code != NULL) { 279 cset_set_format(cset, code); 280 break; 281 } 282 /* Suppose it indicates alias such as ".tgz". */ 283 code = decompose_alias(p); 284 if (code == NULL) 285 break; 286 /* Replace the suffix. */ 287 *p = '\0'; 288 name = realloc(name, strlen(name) + strlen(code) + 1); 289 if (name == NULL) 290 lafe_errc(1, 0, "No memory"); 291 strcat(name, code); 292 } 293 free(name); 294 if (cset->filters) { 295 struct filter_set *v; 296 int i, r; 297 298 /* Release previous filters. */ 299 _cleanup_filters(old_filters, old_filter_count); 300 301 v = malloc(sizeof(*v) * cset->filter_count); 302 if (v == NULL) 303 lafe_errc(1, 0, "No memory"); 304 /* Reverse filter sequence. */ 305 for (i = 0, r = cset->filter_count; r > 0; ) 306 v[i++] = cset->filters[--r]; 307 free(cset->filters); 308 cset->filters = v; 309 return (1); 310 } else { 311 /* Put previous filters back. */ 312 cset->filters = old_filters; 313 cset->filter_count = old_filter_count; 314 return (0); 315 } 316 } 317