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