Home | History | Annotate | Line # | Download | only in tar
      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 "lafe_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