/// \brief Removes the filename suffix of the compressed file /// /// \return Name of the uncompressed file, or NULL if file has unknown /// suffix. 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" }, // .txz abbreviation for .txt.gz is rare. { ".lzma", "" }, { ".tlz", ".tar" }, // { ".gz", "" }, // { ".tgz", ".tar" }, }; const char *new_suffix = ""; size_t new_len = 0; if (opt_format == FORMAT_RAW) { // Don't check for known suffixes when --format=raw was used. if (custom_suffix == NULL) { message_error(_("%s: With --format=raw, " "--suffix=.SUF is required unless " "writing to stdout"), src_name); return NULL; } } else { 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; } } } 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; }
int main(int, char**) { plan_tests(69); test_integrals(); test_strings(); test_pointers(); test_double(); test_concat(); test_suffix(); test_to(); test_path_cat(); test_basename_dirname(); test_operator(); test_split(); return exit_status(); }
/// \brief Appends suffix to src_name /// /// In contrast to uncompressed_name(), we check only suffixes that are valid /// for the specified file format. static char * compressed_name(const char *src_name, const size_t src_len) { // The order of these must match the order in args.h. static const char *const all_suffixes[][3] = { { ".xz", ".txz", NULL }, { ".lzma", ".tlz", NULL /* }, { ".gz", ".tgz", NULL */ }, { // --format=raw requires specifying the suffix // manually or using stdout. NULL } }; // args.c ensures this. assert(opt_format != FORMAT_AUTO); 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) { message_warning(_("%s: File already has `%s' " "suffix, skipping"), src_name, suffixes[i]); return NULL; } } if (custom_suffix != NULL) { if (test_suffix(custom_suffix, src_name, src_len) != 0) { message_warning(_("%s: File already has `%s' " "suffix, skipping"), src_name, custom_suffix); return NULL; } } // TODO: Hmm, maybe it would be better to validate this in args.c, // since the suffix handling when decoding is weird now. if (opt_format == FORMAT_RAW && custom_suffix == NULL) { message_error(_("%s: With --format=raw, " "--suffix=.SUF is required unless " "writing to stdout"), src_name); return NULL; } const char *suffix = custom_suffix != NULL ? custom_suffix : suffixes[0]; const size_t suffix_len = strlen(suffix); 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; }
/// \brief Appends suffix to src_name /// /// In contrast to uncompressed_name(), we check only suffixes that are valid /// for the specified file format. static char * compressed_name(const char *src_name, size_t src_len) { // The order of these must match the order in args.h. static const char *const all_suffixes[][4] = { { ".xz", ".txz", NULL }, { ".lzma", #ifdef __DJGPP__ ".lzm", #endif ".tlz", NULL /* }, { ".gz", ".tgz", NULL */ }, { // --format=raw requires specifying the suffix // manually or using stdout. NULL } }; // args.c ensures this. assert(opt_format != FORMAT_AUTO); const size_t format = opt_format - 1; const char *const *suffixes = all_suffixes[format]; // Look for known filename suffixes and refuse to compress them. 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__ // Recognize also the special suffix that is used when long // filename (LFN) support isn't available. This suffix is // recognized on LFN systems too. 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; } } // TODO: Hmm, maybe it would be better to validate this in args.c, // since the suffix handling when decoding is weird now. if (opt_format == FORMAT_RAW && custom_suffix == NULL) { message_error(_("%s: With --format=raw, " "--suffix=.SUF is required unless " "writing to stdout"), src_name); 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)) { // Long filename (LFN) support isn't available and we are // limited to 8.3 short filenames (SFN). // // Look for suffix separator from the filename, and make sure // that it is in the filename, not in a directory name. const char *sufsep = strrchr(src_name, '.'); if (sufsep == NULL || sufsep[1] == '\0' || has_dir_sep(sufsep)) { // src_name has no filename extension. // // Examples: // xz foo -> foo.xz // xz -F lzma foo -> foo.lzm // xz -S x foo -> foox // xz -S x foo. -> foo.x // xz -S x.y foo -> foox.y // xz -S .x foo -> foo.x // xz -S .x foo. -> foo.x // // Avoid double dots: if (sufsep != NULL && sufsep[1] == '\0' && suffix[0] == '.') --src_len; } else if (custom_suffix == NULL && strcasecmp(sufsep, ".tar") == 0) { // ".tar" is handled specially. // // Examples: // xz foo.tar -> foo.txz // xz -F lzma foo.tar -> foo.tlz static const char *const tar_suffixes[] = { ".txz", ".tlz", // ".tgz", }; suffix = tar_suffixes[format]; suffix_len = 4; src_len -= 4; } else { if (custom_suffix == NULL && opt_format == FORMAT_XZ) { // Instead of the .xz suffix, use a single // character at the end of the filename // extension. This is to minimize name // conflicts when compressing multiple files // with the same basename. E.g. foo.txt and // foo.exe become foo.tx- and foo.ex-. Dash // is rare as the last character of the // filename extension, so it seems to be // quite safe choice and it stands out better // in directory listings than e.g. x. For // comparison, gzip uses z. suffix = "-"; suffix_len = 1; } if (suffix[0] == '.') { // The first character of the suffix is a dot. // Throw away the original filename extension // and replace it with the new suffix. // // Examples: // xz -F lzma foo.txt -> foo.lzm // xz -S .x foo.txt -> foo.x src_len = sufsep - src_name; } else { // The first character of the suffix is not // a dot. Preserve the first 0-2 characters // of the original filename extension. // // Examples: // xz foo.txt -> foo.tx- // xz -S x foo.c -> foo.cx // xz -S ab foo.c -> foo.cab // xz -S ab foo.txt -> foo.tab // xz -S abc foo.txt -> foo.abc // // Truncate the suffix to three chars: if (suffix_len > 3) suffix_len = 3; // If needed, overwrite 1-3 characters. 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; }
/// \brief Removes the filename suffix of the compressed file /// /// \return Name of the uncompressed file, or NULL if file has unknown /// suffix. 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" }, // .txz abbreviation for .txt.gz is rare. { ".lzma", "" }, #ifdef __DJGPP__ { ".lzm", "" }, #endif { ".tlz", ".tar" }, // { ".gz", "" }, // { ".tgz", ".tar" }, }; const char *new_suffix = ""; size_t new_len = 0; if (opt_format == FORMAT_RAW) { // Don't check for known suffixes when --format=raw was used. if (custom_suffix == NULL) { message_error(_("%s: With --format=raw, " "--suffix=.SUF is required unless " "writing to stdout"), src_name); return NULL; } } else { 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__ // Support also *.?- -> *.? and *.??- -> *.?? on DOS. // This is done also when long filenames are available // to keep it easy to decompress files created when // long filename support wasn't available. 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; }