From 6d0a54dfee233f53b7e29684ef608bdd6cfffdaa Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Sun, 2 Apr 2023 10:24:51 +0000 Subject: [PATCH 01/23] chore: add .vscode and .idea to .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) 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 From 3f635b9cbf762fec435116eb7908825f0f3647d7 Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Sun, 2 Apr 2023 10:59:47 +0000 Subject: [PATCH 02/23] feat: simple impl 7zip and lot of todo --- Cargo.lock | 416 +++++++++++++++++++++++++------------ Cargo.toml | 1 + src/commands/compress.rs | 9 +- src/commands/decompress.rs | 8 +- src/commands/list.rs | 7 +- src/extension.rs | 7 +- 6 files changed, 305 insertions(+), 143 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dde3d3ccb..b58cba94a 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.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91dd7a30dbf611fc3c790404c9ef8e1631971a9dd020a45905c7685727e9cf43" +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" @@ -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 = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +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.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf83efe73d438a1aad929279a2fc0ff3fb32f841421c09a64f3d04c4d162a23" +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..3e52c1bb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ unrar = "0.5.2" xz2 = "0.1.7" zip = { version = "0.6.6", default-features = false, features = ["time"] } zstd = { version = "0.13.0", default-features = false } +sevenz-rust = {version = "0.2.9", features = ["compress"]} [target.'cfg(not(unix))'.dependencies] is_executable = "1.0.1" diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 72e92c490..50c199929 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -79,7 +79,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) }; @@ -121,10 +121,15 @@ pub fn compress_files( )?; vec_buffer.rewind()?; io::copy(&mut vec_buffer, &mut writer)?; - } + }, Rar => { archive::rar::no_compression_notice(); return Ok(false); + }, + SevenZip => { + for file in files.iter() { + sevenz_rust::compress_to_path(file.as_path(), output_path).unwrap(); // todo error return + } } } diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 9ae0adeb0..e9539f500 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -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) }; @@ -145,7 +145,7 @@ pub fn decompress_file( } else { return Ok(()); } - } + }, Rar => { type UnpackResult = crate::Result; let unpack_fn: Box UnpackResult> = if formats.len() > 1 { @@ -163,6 +163,10 @@ pub fn decompress_file( } else { return Ok(()); } + }, + SevenZip => { + sevenz_rust::decompress_file(input_file_path, output_dir).unwrap(); // todo error return + 1 } }; diff --git a/src/commands/list.rs b/src/commands/list.rs index e33d9997e..c6c282369 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) }; @@ -77,7 +77,7 @@ pub fn list_archive_contents( let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?; Box::new(crate::archive::zip::list_archive(zip_archive)) - } + }, Rar => { if formats.len() > 1 { let mut temp_file = tempfile::NamedTempFile::new()?; @@ -86,6 +86,9 @@ pub fn list_archive_contents( } else { Box::new(crate::archive::rar::list_archive(archive_path)) } + }, + SevenZip => { + unimplemented!(); // todo implement 7z list } 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/extension.rs b/src/extension.rs index 85451cb3b..b31f50169 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -7,7 +7,7 @@ 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 +74,8 @@ pub enum CompressionFormat { Zip, /// .rar Rar, + /// .7z + SevenZip, } impl CompressionFormat { @@ -81,7 +83,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 +112,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(), From f34fee1ed363193de01ddd32651e5df2eda7ee24 Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Sun, 2 Apr 2023 11:01:56 +0000 Subject: [PATCH 03/23] style: cargo fmt --- src/commands/compress.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 50c199929..30a6460a4 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -128,7 +128,8 @@ pub fn compress_files( }, SevenZip => { for file in files.iter() { - sevenz_rust::compress_to_path(file.as_path(), output_path).unwrap(); // todo error return + sevenz_rust::compress_to_path(file.as_path(), output_path).unwrap(); + // todo error return } } } From e0afceed21b5ff027ae0dcd104352eb86ff84be7 Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Mon, 3 Apr 2023 10:18:36 +0000 Subject: [PATCH 04/23] [ci skip] not ready --- src/commands/list.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/commands/list.rs b/src/commands/list.rs index c6c282369..270da8adb 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -88,7 +88,13 @@ pub fn list_archive_contents( } }, SevenZip => { - unimplemented!(); // todo implement 7z list + let mut a = Vec::new(); + + sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| { + a.push(Ok(FileInArchive{path: entry.name().into(), is_dir: entry.is_directory()})); + Ok(true) + }); + Box::new(a.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!"); From 40528a809e7627bd0a576c65a86ed5d095542d6e Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Mon, 3 Apr 2023 13:48:05 +0000 Subject: [PATCH 05/23] fix: now recursivly compress 7z --- src/commands/compress.rs | 16 +++++++++++++--- src/commands/decompress.rs | 2 +- src/commands/list.rs | 9 +++++---- src/commands/mod.rs | 17 ++++++++++++++++- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 30a6460a4..2ee20c52a 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -13,6 +13,8 @@ use crate::{ QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; +use super::copy_recursively; + /// Compress files into `output_file`. /// /// # Arguments: @@ -127,10 +129,18 @@ pub fn compress_files( return Ok(false); }, SevenZip => { - for file in files.iter() { - sevenz_rust::compress_to_path(file.as_path(), output_path).unwrap(); - // todo error return + let tmpdir = tempfile::tempdir()?; + + for filep in files.iter() { + if filep.is_dir() { + copy_recursively(filep, tmpdir.path() + .join(filep.strip_prefix(std::env::current_dir()?).expect("copy folder error")))?; + } else { + fs::copy(filep, tmpdir.path().join(filep.file_name().expect("no filename in file")))?; + } } + + sevenz_rust::compress_to_path(tmpdir.path(), output_path).expect("can't compress 7zip archive"); } } diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index e9539f500..d7243cc0d 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -165,7 +165,7 @@ pub fn decompress_file( } }, SevenZip => { - sevenz_rust::decompress_file(input_file_path, output_dir).unwrap(); // todo error return + sevenz_rust::decompress_file(input_file_path, output_dir).expect("can't decompress"); 1 } }; diff --git a/src/commands/list.rs b/src/commands/list.rs index 270da8adb..5ddbd71b7 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -1,6 +1,7 @@ use std::{ io::{self, BufReader, Read}, path::Path, + cell::RefCell }; use fs_err as fs; @@ -88,13 +89,13 @@ pub fn list_archive_contents( } }, SevenZip => { - let mut a = Vec::new(); + let a = RefCell::new(Vec::new()); sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| { - a.push(Ok(FileInArchive{path: entry.name().into(), is_dir: entry.is_directory()})); + a.borrow_mut().push(Ok(FileInArchive{path: entry.name().into(), is_dir: entry.is_directory()})); Ok(true) - }); - Box::new(a.into_iter()) + }).expect("failed to get 7z file list"); + Box::new(a.into_inner().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..d4d3e8698 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -4,7 +4,7 @@ mod compress; mod decompress; mod list; -use std::{ops::ControlFlow, path::PathBuf}; +use std::{ops::ControlFlow, path::{PathBuf, Path}, fs}; use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use utils::colors; @@ -21,6 +21,21 @@ use crate::{ warning, CliArgs, QuestionPolicy, }; +/// Copy files from source to destination recursively. +fn copy_recursively(source: impl AsRef, destination: impl AsRef) -> std::io::Result<()> { + fs::create_dir_all(&destination)?; + for entry in fs::read_dir(source)? { + let entry = entry?; + let filetype = entry.file_type()?; + if filetype.is_dir() { + copy_recursively(entry.path(), destination.as_ref().join(entry.file_name()))?; + } else { + fs::copy(entry.path(), destination.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + /// Warn the user that (de)compressing this .zip archive might freeze their system. fn warn_user_about_loading_zip_in_memory() { const ZIP_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ From 53eef87c39597464ff676daaf1bc41bfc7bbb500 Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Mon, 3 Apr 2023 13:53:00 +0000 Subject: [PATCH 06/23] [ci skip] add pr to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index edfb9896c..3338e0217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ Categories Used: - `--fast` and `--slow` [\#374](https://github.com/ouch-org/ouch/pull/374) ([figsoda](https://github.com/figsoda)) - Add `--format` option [\#341](https://github.com/ouch-org/ouch/pull/341) ([figsoda](https://github.com/figsoda)) +- Add support for 7z [\#393](https://github.com/ouch-org/ouch/pull/393) ([MisileLab](https://github.com/misilelab)) + ### Improvements - Multi-threaded compression for gzip and snappy using gzp [\#348](https://github.com/ouch-org/ouch/pull/348) ([figsoda](https://github.com/figsoda)) From d3425ca84b2045a9f7cb6286e2552808504b15b9 Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Mon, 17 Apr 2023 13:23:55 +0000 Subject: [PATCH 07/23] feat: decompress with count --- src/commands/decompress.rs | 2 +- src/error.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index d7243cc0d..e1392a385 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -166,7 +166,7 @@ pub fn decompress_file( }, SevenZip => { sevenz_rust::decompress_file(input_file_path, output_dir).expect("can't decompress"); - 1 + fs::read_dir(output_dir)?.count() } }; diff --git a/src/error.rs b/src/error.rs index 896f383ac..3e755eab9 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,9 @@ 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}") From 17b68ad097ff18e4acea591875bea95f6c792884 Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Mon, 17 Apr 2023 13:27:35 +0000 Subject: [PATCH 08/23] feat: good error output --- src/commands/decompress.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index e1392a385..206d6c204 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -165,7 +165,11 @@ pub fn decompress_file( } }, SevenZip => { - sevenz_rust::decompress_file(input_file_path, output_dir).expect("can't decompress"); + sevenz_rust::decompress_file(input_file_path, output_dir).map_err( + |x| { + crate::Error::SevenzipError(x) + } + )?; fs::read_dir(output_dir)?.count() } }; From 90d48dbeaa1e1a862ea2287e7eb1de9da80cf87c Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Mon, 17 Apr 2023 13:33:01 +0000 Subject: [PATCH 09/23] feat: good error handling --- src/commands/list.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/commands/list.rs b/src/commands/list.rs index 5ddbd71b7..fc29b005a 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -94,7 +94,9 @@ pub fn list_archive_contents( sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| { a.borrow_mut().push(Ok(FileInArchive{path: entry.name().into(), is_dir: entry.is_directory()})); Ok(true) - }).expect("failed to get 7z file list"); + }).map_err( + |e| crate::Error::SevenzipError(e) + )?; Box::new(a.into_inner().into_iter()) } Gzip | Bzip | Lz4 | Lzma | Snappy | Zstd => { From 2bfa07397bc55e29951f8055f3c3a32ab0177895 Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Mon, 17 Apr 2023 14:36:15 +0000 Subject: [PATCH 10/23] feat: cleanup codes and more error handling --- src/commands/compress.rs | 29 +++++++++++++++++++---------- src/commands/decompress.rs | 6 +----- src/commands/list.rs | 12 +++++++----- src/commands/mod.rs | 17 +---------------- src/error.rs | 6 ++---- 5 files changed, 30 insertions(+), 40 deletions(-) diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 2ee20c52a..4ecf82c6b 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -1,4 +1,5 @@ use std::{ + env::current_dir, io::{self, BufWriter, Cursor, Seek, Write}, path::{Path, PathBuf}, }; @@ -13,8 +14,6 @@ use crate::{ QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; -use super::copy_recursively; - /// Compress files into `output_file`. /// /// # Arguments: @@ -129,18 +128,28 @@ pub fn compress_files( return Ok(false); }, SevenZip => { - let tmpdir = tempfile::tempdir()?; + let mut writer = + sevenz_rust::SevenZWriter::create(output_path).map_err(|e| crate::Error::SevenzipError(e))?; for filep in files.iter() { - if filep.is_dir() { - copy_recursively(filep, tmpdir.path() - .join(filep.strip_prefix(std::env::current_dir()?).expect("copy folder error")))?; - } else { - fs::copy(filep, tmpdir.path().join(filep.file_name().expect("no filename in file")))?; - } + writer + .push_archive_entry::( + sevenz_rust::SevenZWriter::::create_archive_entry( + filep, + filep + .strip_prefix(current_dir()?) + .expect("StripPrefix Failed") + .as_os_str() + .to_str() + .unwrap() + .to_string(), + ), + None, + ) + .map_err(|e| crate::Error::SevenzipError(e))?; } - sevenz_rust::compress_to_path(tmpdir.path(), output_path).expect("can't compress 7zip archive"); + writer.finish()?; } } diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 206d6c204..1b9c181f9 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -165,11 +165,7 @@ pub fn decompress_file( } }, SevenZip => { - sevenz_rust::decompress_file(input_file_path, output_dir).map_err( - |x| { - crate::Error::SevenzipError(x) - } - )?; + sevenz_rust::decompress_file(input_file_path, output_dir).map_err(|x| crate::Error::SevenzipError(x))?; fs::read_dir(output_dir)?.count() } }; diff --git a/src/commands/list.rs b/src/commands/list.rs index fc29b005a..a2ae9d13c 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -1,7 +1,7 @@ use std::{ + cell::RefCell, io::{self, BufReader, Read}, path::Path, - cell::RefCell }; use fs_err as fs; @@ -92,11 +92,13 @@ pub fn list_archive_contents( let a = RefCell::new(Vec::new()); sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| { - a.borrow_mut().push(Ok(FileInArchive{path: entry.name().into(), is_dir: entry.is_directory()})); + a.borrow_mut().push(Ok(FileInArchive { + path: entry.name().into(), + is_dir: entry.is_directory(), + })); Ok(true) - }).map_err( - |e| crate::Error::SevenzipError(e) - )?; + }) + .map_err(|e| crate::Error::SevenzipError(e))?; Box::new(a.into_inner().into_iter()) } Gzip | Bzip | Lz4 | Lzma | Snappy | Zstd => { diff --git a/src/commands/mod.rs b/src/commands/mod.rs index d4d3e8698..e5be2330a 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -4,7 +4,7 @@ mod compress; mod decompress; mod list; -use std::{ops::ControlFlow, path::{PathBuf, Path}, fs}; +use std::{ops::ControlFlow, path::PathBuf}; use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use utils::colors; @@ -21,21 +21,6 @@ use crate::{ warning, CliArgs, QuestionPolicy, }; -/// Copy files from source to destination recursively. -fn copy_recursively(source: impl AsRef, destination: impl AsRef) -> std::io::Result<()> { - fs::create_dir_all(&destination)?; - for entry in fs::read_dir(source)? { - let entry = entry?; - let filetype = entry.file_type()?; - if filetype.is_dir() { - copy_recursively(entry.path(), destination.as_ref().join(entry.file_name()))?; - } else { - fs::copy(entry.path(), destination.as_ref().join(entry.file_name()))?; - } - } - Ok(()) -} - /// Warn the user that (de)compressing this .zip archive might freeze their system. fn warn_user_about_loading_zip_in_memory() { const ZIP_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ diff --git a/src/error.rs b/src/error.rs index 3e755eab9..b3c0bef70 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,7 +35,7 @@ pub enum Error { /// Invalid format passed to `--format` InvalidFormat { reason: String }, /// From sevenz_rust::Error - SevenzipError(sevenz_rust::Error) + SevenzipError(sevenz_rust::Error), } /// Alias to std's Result with ouch's Error @@ -141,9 +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()) - }, + Error::SevenzipError(reason) => FinalError::with_title("7z error").detail(reason.to_string()), }; write!(f, "{err}") From bea5999210c90f9ef73a2e1d5dbcb76c067e7cfd Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Tue, 18 Apr 2023 04:05:11 +0000 Subject: [PATCH 11/23] feat: RefCell remove --- Cargo.toml | 2 +- src/commands/list.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3e52c1bb4..1e475bb56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ unrar = "0.5.2" xz2 = "0.1.7" zip = { version = "0.6.6", default-features = false, features = ["time"] } zstd = { version = "0.13.0", default-features = false } -sevenz-rust = {version = "0.2.9", features = ["compress"]} +sevenz-rust = {version = "0.2.10", features = ["compress"]} [target.'cfg(not(unix))'.dependencies] is_executable = "1.0.1" diff --git a/src/commands/list.rs b/src/commands/list.rs index a2ae9d13c..9d21e3656 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -1,5 +1,4 @@ use std::{ - cell::RefCell, io::{self, BufReader, Read}, path::Path, }; @@ -89,17 +88,17 @@ pub fn list_archive_contents( } }, SevenZip => { - let a = RefCell::new(Vec::new()); + let mut a = Vec::new(); sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| { - a.borrow_mut().push(Ok(FileInArchive { + a.push(Ok(FileInArchive { path: entry.name().into(), is_dir: entry.is_directory(), })); Ok(true) }) .map_err(|e| crate::Error::SevenzipError(e))?; - Box::new(a.into_inner().into_iter()) + Box::new(a.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!"); From 31db774aff3994c57fbd5cc4e7196ad6cfaf2164 Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Wed, 19 Apr 2023 15:44:44 +0000 Subject: [PATCH 12/23] fix: Incorrect decompressed file count --- src/commands/decompress.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 1b9c181f9..23ebd7f08 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -165,7 +165,13 @@ pub fn decompress_file( } }, SevenZip => { - sevenz_rust::decompress_file(input_file_path, output_dir).map_err(|x| crate::Error::SevenzipError(x))?; + let mut count = 0; + sevenz_rust::decompress_file_with_extract_fn(input_file_path, output_dir, + |entry, reader, dest| { + count += 1; + sevenz_rust::default_entry_extract_fn(entry, reader, dest) + } + ).map_err(|x| crate::Error::SevenzipError(x))?; fs::read_dir(output_dir)?.count() } }; From d3b59b8bc3e8fd61badaeda9cdc123dc151d1a4e Mon Sep 17 00:00:00 2001 From: MisileLaboratory Date: Thu, 20 Apr 2023 23:14:56 +0900 Subject: [PATCH 13/23] fix: Incorrect count --- src/commands/decompress.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 23ebd7f08..675c54928 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -172,7 +172,7 @@ pub fn decompress_file( sevenz_rust::default_entry_extract_fn(entry, reader, dest) } ).map_err(|x| crate::Error::SevenzipError(x))?; - fs::read_dir(output_dir)?.count() + count } }; From d22e289a26c31a7a5408e1d404c50821a36c339b Mon Sep 17 00:00:00 2001 From: misilelab Date: Thu, 11 May 2023 20:28:05 +0900 Subject: [PATCH 14/23] impl except test --- src/archive/mod.rs | 1 + src/archive/sevenz.rs | 40 ++++++++++++++++++++++++++++++++++++++ src/commands/compress.rs | 24 +---------------------- src/commands/decompress.rs | 16 ++++++++------- src/commands/list.rs | 9 ++++++++- src/utils/fs.rs | 5 +++++ 6 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 src/archive/sevenz.rs 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..effae798b --- /dev/null +++ b/src/archive/sevenz.rs @@ -0,0 +1,40 @@ +//! SevenZip archive format compress function +use std::{ + env::current_dir, + path::{Path, PathBuf}, +}; + +pub fn compress_sevenz(files: Vec, output_path: &Path) -> crate::Result { + let mut writer = sevenz_rust::SevenZWriter::create(output_path).map_err(crate::Error::SevenzipError)?; + + for filep in files.iter() { + writer + .push_archive_entry::( + sevenz_rust::SevenZWriter::::create_archive_entry( + filep, + filep + .strip_prefix(current_dir()?) + .expect("StripPrefix Failed") + .as_os_str() + .to_str() + .unwrap() + .to_string(), + ), + None, + ) + .map_err(crate::Error::SevenzipError)?; + } + + writer.finish()?; + Ok(true) +} + +pub fn decompress_sevenz(input_file_path: &Path, output_path: &Path) -> crate::Result { + let mut count: usize = 0; + sevenz_rust::decompress_file_with_extract_fn(input_file_path, output_path, |entry, reader, dest| { + count += 1; + sevenz_rust::default_entry_extract_fn(entry, reader, dest) + }) + .map_err(crate::Error::SevenzipError)?; + Ok(count) +} diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 4ecf82c6b..bd2a91bbc 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -1,5 +1,4 @@ use std::{ - env::current_dir, io::{self, BufWriter, Cursor, Seek, Write}, path::{Path, PathBuf}, }; @@ -128,28 +127,7 @@ pub fn compress_files( return Ok(false); }, SevenZip => { - let mut writer = - sevenz_rust::SevenZWriter::create(output_path).map_err(|e| crate::Error::SevenzipError(e))?; - - for filep in files.iter() { - writer - .push_archive_entry::( - sevenz_rust::SevenZWriter::::create_archive_entry( - filep, - filep - .strip_prefix(current_dir()?) - .expect("StripPrefix Failed") - .as_os_str() - .to_str() - .unwrap() - .to_string(), - ), - None, - ) - .map_err(|e| crate::Error::SevenzipError(e))?; - } - - writer.finish()?; + archive::sevenz::compress_sevenz(files, output_path)?; } } diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 675c54928..9f70433b2 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -165,14 +165,16 @@ pub fn decompress_file( } }, SevenZip => { - let mut count = 0; - sevenz_rust::decompress_file_with_extract_fn(input_file_path, output_dir, - |entry, reader, dest| { - count += 1; - sevenz_rust::default_entry_extract_fn(entry, reader, dest) + if let ControlFlow::Continue(files) = smart_unpack( + |output_dir| crate::archive::sevenz::decompress_sevenz(input_file_path, output_dir), + output_dir, + &output_file_path, + question_policy, + )? { + files + } else { + return Ok(()); } - ).map_err(|x| crate::Error::SevenzipError(x))?; - count } }; diff --git a/src/commands/list.rs b/src/commands/list.rs index 9d21e3656..cbf20a602 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -88,6 +88,13 @@ pub fn list_archive_contents( } }, 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 a = Vec::new(); sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| { @@ -97,7 +104,7 @@ pub fn list_archive_contents( })); Ok(true) }) - .map_err(|e| crate::Error::SevenzipError(e))?; + .map_err(crate::Error::SevenzipError)?; Box::new(a.into_iter()) } Gzip | Bzip | Lz4 | Lzma | Snappy | Zstd => { 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 } From 34c8291f5c048c743f59787b17bd93f862df2da2 Mon Sep 17 00:00:00 2001 From: figsoda Date: Wed, 24 May 2023 10:52:31 -0400 Subject: [PATCH 15/23] add 7z to tests --- tests/integration.rs | 2 ++ tests/mime.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) 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", From 11ef9c17926e83127daad6af5ca26e0f9de38cc3 Mon Sep 17 00:00:00 2001 From: figsoda Date: Wed, 24 May 2023 10:54:39 -0400 Subject: [PATCH 16/23] small cleanups --- Cargo.toml | 1 + src/commands/list.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1e475bb56..82bb09ce1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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.2.10", features = ["compress"]} snap = "1.1.0" tar = "0.4.40" tempfile = "3.8.1" diff --git a/src/commands/list.rs b/src/commands/list.rs index cbf20a602..0fdaa52f4 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -95,17 +95,17 @@ pub fn list_archive_contents( } } - let mut a = Vec::new(); + let mut files = Vec::new(); sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| { - a.push(Ok(FileInArchive { + files.push(Ok(FileInArchive { path: entry.name().into(), is_dir: entry.is_directory(), })); Ok(true) }) .map_err(crate::Error::SevenzipError)?; - Box::new(a.into_iter()) + 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!"); From 1240024cc3948a4589b06e33b7c3153028a729b8 Mon Sep 17 00:00:00 2001 From: MisileLab Date: Sun, 20 Aug 2023 02:32:59 +0900 Subject: [PATCH 17/23] idk i just merged and patch some Signed-off-by: MisileLab --- Cargo.lock | 8 ++++---- Cargo.toml | 3 +-- src/archive/sevenz.rs | 13 +++++-------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b58cba94a..6b5cc718d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -776,9 +776,9 @@ dependencies = [ [[package]] name = "nt-time" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91dd7a30dbf611fc3c790404c9ef8e1631971a9dd020a45905c7685727e9cf43" +checksum = "b1d4f129ec1833164165b1711c30b4fa4a2b12784ffefa9fe877b016704b7165" dependencies = [ "chrono", "time", @@ -1172,9 +1172,9 @@ dependencies = [ [[package]] name = "sevenz-rust" -version = "0.2.11" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf83efe73d438a1aad929279a2fc0ff3fb32f841421c09a64f3d04c4d162a23" +checksum = "33c7d45965e6557597a30e2d12e15d6a5f438bb7cbc11d5dbe89762541c0cd42" dependencies = [ "bit-set", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index 82bb09ce1..c31fbc445 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +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.2.10", features = ["compress"]} +sevenz-rust = {version = "0.5.3", features = ["compress"]} snap = "1.1.0" tar = "0.4.40" tempfile = "3.8.1" @@ -35,7 +35,6 @@ unrar = "0.5.2" xz2 = "0.1.7" zip = { version = "0.6.6", default-features = false, features = ["time"] } zstd = { version = "0.13.0", default-features = false } -sevenz-rust = {version = "0.2.10", features = ["compress"]} [target.'cfg(not(unix))'.dependencies] is_executable = "1.0.1" diff --git a/src/archive/sevenz.rs b/src/archive/sevenz.rs index effae798b..4dbf639d9 100644 --- a/src/archive/sevenz.rs +++ b/src/archive/sevenz.rs @@ -1,8 +1,7 @@ //! SevenZip archive format compress function -use std::{ - env::current_dir, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; + +use crate::utils::strip_cur_dir; pub fn compress_sevenz(files: Vec, output_path: &Path) -> crate::Result { let mut writer = sevenz_rust::SevenZWriter::create(output_path).map_err(crate::Error::SevenzipError)?; @@ -10,11 +9,9 @@ pub fn compress_sevenz(files: Vec, output_path: &Path) -> crate::Result for filep in files.iter() { writer .push_archive_entry::( - sevenz_rust::SevenZWriter::::create_archive_entry( + sevenz_rust::SevenZArchiveEntry::from_path( filep, - filep - .strip_prefix(current_dir()?) - .expect("StripPrefix Failed") + strip_cur_dir(filep) .as_os_str() .to_str() .unwrap() From 7c95e68c03a1d202c73facecd08ea1da396f1e5f Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 24 Nov 2023 19:16:56 -0500 Subject: [PATCH 18/23] feat: implement 7zip support for compression and decompression This also fixes symlink canonicalization for Windows and fixes UI tests on Windows. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/archive/sevenz.rs | 166 +++++++++++++++++++++++++++++++------ src/archive/zip.rs | 2 +- src/commands/compress.rs | 16 +++- src/commands/decompress.rs | 15 +++- src/commands/mod.rs | 10 +++ src/main.rs | 2 +- tests/ui.rs | 17 ++++ 9 files changed, 199 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b5cc718d..0f0ac61ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -827,7 +827,7 @@ dependencies = [ "clap", "clap_complete", "clap_mangen", - "filetime", + "filetime_creation", "flate2", "fs-err", "gzp", diff --git a/Cargo.toml b/Cargo.toml index c31fbc445..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"] } diff --git a/src/archive/sevenz.rs b/src/archive/sevenz.rs index 4dbf639d9..7269d1653 100644 --- a/src/archive/sevenz.rs +++ b/src/archive/sevenz.rs @@ -1,36 +1,150 @@ //! SevenZip archive format compress function -use std::path::{Path, PathBuf}; - -use crate::utils::strip_cur_dir; - -pub fn compress_sevenz(files: Vec, output_path: &Path) -> crate::Result { - let mut writer = sevenz_rust::SevenZWriter::create(output_path).map_err(crate::Error::SevenzipError)?; - - for filep in files.iter() { - writer - .push_archive_entry::( - sevenz_rust::SevenZArchiveEntry::from_path( - filep, - strip_cur_dir(filep) - .as_os_str() - .to_str() - .unwrap() - .to_string(), - ), - None, - ) - .map_err(crate::Error::SevenzipError)?; +use std::{ + env, + fs::File, + path::{Path, PathBuf}, io::{Write, Seek, Read}, +}; + +use same_file::Handle; + +use crate::{ + info, + utils::{self, cd_into_same_dir_as, EscapedPathDisplay, FileVisibilityPolicy, Bytes}, + 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).map_err(crate::Error::SevenzipError)?; + let output_handle = Handle::from_path(output_path); + for filename in files { + let previous_location = cd_into_same_dir_as(filename)?; + + // Safe unwrap, input shall be treated before + 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(ref 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()); + } + }; + + if metadata.is_dir() { + writer + .push_archive_entry::( + sevenz_rust::SevenZArchiveEntry::from_path(path, path.to_str().unwrap().to_owned()), + None, + ) + .map_err(crate::Error::SevenzipError)?; + } else { + let reader = File::open(path)?; + writer + .push_archive_entry::( + sevenz_rust::SevenZArchiveEntry::from_path(path, path.to_str().unwrap().to_owned()), + Some(reader), + ) + .map_err(crate::Error::SevenzipError)?; + } + } + + env::set_current_dir(previous_location)?; } - writer.finish()?; - Ok(true) + let bytes = writer.finish()?; + Ok(bytes) } -pub fn decompress_sevenz(input_file_path: &Path, output_path: &Path) -> crate::Result { +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_file_with_extract_fn(input_file_path, output_path, |entry, reader, dest| { + sevenz_rust::decompress_with_extract_fn(reader, output_path, |entry, reader, dest| { count += 1; - sevenz_rust::default_entry_extract_fn(entry, reader, dest) + // 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() { + // This is printed for every file in the archive 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, "File {} extracted to \"{}\"", entry.name(), file_path.display()); + } + let dir = dest; + if !dir.exists() { + std::fs::create_dir_all(dir)?; + } + } else { + // same reason is in _is_dir: long, often not needed text + if !quiet { + info!( + inaccessible, + "{:?} extracted. ({})", + file_path.display(), + Bytes::new(entry.size()), + ); + } + let path = dest; + path.parent().and_then(|p| { + if !p.exists() { + std::fs::create_dir_all(p).ok() + } else { + None + } + }); + let file = File::create(path)?; + let mut writer = BufWriter::new(file); + std::io::copy(reader, &mut writer)?; + ft::set_file_handle_times( + writer.get_ref(), + 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) }) .map_err(crate::Error::SevenzipError)?; Ok(count) diff --git a/src/archive/zip.rs b/src/archive/zip.rs index 60cc440db..4de1af9c9 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; diff --git a/src/commands/compress.rs b/src/commands/compress.rs index bd2a91bbc..c73191d01 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -13,6 +13,8 @@ use crate::{ QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; +use super::warn_user_about_loading_sevenz_in_memory; + /// Compress files into `output_file`. /// /// # Arguments: @@ -127,7 +129,19 @@ pub fn compress_files( return Ok(false); }, SevenZip => { - archive::sevenz::compress_sevenz(files, output_path)?; + + 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)?; } } diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 9f70433b2..b7cd0b33e 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_zip_in_memory, warn_user_about_loading_sevenz_in_memory}, extension::{ split_first_compression_format, CompressionFormat::{self, *}, @@ -165,8 +165,19 @@ pub fn decompress_file( } }, 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(input_file_path, output_dir), + |output_dir| crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, quiet), output_dir, &output_file_path, question_policy, 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/main.rs b/src/main.rs index 809c4e6d8..060177d0a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,7 @@ use utils::{QuestionAction, QuestionPolicy}; const BUFFER_CAPACITY: usize = 1024 * 32; /// Current directory or empty directory -static CURRENT_DIRECTORY: Lazy = Lazy::new(|| env::current_dir().unwrap_or_default()); +static CURRENT_DIRECTORY: Lazy = Lazy::new(|| std::fs::canonicalize(env::current_dir().unwrap_or_default()).unwrap_or_default()); /// The status code returned from `ouch` on error pub const EXIT_FAILURE: i32 = libc::EXIT_FAILURE; diff --git a/tests/ui.rs b/tests/ui.rs index c72f05224..eee6e33c2 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -62,8 +62,12 @@ fn ui_test_err_compress_missing_extension() { let (_dropper, dir) = testdir().unwrap(); // prepare + #[cfg(not(windows))] run_in(dir, "touch", "input").unwrap(); + #[cfg(windows)] + run_in(dir, "cmd", "/C copy nul input").unwrap(); + ui!(run_ouch("ouch compress input output", dir)); } @@ -71,8 +75,16 @@ fn ui_test_err_compress_missing_extension() { fn ui_test_err_decompress_missing_extension() { let (_dropper, dir) = testdir().unwrap(); + #[cfg(not(windows))] run_in(dir, "touch", "a b.unknown").unwrap(); + #[cfg(windows)] + run_in(dir, "cmd", "/C copy nul a").unwrap(); + + #[cfg(windows)] + run_in(dir, "cmd", "/C copy nul b.unknown").unwrap(); + + ui!(run_ouch("ouch decompress a", dir)); ui!(run_ouch("ouch decompress a b.unknown", dir)); ui!(run_ouch("ouch decompress b.unknown", dir)); @@ -92,8 +104,12 @@ fn ui_test_ok_compress() { let (_dropper, dir) = testdir().unwrap(); // prepare + #[cfg(not(windows))] run_in(dir, "touch", "input").unwrap(); + #[cfg(windows)] + run_in(dir, "cmd", "/C copy nul input").unwrap(); + ui!(run_ouch("ouch compress input output.zip", dir)); ui!(run_ouch("ouch compress input output.gz", dir)); } @@ -103,6 +119,7 @@ fn ui_test_ok_decompress() { let (_dropper, dir) = testdir().unwrap(); // prepare + #[cfg(not(windows))] run_in(dir, "touch", "input").unwrap(); run_ouch("ouch compress input output.zst", dir); From a6f86bfe70e2a37e6db8339721bcd84f590c943b Mon Sep 17 00:00:00 2001 From: Flat Date: Sat, 25 Nov 2023 14:50:27 -0500 Subject: [PATCH 19/23] chore: update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3338e0217..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 @@ -39,8 +40,6 @@ Categories Used: - `--fast` and `--slow` [\#374](https://github.com/ouch-org/ouch/pull/374) ([figsoda](https://github.com/figsoda)) - Add `--format` option [\#341](https://github.com/ouch-org/ouch/pull/341) ([figsoda](https://github.com/figsoda)) -- Add support for 7z [\#393](https://github.com/ouch-org/ouch/pull/393) ([MisileLab](https://github.com/misilelab)) - ### Improvements - Multi-threaded compression for gzip and snappy using gzp [\#348](https://github.com/ouch-org/ouch/pull/348) ([figsoda](https://github.com/figsoda)) From d448c80c939fa34620b41e1c4d580b64032bdaf7 Mon Sep 17 00:00:00 2001 From: Flat Date: Sat, 25 Nov 2023 14:55:57 -0500 Subject: [PATCH 20/23] chore: cargo fmt --- src/archive/sevenz.rs | 36 ++++++++++++++++++++++-------------- src/commands/compress.rs | 8 +++----- src/commands/decompress.rs | 6 +++--- src/commands/list.rs | 4 ++-- src/extension.rs | 4 +++- src/main.rs | 3 ++- tests/ui.rs | 1 - 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/archive/sevenz.rs b/src/archive/sevenz.rs index 7269d1653..d88c0b283 100644 --- a/src/archive/sevenz.rs +++ b/src/archive/sevenz.rs @@ -2,14 +2,15 @@ use std::{ env, fs::File, - path::{Path, PathBuf}, io::{Write, Seek, Read}, + io::{Read, Seek, Write}, + path::{Path, PathBuf}, }; use same_file::Handle; use crate::{ info, - utils::{self, cd_into_same_dir_as, EscapedPathDisplay, FileVisibilityPolicy, Bytes}, + utils::{self, cd_into_same_dir_as, Bytes, EscapedPathDisplay, FileVisibilityPolicy}, warning, }; @@ -21,8 +22,8 @@ pub fn compress_sevenz( quiet: bool, ) -> crate::Result where -W: Write + Seek { - + W: Write + Seek, +{ let mut writer = sevenz_rust::SevenZWriter::new(writer).map_err(crate::Error::SevenzipError)?; let output_handle = Handle::from_path(output_path); for filename in files { @@ -91,8 +92,10 @@ W: Write + Seek { Ok(bytes) } -pub fn decompress_sevenz(reader: R, output_path: &Path, quiet: bool) -> crate::Result -where R: Read+ Seek { +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, dest| { count += 1; @@ -102,15 +105,20 @@ where R: Read+ Seek { use filetime_creation as ft; let file_path = output_path.join(entry.name()); - + if entry.is_directory() { - // This is printed for every file in the archive 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, "File {} extracted to \"{}\"", entry.name(), file_path.display()); - } + // This is printed for every file in the archive 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, + "File {} extracted to \"{}\"", + entry.name(), + file_path.display() + ); + } let dir = dest; if !dir.exists() { std::fs::create_dir_all(dir)?; diff --git a/src/commands/compress.rs b/src/commands/compress.rs index c73191d01..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, @@ -13,8 +14,6 @@ use crate::{ QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; -use super::warn_user_about_loading_sevenz_in_memory; - /// Compress files into `output_file`. /// /// # Arguments: @@ -123,13 +122,12 @@ pub fn compress_files( )?; vec_buffer.rewind()?; io::copy(&mut vec_buffer, &mut writer)?; - }, + } Rar => { archive::rar::no_compression_notice(); return Ok(false); - }, + } SevenZip => { - if !formats.is_empty() { warn_user_about_loading_sevenz_in_memory(); diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index b7cd0b33e..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, warn_user_about_loading_sevenz_in_memory}, + commands::{warn_user_about_loading_sevenz_in_memory, warn_user_about_loading_zip_in_memory}, extension::{ split_first_compression_format, CompressionFormat::{self, *}, @@ -145,7 +145,7 @@ pub fn decompress_file( } else { return Ok(()); } - }, + } Rar => { type UnpackResult = crate::Result; let unpack_fn: Box UnpackResult> = if formats.len() > 1 { @@ -163,7 +163,7 @@ pub fn decompress_file( } else { return Ok(()); } - }, + } SevenZip => { if formats.len() > 1 { warn_user_about_loading_sevenz_in_memory(); diff --git a/src/commands/list.rs b/src/commands/list.rs index 0fdaa52f4..092ea3cb8 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -77,7 +77,7 @@ pub fn list_archive_contents( let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?; Box::new(crate::archive::zip::list_archive(zip_archive)) - }, + } Rar => { if formats.len() > 1 { let mut temp_file = tempfile::NamedTempFile::new()?; @@ -86,7 +86,7 @@ pub fn list_archive_contents( } else { Box::new(crate::archive::rar::list_archive(archive_path)) } - }, + } SevenZip => { if formats.len() > 1 { warn_user_about_loading_zip_in_memory(); diff --git a/src/extension.rs b/src/extension.rs index b31f50169..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", "7z"]; +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"; diff --git a/src/main.rs b/src/main.rs index 060177d0a..9a161e55e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,8 @@ use utils::{QuestionAction, QuestionPolicy}; const BUFFER_CAPACITY: usize = 1024 * 32; /// Current directory or empty directory -static CURRENT_DIRECTORY: Lazy = Lazy::new(|| std::fs::canonicalize(env::current_dir().unwrap_or_default()).unwrap_or_default()); +static CURRENT_DIRECTORY: Lazy = + Lazy::new(|| std::fs::canonicalize(env::current_dir().unwrap_or_default()).unwrap_or_default()); /// The status code returned from `ouch` on error pub const EXIT_FAILURE: i32 = libc::EXIT_FAILURE; diff --git a/tests/ui.rs b/tests/ui.rs index eee6e33c2..513dce7b8 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -83,7 +83,6 @@ fn ui_test_err_decompress_missing_extension() { #[cfg(windows)] run_in(dir, "cmd", "/C copy nul b.unknown").unwrap(); - ui!(run_ouch("ouch decompress a", dir)); ui!(run_ouch("ouch decompress a b.unknown", dir)); From 3377f5bb2cf33dcce6d39484d510ff5d71484fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos=20P=2E=20Bezerra?= Date: Sat, 25 Nov 2023 20:36:38 -0300 Subject: [PATCH 21/23] Minor refactor to archive handling code --- src/archive/sevenz.rs | 73 +++++++++++++++++++------------------------ src/archive/tar.rs | 5 +-- src/archive/zip.rs | 5 +-- src/commands/list.rs | 3 +- src/error.rs | 6 ++++ 5 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/archive/sevenz.rs b/src/archive/sevenz.rs index d88c0b283..aa234c397 100644 --- a/src/archive/sevenz.rs +++ b/src/archive/sevenz.rs @@ -1,11 +1,12 @@ //! SevenZip archive format compress function + use std::{ - env, - fs::File, + env, io, io::{Read, Seek, Write}, path::{Path, PathBuf}, }; +use fs_err as fs; use same_file::Handle; use crate::{ @@ -24,12 +25,14 @@ pub fn compress_sevenz( where W: Write + Seek, { - let mut writer = sevenz_rust::SevenZWriter::new(writer).map_err(crate::Error::SevenzipError)?; + 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)?; - // 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) { @@ -37,7 +40,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...", @@ -67,22 +70,15 @@ where } }; - if metadata.is_dir() { - writer - .push_archive_entry::( - sevenz_rust::SevenZArchiveEntry::from_path(path, path.to_str().unwrap().to_owned()), - None, - ) - .map_err(crate::Error::SevenzipError)?; + let entry_name = path.to_str().unwrap().to_owned(); + let entry = sevenz_rust::SevenZArchiveEntry::from_path(path, entry_name); + let entry_data = if metadata.is_dir() { + None } else { - let reader = File::open(path)?; - writer - .push_archive_entry::( - sevenz_rust::SevenZArchiveEntry::from_path(path, path.to_str().unwrap().to_owned()), - Some(reader), - ) - .map_err(crate::Error::SevenzipError)?; - } + Some(fs::File::open(path)?) + }; + + writer.push_archive_entry::(entry, entry_data)?; } env::set_current_dir(previous_location)?; @@ -97,7 +93,7 @@ where R: Read + Seek, { let mut count: usize = 0; - sevenz_rust::decompress_with_extract_fn(reader, output_path, |entry, reader, dest| { + 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; @@ -107,10 +103,6 @@ where let file_path = output_path.join(entry.name()); if entry.is_directory() { - // This is printed for every file in the archive 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, @@ -119,12 +111,10 @@ where file_path.display() ); } - let dir = dest; - if !dir.exists() { - std::fs::create_dir_all(dir)?; + if !path.exists() { + fs::create_dir_all(path)?; } } else { - // same reason is in _is_dir: long, often not needed text if !quiet { info!( inaccessible, @@ -133,27 +123,28 @@ where Bytes::new(entry.size()), ); } - let path = dest; - path.parent().and_then(|p| { - if !p.exists() { - std::fs::create_dir_all(p).ok() - } else { - None + + if let Some(parent) = path.parent() { + if !parent.exists() { + fs::create_dir_all(parent)?; } - }); - let file = File::create(path)?; + } + + let file = fs::File::create(path)?; let mut writer = BufWriter::new(file); - std::io::copy(reader, &mut writer)?; + io::copy(reader, &mut writer)?; + ft::set_file_handle_times( - writer.get_ref(), + 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) - }) - .map_err(crate::Error::SevenzipError)?; + })?; + 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 4de1af9c9..3ba462ce9 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -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...", diff --git a/src/commands/list.rs b/src/commands/list.rs index 092ea3cb8..f89830fe6 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -103,8 +103,7 @@ pub fn list_archive_contents( is_dir: entry.is_directory(), })); Ok(true) - }) - .map_err(crate::Error::SevenzipError)?; + })?; Box::new(files.into_iter()) } Gzip | Bzip | Lz4 | Lzma | Snappy | Zstd => { diff --git a/src/error.rs b/src/error.rs index b3c0bef70..1b48623de 100644 --- a/src/error.rs +++ b/src/error.rs @@ -189,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 { From 4374c9b53f10a48329ee7ffdf7b0adb74bd2d2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos=20P=2E=20Bezerra?= Date: Sat, 25 Nov 2023 20:37:03 -0300 Subject: [PATCH 22/23] Removing redundant code --- src/main.rs | 3 +-- tests/ui.rs | 16 ---------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9a161e55e..809c4e6d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,8 +22,7 @@ use utils::{QuestionAction, QuestionPolicy}; const BUFFER_CAPACITY: usize = 1024 * 32; /// Current directory or empty directory -static CURRENT_DIRECTORY: Lazy = - Lazy::new(|| std::fs::canonicalize(env::current_dir().unwrap_or_default()).unwrap_or_default()); +static CURRENT_DIRECTORY: Lazy = Lazy::new(|| env::current_dir().unwrap_or_default()); /// The status code returned from `ouch` on error pub const EXIT_FAILURE: i32 = libc::EXIT_FAILURE; diff --git a/tests/ui.rs b/tests/ui.rs index 513dce7b8..c72f05224 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -62,12 +62,8 @@ fn ui_test_err_compress_missing_extension() { let (_dropper, dir) = testdir().unwrap(); // prepare - #[cfg(not(windows))] run_in(dir, "touch", "input").unwrap(); - #[cfg(windows)] - run_in(dir, "cmd", "/C copy nul input").unwrap(); - ui!(run_ouch("ouch compress input output", dir)); } @@ -75,15 +71,8 @@ fn ui_test_err_compress_missing_extension() { fn ui_test_err_decompress_missing_extension() { let (_dropper, dir) = testdir().unwrap(); - #[cfg(not(windows))] run_in(dir, "touch", "a b.unknown").unwrap(); - #[cfg(windows)] - run_in(dir, "cmd", "/C copy nul a").unwrap(); - - #[cfg(windows)] - run_in(dir, "cmd", "/C copy nul b.unknown").unwrap(); - ui!(run_ouch("ouch decompress a", dir)); ui!(run_ouch("ouch decompress a b.unknown", dir)); ui!(run_ouch("ouch decompress b.unknown", dir)); @@ -103,12 +92,8 @@ fn ui_test_ok_compress() { let (_dropper, dir) = testdir().unwrap(); // prepare - #[cfg(not(windows))] run_in(dir, "touch", "input").unwrap(); - #[cfg(windows)] - run_in(dir, "cmd", "/C copy nul input").unwrap(); - ui!(run_ouch("ouch compress input output.zip", dir)); ui!(run_ouch("ouch compress input output.gz", dir)); } @@ -118,7 +103,6 @@ fn ui_test_ok_decompress() { let (_dropper, dir) = testdir().unwrap(); // prepare - #[cfg(not(windows))] run_in(dir, "touch", "input").unwrap(); run_ouch("ouch compress input output.zst", dir); From f9fba4a6832a85c9659b8aac66f59fa82dbc11a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos=20P=2E=20Bezerra?= Date: Sat, 25 Nov 2023 20:58:27 -0300 Subject: [PATCH 23/23] Report errors for non-UTF-8 entries in Zip and 7z --- src/archive/sevenz.rs | 9 +++++++-- src/archive/zip.rs | 16 +++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/archive/sevenz.rs b/src/archive/sevenz.rs index aa234c397..0847f4ca5 100644 --- a/src/archive/sevenz.rs +++ b/src/archive/sevenz.rs @@ -10,6 +10,7 @@ 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, @@ -70,8 +71,12 @@ where } }; - let entry_name = path.to_str().unwrap().to_owned(); - let entry = sevenz_rust::SevenZArchiveEntry::from_path(path, entry_name); + 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 { diff --git a/src/archive/zip.rs b/src/archive/zip.rs index 3ba462ce9..e9bde15d6 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -209,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) { @@ -220,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)?; } }