#include "private.h"
#ifdef __DJGPP__
# include <fcntl.h>
#endif
#if defined(TUKLIB_DOSLIKE) || defined(__VMS)
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
# ifdef _MSC_VER
# define suffix_strcmp _stricmp
# else
# define suffix_strcmp strcasecmp
# endif
#else
# define suffix_strcmp strcmp
#endif
static char *custom_suffix = NULL;
static bool
is_dir_sep(char c)
{
#ifdef TUKLIB_DOSLIKE
return c == '/' || c == '\\' || c == ':';
#else
return c == '/';
#endif
}
static bool
has_dir_sep(const char *str)
{
#ifdef TUKLIB_DOSLIKE
return strpbrk(str, "/\\:") != NULL;
#else
return strchr(str, '/') != NULL;
#endif
}
#ifdef __DJGPP__
static bool
has_sfn_suffix(const char *str, size_t len)
{
if (len >= 4 && str[len - 1] == '-' && str[len - 2] != '.'
&& !is_dir_sep(str[len - 2])) {
if (str[len - 3] == '.')
return !is_dir_sep(str[len - 4]);
if (len >= 5 && !is_dir_sep(str[len - 3])
&& str[len - 4] == '.')
return !is_dir_sep(str[len - 5]);
}
return false;
}
#endif
static size_t
test_suffix(const char *suffix, const char *src_name, size_t src_len)
{
const size_t suffix_len = strlen(suffix);
if (src_len <= suffix_len
|| is_dir_sep(src_name[src_len - suffix_len - 1]))
return 0;
if (suffix_strcmp(suffix, src_name + src_len - suffix_len) == 0)
return src_len - suffix_len;
return 0;
}
static char *
uncompressed_name(const char *src_name, const size_t src_len)
{
static const struct {
const char *compressed;
const char *uncompressed;
} suffixes[] = {
{ ".xz", "" },
{ ".txz", ".tar" }, { ".lzma", "" },
#ifdef __DJGPP__
{ ".lzm", "" },
#endif
{ ".tlz", ".tar" }, #ifdef HAVE_LZIP_DECODER
{ ".lz", "" },
#endif
};
const char *new_suffix = "";
size_t new_len = 0;
if (opt_format != FORMAT_RAW) {
for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
new_len = test_suffix(suffixes[i].compressed,
src_name, src_len);
if (new_len != 0) {
new_suffix = suffixes[i].uncompressed;
break;
}
}
#ifdef __DJGPP__
if (new_len == 0 && has_sfn_suffix(src_name, src_len)) {
new_suffix = "";
new_len = src_len - 1;
}
#endif
}
if (new_len == 0 && custom_suffix != NULL)
new_len = test_suffix(custom_suffix, src_name, src_len);
if (new_len == 0) {
message_warning(_("%s: Filename has an unknown suffix, "
"skipping"), src_name);
return NULL;
}
const size_t new_suffix_len = strlen(new_suffix);
char *dest_name = xmalloc(new_len + new_suffix_len + 1);
memcpy(dest_name, src_name, new_len);
memcpy(dest_name + new_len, new_suffix, new_suffix_len);
dest_name[new_len + new_suffix_len] = '\0';
return dest_name;
}
static void
msg_suffix(const char *src_name, const char *suffix)
{
message_warning(_("%s: File already has '%s' suffix, skipping"),
src_name, suffix);
return;
}
static char *
compressed_name(const char *src_name, size_t src_len)
{
static const char *const all_suffixes[][4] = {
{
".xz",
".txz",
NULL
}, {
".lzma",
#ifdef __DJGPP__
".lzm",
#endif
".tlz",
NULL
#ifdef HAVE_LZIP_DECODER
}, {
NULL
#endif
}, {
NULL
}
};
assert(opt_format != FORMAT_AUTO);
#ifdef HAVE_LZIP_DECODER
assert(opt_format != FORMAT_LZIP);
#endif
const size_t format = opt_format - 1;
const char *const *suffixes = all_suffixes[format];
for (size_t i = 0; suffixes[i] != NULL; ++i) {
if (test_suffix(suffixes[i], src_name, src_len) != 0) {
msg_suffix(src_name, suffixes[i]);
return NULL;
}
}
#ifdef __DJGPP__
if (opt_format == FORMAT_XZ && has_sfn_suffix(src_name, src_len)) {
msg_suffix(src_name, "-");
return NULL;
}
#endif
if (custom_suffix != NULL) {
if (test_suffix(custom_suffix, src_name, src_len) != 0) {
msg_suffix(src_name, custom_suffix);
return NULL;
}
}
const char *suffix = custom_suffix != NULL
? custom_suffix : suffixes[0];
size_t suffix_len = strlen(suffix);
#ifdef __DJGPP__
if (!_use_lfn(src_name)) {
const char *sufsep = strrchr(src_name, '.');
if (sufsep == NULL || sufsep[1] == '\0'
|| has_dir_sep(sufsep)) {
if (sufsep != NULL && sufsep[1] == '\0'
&& suffix[0] == '.')
--src_len;
} else if (custom_suffix == NULL
&& strcasecmp(sufsep, ".tar") == 0) {
static const char *const tar_suffixes[] = {
".txz", ".tlz",
};
suffix = tar_suffixes[format];
suffix_len = 4;
src_len -= 4;
} else {
if (custom_suffix == NULL && opt_format == FORMAT_XZ) {
suffix = "-";
suffix_len = 1;
}
if (suffix[0] == '.') {
src_len = sufsep - src_name;
} else {
if (suffix_len > 3)
suffix_len = 3;
if (strlen(sufsep) > 4 - suffix_len)
src_len = sufsep - src_name
+ 4 - suffix_len;
}
}
}
#endif
char *dest_name = xmalloc(src_len + suffix_len + 1);
memcpy(dest_name, src_name, src_len);
memcpy(dest_name + src_len, suffix, suffix_len);
dest_name[src_len + suffix_len] = '\0';
return dest_name;
}
extern char *
suffix_get_dest_name(const char *src_name)
{
assert(src_name != NULL);
const size_t src_len = strlen(src_name);
return opt_mode == MODE_COMPRESS
? compressed_name(src_name, src_len)
: uncompressed_name(src_name, src_len);
}
extern void
suffix_set(const char *suffix)
{
if (suffix[0] == '\0' || has_dir_sep(suffix))
message_fatal(_("%s: Invalid filename suffix"), suffix);
free(custom_suffix);
custom_suffix = xstrdup(suffix);
return;
}
extern bool
suffix_is_set(void)
{
return custom_suffix != NULL;
}