diff --git a/Cargo.lock b/Cargo.lock index fdbc4cac0..e9a66d4ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,6 +99,12 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "ar" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" + [[package]] name = "arbitrary" version = "1.4.1" @@ -1161,6 +1167,7 @@ name = "ouch" version = "0.6.1" dependencies = [ "anyhow", + "ar", "assert_cmd", "brotli", "bstr", diff --git a/Cargo.toml b/Cargo.toml index 1891c1fac..8d849c77b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ categories = ["command-line-utilities", "compression", "encoding"] description = "A command-line utility for easily compressing and decompressing files and directories." [dependencies] +ar = "0.9.0" brotli = "7.0.0" bstr = { version = "1.10.0", default-features = false, features = ["std"] } bytesize = "1.3.0" diff --git a/src/archive/ar.rs b/src/archive/ar.rs new file mode 100644 index 000000000..edb55fc2e --- /dev/null +++ b/src/archive/ar.rs @@ -0,0 +1,181 @@ +//! Contains Ar-specific building and unpacking functions + +use std::{ + env, + io::{Read, Write}, + path::{Path, PathBuf}, + sync::mpsc::{self, Receiver}, + thread, +}; + +use fs_err as fs; +use same_file::Handle; + +use crate::{ + Result, + error::FinalError, + info, + list::FileInArchive, + utils::{self, BytesFmt, FileVisibilityPolicy, PathFmt, is_same_file_as_output}, + warning, +}; + +/// Unpacks an ar archive into the given output folder +pub fn unpack_archive(reader: impl Read, output_folder: &Path) -> Result { + let mut archive = ar::Archive::new(reader); + let mut files_unpacked = 0; + + while let Some(entry_result) = archive.next_entry() { + let mut entry = entry_result.map_err(|e| { + FinalError::with_title("Failed to read ar archive").detail(format!("Error reading ar entry: {e}")) + })?; + + let identifier = String::from_utf8_lossy(entry.header().identifier()) + .trim_end_matches('/') + .to_string(); + + // Skip empty identifiers + if identifier.is_empty() { + continue; + } + + let output_path = output_folder.join(&identifier); + + // Create parent directories if needed + if let Some(parent) = output_path.parent() { + fs::create_dir_all(parent)?; + } + + // Extract the file + let mut output_file = fs::File::create(&output_path)?; + let size = std::io::copy(&mut entry, &mut output_file)?; + + info!("extracted ({}) {}", BytesFmt(size), PathFmt(&output_path),); + + files_unpacked += 1; + } + + Ok(files_unpacked) +} + +/// List contents of an ar archive +pub fn list_archive(reader: R) -> impl Iterator> { + struct Files(Receiver>); + impl Iterator for Files { + type Item = crate::Result; + + fn next(&mut self) -> Option { + self.0.recv().ok() + } + } + + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + let mut archive = ar::Archive::new(reader); + + while let Some(entry_result) = archive.next_entry() { + let entry = match entry_result { + Ok(e) => e, + Err(e) => { + let _ = tx.send(Err(FinalError::with_title("Failed to read ar archive") + .detail(format!("Error reading ar entry: {e}")) + .into())); + break; + } + }; + + let identifier = String::from_utf8_lossy(entry.header().identifier()) + .trim_end_matches('/') + .to_string(); + + // Skip empty identifiers + if identifier.is_empty() { + continue; + } + + let file = FileInArchive { + path: identifier.into(), + is_dir: false, + }; + + if tx.send(Ok(file)).is_err() { + break; + } + } + }); + + Files(rx) +} + +/// Compresses the files given by `input_filenames` into an ar archive written to `writer`. +pub fn build_archive_from_paths( + input_filenames: &[PathBuf], + output_path: &Path, + writer: W, + file_visibility_policy: FileVisibilityPolicy, +) -> Result +where + W: Write, +{ + let mut builder = ar::Builder::new(writer); + let output_handle = Handle::from_path(output_path); + + for filename in input_filenames { + let previous_location = utils::cd_into_same_dir_as(filename)?; + + // Unwrap safety: + // paths should be canonicalized by now, and the root directory rejected. + let filename = filename.file_name().unwrap(); + + for entry in file_visibility_policy.build_walker(filename) { + let entry = entry?; + let path = entry.path(); + + // Avoid compressing the output file into itself + if let Ok(handle) = output_handle.as_ref() + && is_same_file_as_output(path, handle) + { + warning!("Cannot compress {} into itself, skipping", PathFmt(output_path)); + continue; + } + + // ar archives only support regular files, skip directories and symlinks + if !path.is_file() || path.is_symlink() { + continue; + } + + info!("Compressing {}", PathFmt(path)); + + let file = match fs::File::open(path) { + Ok(f) => f, + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound && path.is_symlink() { + // Broken symlink, skip + continue; + } + return Err(e.into()); + } + }; + + // Get file name for the archive entry + let file_name = path + .file_name() + .map(|s| s.to_string_lossy().into_owned()) + .unwrap_or_else(|| "unknown".to_string()); + + // ar crate requires std::fs::File, so we get the inner file + let mut std_file = file.into_parts().0; + + builder + .append_file(file_name.as_bytes(), &mut std_file) + .map_err(|err| { + FinalError::with_title("Could not create ar archive") + .detail(format!("Error adding file '{}': {err}", path.display())) + })?; + } + env::set_current_dir(previous_location)?; + } + + Ok(builder.into_inner()?) +} diff --git a/src/archive/mod.rs b/src/archive/mod.rs index a5d8a147b..502858835 100644 --- a/src/archive/mod.rs +++ b/src/archive/mod.rs @@ -1,3 +1,4 @@ +pub mod ar; #[cfg(feature = "unrar")] pub mod rar; pub mod sevenz; diff --git a/src/check.rs b/src/check.rs index 424759923..d4b1af769 100644 --- a/src/check.rs +++ b/src/check.rs @@ -154,6 +154,20 @@ pub fn check_archive_formats_position(formats: &[Extension], output_path: &Path) Ok(()) } +/// Check if trying to compress to a .deb file (not supported, requires dpkg tool and proper metadata). +pub fn check_deb_compression(output_path: &Path) -> Result<()> { + if output_path + .extension() + .is_some_and(|ext| ext.eq_ignore_ascii_case("deb")) + { + let error = FinalError::with_title(format!("Cannot compress to '{}'.", PathFmt(output_path))) + .detail("Creating .deb packages is not supported, use 'dpkg-deb' instead."); + + return Err(error.into()); + } + Ok(()) +} + /// Check if all provided files have formats to decompress. pub fn check_missing_formats_when_decompressing(files: &[PathBuf], formats: &[Vec]) -> Result<()> { let files_with_broken_extension: Vec<&PathBuf> = files diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 4994fdb34..532492106 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -127,7 +127,7 @@ pub fn compress_files( let win_size = 22; // default to 2^22 = 4 MiB window size Box::new(brotli::CompressorWriter::new(encoder, BUFFER_CAPACITY, level, win_size)) } - Tar | Zip | Rar | SevenZip => unreachable!(), + Tar | Zip | Rar | SevenZip | Ar => unreachable!(), }; Ok(encoder) }; @@ -201,6 +201,10 @@ pub fn compress_files( vec_buffer.rewind()?; io::copy(&mut vec_buffer, &mut writer)?; } + Ar => { + archive::ar::build_archive_from_paths(&files, output_path, &mut writer, file_visibility_policy)?; + writer.flush()?; + } } Ok(true) diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index f87684600..8a4485d97 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -65,7 +65,7 @@ pub fn decompress_file(options: DecompressOptions) -> Result<()> { Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), Brotli => Box::new(brotli::Decompressor::new(decoder, BUFFER_CAPACITY)), - Tar | Zip | Rar | SevenZip => unreachable!(), + Tar | Zip | Rar | SevenZip | Ar => unreachable!(), }; Ok(decoder) }; @@ -157,6 +157,11 @@ pub fn decompress_file(options: DecompressOptions) -> Result<()> { options.question_policy, )? } + Ar => unpack_archive( + |output_dir| crate::archive::ar::unpack_archive(create_decoder_up_to_first_extension()?, output_dir), + options.output_dir, + options.question_policy, + )?, #[cfg(feature = "unrar")] Rar => { let unpack_fn: Box Result> = if options.formats.len() > 1 || input_is_stdin { diff --git a/src/commands/list.rs b/src/commands/list.rs index be458e85b..e1c4374c8 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -63,7 +63,7 @@ pub fn list_archive_contents( Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), Brotli => Box::new(brotli::Decompressor::new(decoder, BUFFER_CAPACITY)), - Tar | Zip | Rar | SevenZip => unreachable!("should be treated by caller"), + Tar | Zip | Rar | SevenZip | Ar => unreachable!("should be treated by caller"), }; Ok(decoder) }; @@ -130,6 +130,7 @@ pub fn list_archive_contents( Box::new(archive::sevenz::list_archive(fs::File::open(archive_path)?, password)?) } } + Ar => Box::new(archive::ar::list_archive(reader)), Gzip | Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli => { unreachable!("Not an archive, should be validated before calling this function."); } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 0a259c289..f5cefcb4e 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -86,6 +86,7 @@ pub fn run(args: CliArgs, question_policy: QuestionPolicy, file_visibility_polic formats_from_flag.as_deref(), )?; check::check_archive_formats_position(&formats, &output_path)?; + check::check_deb_compression(&output_path)?; let (output_file, output_path) = match utils::create_file_or_prompt_on_conflict( &output_path, diff --git a/src/extension.rs b/src/extension.rs index a6954e4c8..22b90655e 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -27,6 +27,8 @@ pub const SUPPORTED_EXTENSIONS: &[&str] = &[ "rar", "7z", "br", + "a", + "deb", ]; pub const SUPPORTED_ALIASES: &[&str] = &[ @@ -34,9 +36,9 @@ pub const SUPPORTED_ALIASES: &[&str] = &[ ]; #[cfg(not(feature = "unrar"))] -pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z"; +pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z, a, deb"; #[cfg(feature = "unrar")] -pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z"; +pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z, a, deb"; pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr"; @@ -117,13 +119,15 @@ pub enum CompressionFormat { SevenZip, /// .br Brotli, + /// .a, .deb (Unix ar archives) + Ar, } impl CompressionFormat { pub fn is_archive_format(&self) -> bool { // Keep this match without a wildcard `_` so we never forget to update it match self { - Tar | Zip | Rar | SevenZip => true, + Tar | Zip | Rar | SevenZip | Ar => true, Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli | Gzip => false, } } @@ -144,6 +148,7 @@ impl CompressionFormat { Lzip => "lz", Snappy => "sz", Zstd => "zst", + Ar => "a", } } } @@ -173,6 +178,7 @@ fn slice_to_extension(ext: &[u8]) -> Option { b"rar" | b"cbr" => [Rar].as_slice(), b"7z" | b"cb7" => [SevenZip].as_slice(), b"br" => [Brotli].as_slice(), + b"a" | b"deb" => [Ar].as_slice(), _ => return None, }; let extension_text = ext.to_str_lossy(); diff --git a/src/utils/fs.rs b/src/utils/fs.rs index e61de169a..4477fb87a 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -203,6 +203,10 @@ pub fn try_infer_format(path: &Path) -> Option { fn is_sevenz(buf: &[u8]) -> bool { buf.starts_with(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]) } + fn is_ar(buf: &[u8]) -> bool { + // Unix ar archives start with "!\n" + buf.starts_with(b"!\n") + } let buf = { let mut buf = [0; 270]; @@ -243,6 +247,8 @@ pub fn try_infer_format(path: &Path) -> Option { Some(CompressionFormat::Rar) } else if is_sevenz(&buf) { Some(CompressionFormat::SevenZip) + } else if is_ar(&buf) { + Some(CompressionFormat::Ar) } else { None } diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap index 03439bf8c..5b99e898b 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap @@ -1,12 +1,14 @@ --- source: tests/ui.rs +assertion_line: 68 expression: "run_ouch(\"ouch decompress a\", dir)" --- +[WARNING] Received a file with name 'a', but a was expected as the extension [ERROR] Cannot decompress files - Files with missing extensions: "a" - Decompression formats are detected automatically from file extension and signature -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Alternatively, you can pass an extension to the '--format' flag: diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap index 4a07a9924..345867720 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap @@ -1,11 +1,13 @@ --- source: tests/ui.rs +assertion_line: 69 expression: "run_ouch(\"ouch decompress a b.unknown\", dir)" --- +[WARNING] Received a file with name 'a', but a was expected as the extension [ERROR] Cannot decompress files - Files with unsupported extensions: "b.unknown" - Files with missing extensions: "a" - Decompression formats are detected automatically from file extension and signature -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap index aa0e8c13a..7be8f01c7 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap @@ -6,7 +6,7 @@ expression: "run_ouch(\"ouch decompress b.unknown\", dir)" - Files with unsupported extensions: "b.unknown" - Decompression formats are detected automatically from file extension and signature -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Alternatively, you can pass an extension to the '--format' flag: diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap index bd7a7f3aa..f5e167b12 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap @@ -2,11 +2,12 @@ source: tests/ui.rs expression: "run_ouch(\"ouch decompress a\", dir)" --- +[WARNING] Received a file with name 'a', but a was expected as the extension [ERROR] Cannot decompress files - Files with missing extensions: "a" - Decompression formats are detected automatically from file extension and signature -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Alternatively, you can pass an extension to the '--format' flag: diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap index d21019509..bb445b4f5 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap @@ -2,10 +2,11 @@ source: tests/ui.rs expression: "run_ouch(\"ouch decompress a b.unknown\", dir)" --- +[WARNING] Received a file with name 'a', but a was expected as the extension [ERROR] Cannot decompress files - Files with unsupported extensions: "b.unknown" - Files with missing extensions: "a" - Decompression formats are detected automatically from file extension and signature -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap index b159bd28d..2791473ef 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap @@ -6,7 +6,7 @@ expression: "run_ouch(\"ouch decompress b.unknown\", dir)" - Files with unsupported extensions: "b.unknown" - Decompression formats are detected automatically from file extension and signature -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Alternatively, you can pass an extension to the '--format' flag: diff --git a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-1.snap b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-1.snap index 551eaeaef..2726f276f 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-1.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-1.snap @@ -1,11 +1,12 @@ --- source: tests/ui.rs +assertion_line: 90 expression: "run_ouch(\"ouch compress input output --format tar.gz.unknown\", dir)" --- [ERROR] Failed to parse `--format tar.gz.unknown` - Unsupported extension 'unknown' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Examples: diff --git a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-2.snap b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-2.snap index 8d77570bf..4fb17902f 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-2.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-2.snap @@ -1,11 +1,12 @@ --- source: tests/ui.rs +assertion_line: 94 expression: "run_ouch(\"ouch compress input output --format targz\", dir)" --- [ERROR] Failed to parse `--format targz` - Unsupported extension 'targz' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Examples: diff --git a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-3.snap b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-3.snap index 24c06c552..afb50092b 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-3.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-3.snap @@ -5,7 +5,7 @@ expression: "run_ouch(\"ouch compress input output --format .tar.$#!@.rest\", di [ERROR] Failed to parse `--format .tar.$#!@.rest` - Unsupported extension '$#!@' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Examples: diff --git a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-1.snap b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-1.snap index 4673474a5..08ba00970 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-1.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-1.snap @@ -5,7 +5,7 @@ expression: "run_ouch(\"ouch compress input output --format tar.gz.unknown\", di [ERROR] Failed to parse `--format tar.gz.unknown` - Unsupported extension 'unknown' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Examples: diff --git a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-2.snap b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-2.snap index 50b175dad..76e54fba1 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-2.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-2.snap @@ -5,7 +5,7 @@ expression: "run_ouch(\"ouch compress input output --format targz\", dir)" [ERROR] Failed to parse `--format targz` - Unsupported extension 'targz' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Examples: diff --git a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-3.snap b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-3.snap index db541782c..ea6d5ce74 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-3.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-3.snap @@ -5,7 +5,7 @@ expression: "run_ouch(\"ouch compress input output --format .tar.$#!@.rest\", di [ERROR] Failed to parse `--format .tar.$#!@.rest` - Unsupported extension '$#!@' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z, a, deb hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz, cbt, cbz, cb7, cbr hint: hint: Examples: