diff --git a/.gitignore b/.gitignore index b5355c780..920e58de8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ artifacts/ /benchmarks/input.* /benchmarks/*.md !/benchmarks/results.md + +# IDE-specific setting +.vscode +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md index edfb9896c..b22b4c9d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Categories Used: ## [Unreleased](https://github.com/ouch-org/ouch/compare/0.4.2...HEAD) - Add support for listing and decompressing `.rar` archives - Fix mime type detection +- Add support for 7z [\#555](https://github.com/ouch-org/ouch/pull/555) ([Flat](https://github.com/flat) & [MissileLab](https://github.com/MisileLab)) ### Bug Fixes diff --git a/Cargo.lock b/Cargo.lock index dde3d3ccb..0f0ac61ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,18 +10,18 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cd65a4b849ace0b7f6daeebcc1a1d111282227ca745458c61dbf670e52a597" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", @@ -33,15 +33,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -57,9 +57,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -124,6 +124,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "bstr" version = "1.8.0" @@ -131,27 +140,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" dependencies = [ "memchr", - "regex-automata 0.4.1", + "regex-automata", "serde", ] [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bzip2" @@ -201,6 +210,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "num-traits", +] + [[package]] name = "clap" version = "4.4.8" @@ -289,6 +307,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.3.2" @@ -346,6 +388,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -366,30 +417,19 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "filetime" @@ -403,6 +443,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "filetime_creation" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aea213d5ab4e6cd49f50c0688a4e20e5b75ff3bc07ff63f814778bd9b1dd42d" +dependencies = [ + "cfg-if", + "filetime", + "windows-sys 0.48.0", +] + [[package]] name = "flate2" version = "1.0.28" @@ -444,21 +495,31 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "js-sys", @@ -514,9 +575,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "ignore" @@ -568,27 +629,27 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -607,9 +668,9 @@ checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libz-sys" @@ -631,15 +692,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -647,9 +708,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lz4_flex" @@ -660,6 +721,15 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "lzma-rust" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f798132166cc040cb70dbab4ccbb89643a6966a4ac33f0b312e76a8238673a5" +dependencies = [ + "byteorder", +] + [[package]] name = "lzma-sys" version = "0.1.20" @@ -673,9 +743,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.1" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f478948fd84d9f8e86967bf432640e46adfb5a4bd4f14ef7e864ab38220534ae" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -704,11 +774,21 @@ dependencies = [ "getrandom", ] +[[package]] +name = "nt-time" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d4f129ec1833164165b1711c30b4fa4a2b12784ffefa9fe877b016704b7165" +dependencies = [ + "chrono", + "time", +] + [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -720,7 +800,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.3", "libc", ] @@ -730,6 +810,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "ouch" version = "0.4.2" @@ -741,7 +827,7 @@ dependencies = [ "clap", "clap_complete", "clap_mangen", - "filetime", + "filetime_creation", "flate2", "fs-err", "gzp", @@ -758,6 +844,7 @@ dependencies = [ "rand", "rayon", "same-file", + "sevenz-rust", "snap", "tar", "tempfile", @@ -835,9 +922,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" dependencies = [ "anstyle", "difflib", @@ -863,9 +950,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -898,9 +985,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -984,33 +1071,27 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.8", - "regex-syntax 0.7.5", + "regex-automata", + "regex-syntax 0.8.2", ] [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] -[[package]] -name = "regex-automata" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" - [[package]] name = "regex-syntax" version = "0.7.5" @@ -1031,9 +1112,9 @@ checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags 2.4.1", "errno", @@ -1071,15 +1152,59 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.185" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sevenz-rust" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +checksum = "33c7d45965e6557597a30e2d12e15d6a5f438bb7cbc11d5dbe89762541c0cd42" +dependencies = [ + "bit-set", + "byteorder", + "crc", + "filetime_creation", + "js-sys", + "lzma-rust", + "nt-time", + "sha2", + "wasm-bindgen", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] [[package]] name = "similar" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" +checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" [[package]] name = "snap" @@ -1133,9 +1258,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -1186,18 +1311,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", @@ -1224,6 +1349,7 @@ dependencies = [ "powerfmt", "serde", "time-core", + "time-macros", ] [[package]] @@ -1232,6 +1358,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -1242,6 +1377,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unarray" version = "0.1.4" @@ -1250,9 +1391,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unrar" @@ -1285,9 +1426,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" [[package]] name = "vcpkg" @@ -1295,6 +1436,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -1306,9 +1453,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -1322,9 +1469,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1332,9 +1479,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", @@ -1347,9 +1494,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1357,9 +1504,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", @@ -1370,9 +1517,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "widestring" @@ -1398,9 +1545,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -1426,7 +1573,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -1446,17 +1593,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1467,9 +1614,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -1479,9 +1626,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -1491,9 +1638,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -1503,9 +1650,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -1515,9 +1662,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -1527,9 +1674,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -1539,9 +1686,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "xattr" @@ -1603,11 +1750,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 415a602f7..4c195b342 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ atty = "0.2.14" bstr = { version = "1.8.0", default-features = false, features = ["std"] } bzip2 = "0.4.4" clap = { version = "4.4.8", features = ["derive", "env"] } -filetime = "0.2.22" +filetime_creation = "0.1" flate2 = { version = "1.0.28", default-features = false } fs-err = "2.11.0" gzp = { version = "0.11.3", default-features = false, features = ["snappy_default"] } @@ -26,6 +26,7 @@ lz4_flex = "0.11.1" once_cell = "1.18.0" rayon = "1.8.0" same-file = "1.0.6" +sevenz-rust = {version = "0.5.3", features = ["compress"]} snap = "1.1.0" tar = "0.4.40" tempfile = "3.8.1" diff --git a/src/archive/mod.rs b/src/archive/mod.rs index 428f3cb04..077d16b4a 100644 --- a/src/archive/mod.rs +++ b/src/archive/mod.rs @@ -1,5 +1,6 @@ //! Archive compression algorithms pub mod rar; +pub mod sevenz; pub mod tar; pub mod zip; diff --git a/src/archive/sevenz.rs b/src/archive/sevenz.rs new file mode 100644 index 000000000..0847f4ca5 --- /dev/null +++ b/src/archive/sevenz.rs @@ -0,0 +1,155 @@ +//! SevenZip archive format compress function + +use std::{ + env, io, + io::{Read, Seek, Write}, + path::{Path, PathBuf}, +}; + +use fs_err as fs; +use same_file::Handle; + +use crate::{ + error::FinalError, + info, + utils::{self, cd_into_same_dir_as, Bytes, EscapedPathDisplay, FileVisibilityPolicy}, + warning, +}; + +pub fn compress_sevenz( + files: &[PathBuf], + output_path: &Path, + writer: W, + file_visibility_policy: FileVisibilityPolicy, + quiet: bool, +) -> crate::Result +where + W: Write + Seek, +{ + let mut writer = sevenz_rust::SevenZWriter::new(writer)?; + let output_handle = Handle::from_path(output_path); + + for filename in files { + let previous_location = 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(); + + // If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion) + if let Ok(handle) = &output_handle { + if matches!(Handle::from_path(path), Ok(x) if &x == handle) { + warning!( + "The output file and the input file are the same: `{}`, skipping...", + output_path.display() + ); + continue; + } + } + + // This is printed for every file in `input_filenames` and has + // little importance for most users, but would generate lots of + // spoken text for users using screen readers, braille displays + // and so on + if !quiet { + info!(inaccessible, "Compressing '{}'.", EscapedPathDisplay::new(path)); + } + + let metadata = match path.metadata() { + Ok(metadata) => metadata, + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound && utils::is_symlink(path) { + // This path is for a broken symlink + // We just ignore it + continue; + } + return Err(e.into()); + } + }; + + let entry_name = path.to_str().ok_or_else(|| { + FinalError::with_title("7z requires that all entry names are valid UTF-8") + .detail(format!("File at '{path:?}' has a non-UTF-8 name")) + })?; + + let entry = sevenz_rust::SevenZArchiveEntry::from_path(path, entry_name.to_owned()); + let entry_data = if metadata.is_dir() { + None + } else { + Some(fs::File::open(path)?) + }; + + writer.push_archive_entry::(entry, entry_data)?; + } + + env::set_current_dir(previous_location)?; + } + + let bytes = writer.finish()?; + Ok(bytes) +} + +pub fn decompress_sevenz(reader: R, output_path: &Path, quiet: bool) -> crate::Result +where + R: Read + Seek, +{ + let mut count: usize = 0; + sevenz_rust::decompress_with_extract_fn(reader, output_path, |entry, reader, path| { + count += 1; + // Manually handle writing all files from 7z archive, due to library exluding empty files + use std::io::BufWriter; + + use filetime_creation as ft; + + let file_path = output_path.join(entry.name()); + + if entry.is_directory() { + if !quiet { + info!( + inaccessible, + "File {} extracted to \"{}\"", + entry.name(), + file_path.display() + ); + } + if !path.exists() { + fs::create_dir_all(path)?; + } + } else { + if !quiet { + info!( + inaccessible, + "{:?} extracted. ({})", + file_path.display(), + Bytes::new(entry.size()), + ); + } + + if let Some(parent) = path.parent() { + if !parent.exists() { + fs::create_dir_all(parent)?; + } + } + + let file = fs::File::create(path)?; + let mut writer = BufWriter::new(file); + io::copy(reader, &mut writer)?; + + ft::set_file_handle_times( + writer.get_ref().file(), + Some(ft::FileTime::from_system_time(entry.access_date().into())), + Some(ft::FileTime::from_system_time(entry.last_modified_date().into())), + Some(ft::FileTime::from_system_time(entry.creation_date().into())), + ) + .unwrap_or_default(); + } + + Ok(true) + })?; + + Ok(count) +} diff --git a/src/archive/tar.rs b/src/archive/tar.rs index d37628819..822212283 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -96,7 +96,8 @@ where for filename in input_filenames { let previous_location = utils::cd_into_same_dir_as(filename)?; - // Safe unwrap, input shall be treated before + // 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) { @@ -104,7 +105,7 @@ where let path = entry.path(); // If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion) - if let Ok(ref handle) = output_handle { + if let Ok(handle) = &output_handle { if matches!(Handle::from_path(path), Ok(x) if &x == handle) { warning!( "The output file and the input file are the same: `{}`, skipping...", diff --git a/src/archive/zip.rs b/src/archive/zip.rs index 60cc440db..e9bde15d6 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -10,7 +10,7 @@ use std::{ thread, }; -use filetime::{set_file_mtime, FileTime}; +use filetime_creation::{set_file_mtime, FileTime}; use fs_err as fs; use same_file::Handle; use time::OffsetDateTime; @@ -167,7 +167,8 @@ where for filename in input_filenames { let previous_location = cd_into_same_dir_as(filename)?; - // Safe unwrap, input shall be treated before + // 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) { @@ -175,7 +176,7 @@ where let path = entry.path(); // If the output_path is the same as the input file, warn the user and skip the input (in order to avoid compression recursion) - if let Ok(ref handle) = output_handle { + if let Ok(handle) = &output_handle { if matches!(Handle::from_path(path), Ok(x) if &x == handle) { warning!( "The output file and the input file are the same: `{}`, skipping...", @@ -208,8 +209,13 @@ where #[cfg(unix)] let options = options.unix_permissions(metadata.permissions().mode()); + let entry_name = path.to_str().ok_or_else(|| { + FinalError::with_title("Zip requires that all directories names are valid UTF-8") + .detail(format!("File at '{path:?}' has a non-UTF-8 name")) + })?; + if metadata.is_dir() { - writer.add_directory(path.to_str().unwrap().to_owned(), options)?; + writer.add_directory(entry_name, options)?; } else { #[cfg(not(unix))] let options = if is_executable::is_executable(path) { @@ -219,10 +225,11 @@ where }; let mut file = fs::File::open(path)?; - writer.start_file( - path.to_str().unwrap(), - options.last_modified_time(get_last_modified_time(&file)), - )?; + + // Updated last modified time + let last_modified_time = options.last_modified_time(get_last_modified_time(&file)); + + writer.start_file(entry_name, last_modified_time)?; io::copy(&mut file, &mut writer)?; } } diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 72e92c490..5150fff9c 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -5,6 +5,7 @@ use std::{ use fs_err as fs; +use super::warn_user_about_loading_sevenz_in_memory; use crate::{ archive, commands::warn_user_about_loading_zip_in_memory, @@ -79,7 +80,7 @@ pub fn compress_files( // is `clamp`ed and therefore guaranteed to be valid Box::new(zstd_encoder.unwrap().auto_finish()) } - Tar | Zip | Rar => unreachable!(), + Tar | Zip | Rar | SevenZip => unreachable!(), }; Ok(encoder) }; @@ -126,6 +127,20 @@ pub fn compress_files( archive::rar::no_compression_notice(); return Ok(false); } + SevenZip => { + if !formats.is_empty() { + warn_user_about_loading_sevenz_in_memory(); + + if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? { + return Ok(false); + } + } + + let mut vec_buffer = Cursor::new(vec![]); + archive::sevenz::compress_sevenz(&files, output_path, &mut vec_buffer, file_visibility_policy, quiet)?; + vec_buffer.rewind()?; + io::copy(&mut vec_buffer, &mut writer)?; + } } Ok(true) diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 9ae0adeb0..c717dbcfa 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -7,7 +7,7 @@ use std::{ use fs_err as fs; use crate::{ - commands::warn_user_about_loading_zip_in_memory, + commands::{warn_user_about_loading_sevenz_in_memory, warn_user_about_loading_zip_in_memory}, extension::{ split_first_compression_format, CompressionFormat::{self, *}, @@ -86,7 +86,7 @@ pub fn decompress_file( Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), - Tar | Zip | Rar => unreachable!(), + Tar | Zip | Rar | SevenZip => unreachable!(), }; Ok(decoder) }; @@ -164,6 +164,29 @@ pub fn decompress_file( return Ok(()); } } + SevenZip => { + if formats.len() > 1 { + warn_user_about_loading_sevenz_in_memory(); + + if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? { + return Ok(()); + } + } + + let mut vec = vec![]; + io::copy(&mut reader, &mut vec)?; + + if let ControlFlow::Continue(files) = smart_unpack( + |output_dir| crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, quiet), + output_dir, + &output_file_path, + question_policy, + )? { + files + } else { + return Ok(()); + } + } }; // this is only printed once, so it doesn't result in much text. On the other hand, diff --git a/src/commands/list.rs b/src/commands/list.rs index e33d9997e..f89830fe6 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -52,7 +52,7 @@ pub fn list_archive_contents( Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), - Tar | Zip | Rar => unreachable!(), + Tar | Zip | Rar | SevenZip => unreachable!(), }; Ok(decoder) }; @@ -87,6 +87,25 @@ pub fn list_archive_contents( Box::new(crate::archive::rar::list_archive(archive_path)) } } + SevenZip => { + if formats.len() > 1 { + warn_user_about_loading_zip_in_memory(); + if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? { + return Ok(()); + } + } + + let mut files = Vec::new(); + + sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| { + files.push(Ok(FileInArchive { + path: entry.name().into(), + is_dir: entry.is_directory(), + })); + Ok(true) + })?; + Box::new(files.into_iter()) + } Gzip | Bzip | Lz4 | Lzma | Snappy | Zstd => { panic!("Not an archive! This should never happen, if it does, something is wrong with `CompressionFormat::is_archive()`. Please report this error!"); } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index e5be2330a..b9763d1fc 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -31,6 +31,16 @@ fn warn_user_about_loading_zip_in_memory() { warning!("{}", ZIP_IN_MEMORY_LIMITATION_WARNING); } +/// Warn the user that (de)compressing this .7z archive might freeze their system. +fn warn_user_about_loading_sevenz_in_memory() { + const SEVENZ_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ + \tThe format '.7z' is limited and cannot be (de)compressed using encoding streams.\n\ + \tWhen using '.7z' with other formats, (de)compression must be done in-memory\n\ + \tCareful, you might run out of RAM if the archive is too large!"; + + warning!("{}", SEVENZ_IN_MEMORY_LIMITATION_WARNING); +} + /// This function checks what command needs to be run and performs A LOT of ahead-of-time checks /// to assume everything is OK. /// diff --git a/src/error.rs b/src/error.rs index 896f383ac..1b48623de 100644 --- a/src/error.rs +++ b/src/error.rs @@ -34,6 +34,8 @@ pub enum Error { Custom { reason: FinalError }, /// Invalid format passed to `--format` InvalidFormat { reason: String }, + /// From sevenz_rust::Error + SevenzipError(sevenz_rust::Error), } /// Alias to std's Result with ouch's Error @@ -139,6 +141,7 @@ impl fmt::Display for Error { Error::UnsupportedZipArchive(reason) => FinalError::with_title("Unsupported zip archive").detail(*reason), Error::InvalidFormat { reason } => FinalError::with_title("Invalid archive format").detail(reason.clone()), Error::Custom { reason } => reason.clone(), + Error::SevenzipError(reason) => FinalError::with_title("7z error").detail(reason.to_string()), }; write!(f, "{err}") @@ -186,6 +189,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: sevenz_rust::Error) -> Self { + Self::SevenzipError(err) + } +} + impl From for Error { fn from(err: ignore::Error) -> Self { Self::WalkdirError { diff --git a/src/extension.rs b/src/extension.rs index 85451cb3b..b4029a825 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -7,7 +7,9 @@ use bstr::ByteSlice; use self::CompressionFormat::*; use crate::{error::Error, warning}; -pub const SUPPORTED_EXTENSIONS: &[&str] = &["tar", "zip", "bz", "bz2", "gz", "lz4", "xz", "lzma", "sz", "zst", "rar"]; +pub const SUPPORTED_EXTENSIONS: &[&str] = &[ + "tar", "zip", "bz", "bz2", "gz", "lz4", "xz", "lzma", "sz", "zst", "rar", "7z", +]; pub const SUPPORTED_ALIASES: &[&str] = &["tgz", "tbz", "tlz4", "txz", "tzlma", "tsz", "tzst"]; pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, rar"; pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tzlma, tsz, tzst"; @@ -74,6 +76,8 @@ pub enum CompressionFormat { Zip, /// .rar Rar, + /// .7z + SevenZip, } impl CompressionFormat { @@ -81,7 +85,7 @@ impl CompressionFormat { fn is_archive_format(&self) -> bool { // Keep this match like that without a wildcard `_` so we don't forget to update it match self { - Tar | Zip | Rar => true, + Tar | Zip | Rar | SevenZip => true, Gzip => false, Bzip => false, Lz4 => false, @@ -110,6 +114,7 @@ fn to_extension(ext: &[u8]) -> Option { b"sz" => &[Snappy], b"zst" => &[Zstd], b"rar" => &[Rar], + b"7z" => &[SevenZip], _ => return None, }, ext.to_str_lossy(), diff --git a/src/utils/fs.rs b/src/utils/fs.rs index 08f6d1bac..dbd422e0e 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -91,6 +91,9 @@ pub fn try_infer_extension(path: &Path) -> Option { && buf.starts_with(&[0x52, 0x61, 0x72, 0x21, 0x1A, 0x07]) && (buf[6] == 0x00 || (buf.len() >= 8 && buf[6..=7] == [0x01, 0x00])) } + fn is_sevenz(buf: &[u8]) -> bool { + buf.starts_with(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]) + } let buf = { let mut buf = [0; 270]; @@ -124,6 +127,8 @@ pub fn try_infer_extension(path: &Path) -> Option { Some(Extension::new(&[Zstd], "zst")) } else if is_rar(&buf) { Some(Extension::new(&[Rar], "rar")) + } else if is_sevenz(&buf) { + Some(Extension::new(&[SevenZip], "7z")) } else { None } diff --git a/tests/integration.rs b/tests/integration.rs index b67b3ee84..f88e56827 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -19,6 +19,8 @@ use crate::utils::{assert_same_directory, write_random_content}; #[derive(Arbitrary, Debug, Display)] #[display(style = "lowercase")] enum DirectoryExtension { + #[display("7z")] + SevenZ, Tar, Tbz, Tbz2, diff --git a/tests/mime.rs b/tests/mime.rs index 1f340d078..bf5217984 100644 --- a/tests/mime.rs +++ b/tests/mime.rs @@ -17,11 +17,12 @@ fn sanity_check_through_mime() { write_random_content(test_file, &mut SmallRng::from_entropy()); let formats = [ - "tar", "zip", "tar.gz", "tgz", "tbz", "tbz2", "txz", "tlzma", "tzst", "tar.bz", "tar.bz2", "tar.lzma", + "7z", "tar", "zip", "tar.gz", "tgz", "tbz", "tbz2", "txz", "tlzma", "tzst", "tar.bz", "tar.bz2", "tar.lzma", "tar.xz", "tar.zst", ]; let expected_mimes = [ + "application/x-7z-compressed", "application/x-tar", "application/zip", "application/gzip",