Skip to content

Commit cffa377

Browse files
authored
Add listing and decompressing support for LZMA1 and Lzip (#838)
* Replace xz2 dependency by liblzma * feat: Add list and decompressing support for legacy lzma (LZMA1) * feat: Add listing and decompressing support for lzip
1 parent b531a25 commit cffa377

26 files changed

Lines changed: 121 additions & 85 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Categories Used:
2525
- Merge folders in decompression [\#798](https://github.com/ouch-org/ouch/pull/798) ([tommady](https://github.com/tommady))
2626
- Add `--no-smart-unpack` flag to decompression command to disable smart unpack [\#809](https://github.com/ouch-org/ouch/pull/809) ([talis-fb](https://github.com/talis-fb))
2727
- Provide Nushell completions (packages still need to install them) [\#827](https://github.com/ouch-org/ouch/pull/827) ([FrancescElies](https://github.com/FrancescElies))
28+
- Support `.lz` decompression [\#838](https://github.com/ouch-org/ouch/pull/838) ([zzzsyyy](https://github.com/zzzsyyy))
29+
- Support `.lzma` decompression (and fix `.lzma` being a wrong alias for `.xz`) [\#838](https://github.com/ouch-org/ouch/pull/838) ([zzzsyyy](https://github.com/zzzsyyy))
2830

2931
### Improvements
3032

Cargo.lock

Lines changed: 21 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ tar = "0.4.42"
4141
tempfile = "3.10.1"
4242
time = { version = "0.3.36", default-features = false }
4343
unrar = { version = "0.5.7", optional = true }
44-
xz2 = "0.1.7"
44+
liblzma = "0.4"
4545
zip = { version = "0.6.6", default-features = false, features = [
4646
"time",
4747
"aes-crypto",

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ Output:
111111

112112
# Supported formats
113113

114-
| Format | `.tar` | `.zip` | `7z` | `.gz` | `.xz`, `.lzma` | `.bz`, `.bz2` | `.bz3` | `.lz4` | `.sz` (Snappy) | `.zst` | `.rar` | `.br` |
115-
|:---------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
116-
| Supported || ✓¹ | ✓¹ | ✓² ||||| ✓² | ✓² | ✓³ ||
114+
| Format | `.tar` | `.zip` | `7z` | `.gz` | `.xz` | `.lzma` | `.lz` | `.bz`, `.bz2` | `.bz3` | `.lz4` | `.sz` (Snappy) | `.zst` | `.rar` | `.br` |
115+
|:---------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
116+
| Supported || ✓¹ | ✓¹ | ✓² ||| ✓⁴ | ||| ✓² | ✓² | ✓³ ||
117117

118118
✓: Supports compression and decompression.
119119

@@ -122,10 +122,13 @@ Output:
122122
✓²: Supported, and compression runs in parallel.
123123

124124
✓³: Due to RAR's restrictive license, only decompression and listing can be supported.
125+
126+
✓⁴: Only decompression is supported, compression is not implemented yet.
127+
125128
If you wish to exclude non-free code from your build, you can disable RAR support
126129
by building without the `unrar` feature.
127130

128-
`tar` aliases are also supported: `tgz`, `tbz`, `tbz2`, `tlz4`, `txz`, `tlzma`, `tsz`, `tzst`.
131+
`tar` aliases are also supported: `tgz`, `tbz`, `tbz2`, `tlz4`, `txz`, `tlzma`, `tsz`, `tzst`, `tlz`.
129132

130133
Formats can be chained:
131134

src/cli/args.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use clap::{Parser, ValueHint};
55
// Ouch command line options (docstrings below are part of --help)
66
/// A command-line utility for easily compressing and decompressing files and directories.
77
///
8-
/// Supported formats: tar, zip, gz, 7z, xz/lzma, bz/bz2, bz3, lz4, sz (Snappy), zst, rar and br.
8+
/// Supported formats: tar, zip, gz, 7z, xz, lzma, lzip, bz/bz2, bz3, lz4, sz (Snappy), zst, rar and br.
99
///
1010
/// Repository: https://github.com/ouch-org/ouch
1111
#[derive(Parser, Debug, PartialEq)]

src/commands/compress.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,20 @@ pub fn compress_files(
6868
)
6969
}
7070
Lz4 => Box::new(lz4_flex::frame::FrameEncoder::new(encoder).auto_finish()),
71-
Lzma => Box::new(xz2::write::XzEncoder::new(
71+
Lzma => {
72+
return Err(crate::Error::UnsupportedFormat {
73+
reason: "LZMA1 compression is not supported in ouch, use .xz instead.".to_string(),
74+
})
75+
}
76+
Xz => Box::new(liblzma::write::XzEncoder::new(
7277
encoder,
7378
level.map_or(6, |l| (l as u32).clamp(0, 9)),
7479
)),
80+
Lzip => {
81+
return Err(crate::Error::UnsupportedFormat {
82+
reason: "Lzip compression is not supported in ouch.".to_string(),
83+
})
84+
}
7585
Snappy => Box::new(
7686
gzp::par::compress::ParCompress::<gzp::snap::Snap>::builder()
7787
.compression_level(gzp::par::compress::Compression::new(
@@ -108,7 +118,7 @@ pub fn compress_files(
108118
}
109119

110120
match first_format {
111-
Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => {
121+
Gzip | Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli => {
112122
writer = chain_writer_encoder(&first_format, writer)?;
113123
let mut reader = fs::File::open(&files[0])?;
114124

src/commands/decompress.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,15 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> {
128128
Box::new(bzip3::read::Bz3Decoder::new(decoder)?)
129129
}
130130
Lz4 => Box::new(lz4_flex::frame::FrameDecoder::new(decoder)),
131-
Lzma => Box::new(xz2::read::XzDecoder::new(decoder)),
131+
Lzma => Box::new(liblzma::read::XzDecoder::new_stream(
132+
decoder,
133+
liblzma::stream::Stream::new_lzma_decoder(u64::MAX).unwrap(),
134+
)),
135+
Xz => Box::new(liblzma::read::XzDecoder::new(decoder)),
136+
Lzip => Box::new(liblzma::read::XzDecoder::new_stream(
137+
decoder,
138+
liblzma::stream::Stream::new_lzip_decoder(u64::MAX, 0).unwrap(),
139+
)),
132140
Snappy => Box::new(snap::read::FrameDecoder::new(decoder)),
133141
Zstd => Box::new(zstd::stream::Decoder::new(decoder)?),
134142
Brotli => Box::new(brotli::Decompressor::new(decoder, BUFFER_CAPACITY)),
@@ -144,7 +152,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> {
144152
}
145153

146154
let files_unpacked = match first_extension {
147-
Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => {
155+
Gzip | Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli => {
148156
reader = chain_reader_decoder(&first_extension, reader)?;
149157

150158
let mut writer = match utils::ask_to_create_file(

src/commands/list.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,15 @@ pub fn list_archive_contents(
5757
Box::new(bzip3::read::Bz3Decoder::new(decoder).unwrap())
5858
}
5959
Lz4 => Box::new(lz4_flex::frame::FrameDecoder::new(decoder)),
60-
Lzma => Box::new(xz2::read::XzDecoder::new(decoder)),
60+
Lzma => Box::new(liblzma::read::XzDecoder::new_stream(
61+
decoder,
62+
liblzma::stream::Stream::new_lzma_decoder(u64::MAX).unwrap(),
63+
)),
64+
Xz => Box::new(liblzma::read::XzDecoder::new(decoder)),
65+
Lzip => Box::new(liblzma::read::XzDecoder::new_stream(
66+
decoder,
67+
liblzma::stream::Stream::new_lzip_decoder(u64::MAX, 0).unwrap(),
68+
)),
6169
Snappy => Box::new(snap::read::FrameDecoder::new(decoder)),
6270
Zstd => Box::new(zstd::stream::Decoder::new(decoder)?),
6371
Brotli => Box::new(brotli::Decompressor::new(decoder, BUFFER_CAPACITY)),
@@ -127,7 +135,7 @@ pub fn list_archive_contents(
127135

128136
Box::new(archive::sevenz::list_archive(io::Cursor::new(vec), password)?)
129137
}
130-
Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => {
138+
Gzip | Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli => {
131139
unreachable!("Not an archive, should be validated before calling this function.");
132140
}
133141
};

src/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ impl FinalError {
135135
///
136136
/// This is what it looks like:
137137
/// ```
138-
/// hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst
139-
/// hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst
138+
/// hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, lz, sz, zst
139+
/// hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz
140140
/// ```
141141
pub fn hint_all_supported_formats(self) -> Self {
142142
self.hint(format!("Supported extensions are: {PRETTY_SUPPORTED_EXTENSIONS}"))

src/extension.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub const SUPPORTED_EXTENSIONS: &[&str] = &[
1919
"lz4",
2020
"xz",
2121
"lzma",
22+
"lz",
2223
"sz",
2324
"zst",
2425
#[cfg(feature = "unrar")]
@@ -27,14 +28,14 @@ pub const SUPPORTED_EXTENSIONS: &[&str] = &[
2728
"br",
2829
];
2930

30-
pub const SUPPORTED_ALIASES: &[&str] = &["tgz", "tbz", "tlz4", "txz", "tzlma", "tsz", "tzst"];
31+
pub const SUPPORTED_ALIASES: &[&str] = &["tgz", "tbz", "tlz4", "txz", "tlzma", "tsz", "tzst", "tlz"];
3132

3233
#[cfg(not(feature = "unrar"))]
33-
pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, 7z";
34+
pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z";
3435
#[cfg(feature = "unrar")]
35-
pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, rar, 7z";
36+
pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z";
3637

37-
pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tzlma, tsz, tzst";
38+
pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz";
3839

3940
/// A wrapper around `CompressionFormat` that allows combinations like `tgz`
4041
#[derive(Debug, Clone)]
@@ -85,8 +86,12 @@ pub enum CompressionFormat {
8586
Bzip3,
8687
/// .lz4
8788
Lz4,
88-
/// .xz .lzma
89+
/// .xz
90+
Xz,
91+
/// .lzma
8992
Lzma,
93+
/// .lzip
94+
Lzip,
9095
/// .sz
9196
Snappy,
9297
/// tar, tgz, tbz, tbz2, tbz3, txz, tlz4, tlzma, tsz, tzst
@@ -95,7 +100,6 @@ pub enum CompressionFormat {
95100
Zstd,
96101
/// .zip
97102
Zip,
98-
// even if built without RAR support, we still want to recognise the format
99103
/// .rar
100104
Rar,
101105
/// .7z
@@ -105,19 +109,11 @@ pub enum CompressionFormat {
105109
}
106110

107111
impl CompressionFormat {
108-
/// Currently supported archive formats are .tar (and aliases to it) and .zip
109112
pub fn archive_format(&self) -> bool {
110-
// Keep this match like that without a wildcard `_` so we don't forget to update it
113+
// Keep this match without a wildcard `_` so we never forget to update it
111114
match self {
112115
Tar | Zip | Rar | SevenZip => true,
113-
Gzip => false,
114-
Bzip => false,
115-
Bzip3 => false,
116-
Lz4 => false,
117-
Lzma => false,
118-
Snappy => false,
119-
Zstd => false,
120-
Brotli => false,
116+
Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli | Gzip => false,
121117
}
122118
}
123119
}
@@ -130,15 +126,19 @@ fn to_extension(ext: &[u8]) -> Option<Extension> {
130126
b"tbz" | b"tbz2" => &[Tar, Bzip],
131127
b"tbz3" => &[Tar, Bzip3],
132128
b"tlz4" => &[Tar, Lz4],
133-
b"txz" | b"tlzma" => &[Tar, Lzma],
129+
b"txz" => &[Tar, Xz],
130+
b"tlzma" => &[Tar, Lzma],
131+
b"tlz" => &[Tar, Lzip],
134132
b"tsz" => &[Tar, Snappy],
135133
b"tzst" => &[Tar, Zstd],
136134
b"zip" => &[Zip],
137135
b"bz" | b"bz2" => &[Bzip],
138136
b"bz3" => &[Bzip3],
139137
b"gz" => &[Gzip],
140138
b"lz4" => &[Lz4],
141-
b"xz" | b"lzma" => &[Lzma],
139+
b"xz" => &[Xz],
140+
b"lzma" => &[Lzma],
141+
b"lz" => &[Lzip],
142142
b"sz" => &[Snappy],
143143
b"zst" => &[Zstd],
144144
b"rar" => &[Rar],

0 commit comments

Comments
 (0)