From aa5fb1a9525770648c07e8ab2303340bc69de31b Mon Sep 17 00:00:00 2001 From: Jeff Bailey Date: Sat, 22 Nov 2025 08:54:00 +0000 Subject: [PATCH] Implement minimal create and extract operations (#35) * Implement minimal create and extract operations Adds basic tar archive functionality using tar-rs library with proper error handling infrastructure. Core Implementation: - TarError enum implementing UError for uucore integration - Extract operation (-x) for unpacking tar archives - Create operation (-c) for creating tar archives from files/directories - Verbose mode (-v) for both operations - Proper CLI argument parsing with -f flag for archive file Error Handling: - Comprehensive error types (IoError, InvalidArchive, FileNotFound, etc.) - Proper error conversion from io::Error - UError trait implementation for exit code handling This provides the minimal foundation for tar functionality. Both create and extract are included together since they depend on each other for testing purposes. Includes 18 tests total: - 8 integration tests (CLI basics, create/extract operations, error handling) - 10 unit tests (CLI flag parsing, error type handling) - Unit tests organized in src/uu/tar/tests/ separate from implementation Note: Run `cargo test --workspace` to execute all tests including unit tests. * Re-add cognitive complexity warning suppression for uu_app function * Sort out tar/tarapp conflict Rename package from "tar" to "tarapp" in Cargo.toml and update dependencies to use workspace for tar. * fix(errors): improve error handling and exit codes for tar operations - Updated error messages for file not found and permission denied cases. - Fix exit codes for various TarError types to conform to standard. - Refactored extract_archive to use new error handling. - Adjusted tests to reflect updated exit codes. * Update for error changes (fixes cargo test --all) * Use arg! macro and comment out rather than remove the unimplemented items. * fix(create): Remove duplicate message for missing files in create_archive --------- Co-authored-by: Sylvestre Ledru --- .github/workflows/code-quality.yml | 2 +- .gitignore | 24 +- Cargo.lock | 501 ++++++++++++++++----------- Cargo.toml | 4 +- src/bin/tarapp.rs | 9 +- src/uu/tar/Cargo.toml | 1 + src/uu/tar/src/errors.rs | 85 +++++ src/uu/tar/src/operations/create.rs | 81 +++++ src/uu/tar/src/operations/extract.rs | 62 ++++ src/uu/tar/src/operations/mod.rs | 7 + src/uu/tar/src/tar.rs | 102 ++++-- src/uu/tar/tests/test_cli.rs | 34 ++ src/uu/tar/tests/test_errors.rs | 92 +++++ tests/by-util/test_tar.rs | 102 +++++- 14 files changed, 864 insertions(+), 242 deletions(-) create mode 100644 src/uu/tar/src/errors.rs create mode 100644 src/uu/tar/src/operations/create.rs create mode 100644 src/uu/tar/src/operations/extract.rs create mode 100644 src/uu/tar/src/operations/mod.rs create mode 100644 src/uu/tar/tests/test_cli.rs create mode 100644 src/uu/tar/tests/test_errors.rs diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index ff468d3..374783d 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -102,5 +102,5 @@ jobs: fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') # * convert any warnings to GHA UI annotations; ref: - S=$(cargo clippy --all-targets -ptar -- ${CLIPPY_FLAGS} -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; } + S=$(cargo clippy --all-targets -ptarapp -- ${CLIPPY_FLAGS} -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; } if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi diff --git a/.gitignore b/.gitignore index ea8c4bf..7528e5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,23 @@ -/target +# spell-checker:ignore (misc) direnv + +target/ +coverage/ +/src/*/gen_table +/build/ +/tmp/ +/busybox/ +/.vscode/ +/.vs/ +/public/ +*~ +.*.swp +.*.swo +.idea +lib*.a +/docs/_build +*.iml +### macOS ### +.DS_Store + +### direnv ### +/.direnv/ diff --git a/Cargo.lock b/Cargo.lock index 2c23655..86cd22d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -54,37 +54,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "once_cell", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] @@ -98,15 +98,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "block-buffer" @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "regex-automata", @@ -130,9 +130,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bzip2" @@ -145,10 +145,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.16" +version = "1.2.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -156,9 +157,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -174,7 +175,7 @@ checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "iana-time-zone", "num-traits", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -220,9 +221,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "clap_mangen" @@ -236,9 +237,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "constant_time_eq" @@ -313,15 +314,15 @@ checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" [[package]] name = "deflate64" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" +checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" [[package]] name = "deranged" -version = "0.4.1" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] @@ -367,9 +368,9 @@ dependencies = [ [[package]] name = "dns-lookup" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853d5bcf0b73bd5e6d945b976288621825c7166e9f06c5a035ae1aaf42d1b64f" +checksum = "6e39034cee21a2f5bbb66ba0e3689819c4bb5d00382a282006e802a7ffa6c41d" dependencies = [ "cfg-if", "libc", @@ -400,12 +401,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -415,10 +416,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "flate2" -version = "1.1.2" +name = "filetime" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.60.2", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "libz-rs-sys", @@ -453,9 +472,9 @@ dependencies = [ [[package]] name = "fluent-langneg" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +checksum = "7eebbe59450baee8282d71676f3bfed5689aeab00b27545e83e5f14b1195e8b0" dependencies = [ "unic-langid", ] @@ -472,9 +491,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -482,21 +501,21 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi", + "wasip2", ] [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "hashbrown" @@ -521,14 +540,15 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -544,9 +564,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", @@ -582,9 +602,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" @@ -645,9 +665,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", @@ -665,6 +685,17 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + [[package]] name = "libz-rs-sys" version = "0.5.2" @@ -676,15 +707,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "log" -version = "0.4.26" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lzma-rust2" @@ -698,9 +729,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "miniz_oxide" @@ -709,6 +740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -755,17 +787,23 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "os_display" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6229bad892b46b0dcfaaeb18ad0d2e56400f5aaea05b768bde96e73676cf75" +checksum = "ad5fd71b79026fb918650dde6d125000a233764f1c2f1659a1c71118e33ea08f" dependencies = [ - "unicode-width 0.1.14", + "unicode-width", ] [[package]] @@ -846,9 +884,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppmd-rust" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c834641d8ad1b348c9ee86dec3b9840d805acd5f24daa5f90c788951a52ff59b" +checksum = "d558c559f0450f16f2a27a1f017ef38468c1090c9ce63c8e51366232d53717b4" [[package]] name = "ppv-lite86" @@ -871,9 +909,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -904,18 +942,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -946,6 +984,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.12.2" @@ -971,9 +1018,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rlimit" @@ -998,43 +1045,53 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" -version = "1.0.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "self_cell" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" +checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1095,12 +1152,12 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1117,9 +1174,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -1128,6 +1185,15 @@ dependencies = [ [[package]] name = "tar" +version = "0.4.44" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tarapp" version = "0.0.1" dependencies = [ "chrono", @@ -1142,6 +1208,7 @@ dependencies = [ "rand", "regex", "rlimit", + "tar", "tempfile", "textwrap", "uu_tar", @@ -1162,17 +1229,17 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "terminal_size" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1184,23 +1251,23 @@ dependencies = [ "smawk", "terminal_size", "unicode-linebreak", - "unicode-width 0.2.0", + "unicode-width", ] [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -1209,9 +1276,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -1226,15 +1293,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -1242,11 +1309,12 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", + "serde_core", "zerovec", ] @@ -1261,9 +1329,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unic-langid" @@ -1285,9 +1353,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-linebreak" @@ -1297,15 +1365,9 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "utf8parse" @@ -1319,6 +1381,7 @@ version = "0.0.1" dependencies = [ "clap", "regex", + "tar", "uucore", ] @@ -1389,45 +1452,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1435,22 +1485,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -1466,18 +1516,38 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.52.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-link" -version = "0.1.3" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "windows-link" @@ -1485,6 +1555,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -1500,7 +1588,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -1521,19 +1618,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -1544,9 +1641,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -1556,9 +1653,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -1568,9 +1665,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -1580,9 +1677,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -1592,9 +1689,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -1604,9 +1701,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -1616,9 +1713,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -1628,18 +1725,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "xattr" @@ -1659,18 +1753,18 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" -version = "0.8.23" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.23" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", @@ -1685,9 +1779,9 @@ checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -1705,10 +1799,11 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ + "serde", "zerofrom", ] @@ -1747,9 +1842,9 @@ checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" [[package]] name = "zopfli" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" dependencies = [ "bumpalo", "crc32fast", diff --git a/Cargo.toml b/Cargo.toml index ca0a764..0d993e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ # spell-checker:ignore (libs) bigdecimal datetime fundu gethostid kqueue libselinux mangen memmap tar uuhelp [package] -name = "tar" +name = "tarapp" version = "0.0.1" authors = ["uutils developers"] license = "MIT" @@ -46,6 +46,7 @@ regex = "1.10.4" uucore = "0.4.0" uuhelp_parser = "0.2.0" uutests = "0.4.0" +tar = "0.4" tempfile = "3.10.1" textwrap = { version = "0.16.1", features = ["terminal_size"] } xattr = "1.3.1" @@ -71,6 +72,7 @@ libc = { workspace = true } pretty_assertions = "1" rand = { workspace = true } regex = { workspace = true } +tar-rs-crate = { version = "0.4", package = "tar" } tempfile = { workspace = true } uucore = { workspace = true, features = ["entries", "process", "signals"] } uutests = { workspace = true } diff --git a/src/bin/tarapp.rs b/src/bin/tarapp.rs index 2cb9fad..b8814cc 100644 --- a/src/bin/tarapp.rs +++ b/src/bin/tarapp.rs @@ -7,5 +7,12 @@ use std::env; use tar::uumain; fn main() { - std::process::exit(uumain(env::args_os())); + let exit_code = uumain(env::args_os()); + + // If exiting with code 2, show the "not recoverable" message + if exit_code == 2 { + uucore::show_error!("Error is not recoverable: exiting now"); + } + + std::process::exit(exit_code); } diff --git a/src/uu/tar/Cargo.toml b/src/uu/tar/Cargo.toml index fe25ea7..d6ed134 100644 --- a/src/uu/tar/Cargo.toml +++ b/src/uu/tar/Cargo.toml @@ -16,6 +16,7 @@ categories = ["command-line-utilities"] uucore = { workspace = true } clap = { workspace = true } regex = { workspace = true } +tar = { workspace = true } [lib] path = "src/tar.rs" diff --git a/src/uu/tar/src/errors.rs b/src/uu/tar/src/errors.rs new file mode 100644 index 0000000..5c51ff4 --- /dev/null +++ b/src/uu/tar/src/errors.rs @@ -0,0 +1,85 @@ +// This file is part of the uutils tar package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use std::fmt; +use std::io; +use uucore::error::UError; + +/// Error types for tar operations +#[derive(Debug)] +pub enum TarError { + /// I/O error occurred + IoError(io::Error), + /// Invalid archive format or corrupted archive + InvalidArchive(String), + /// File or directory not found + FileNotFound(String), + /// Permission denied + PermissionDenied(String), + /// General tar operation error + TarOperationError(String), +} + +/// Implements display formatting for TarError. +impl fmt::Display for TarError { + /// Formats the error for display to users + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TarError::IoError(err) => write!(f, "{err}"), + TarError::InvalidArchive(msg) => write!(f, "{msg}"), + TarError::FileNotFound(path) => { + write!(f, "{path}: Cannot open: No such file or directory") + } + TarError::PermissionDenied(path) => { + write!(f, "{path}: Cannot open: Permission denied") + } + TarError::TarOperationError(msg) => write!(f, "{msg}"), + } + } +} + +impl TarError { + /// Create a TarError from an io::Error with file path context + pub fn from_io_error(err: io::Error, path: &std::path::Path) -> Self { + match err.kind() { + io::ErrorKind::NotFound => TarError::FileNotFound(path.display().to_string()), + io::ErrorKind::PermissionDenied => { + TarError::PermissionDenied(path.display().to_string()) + } + _ => TarError::IoError(err), + } + } +} + +impl std::error::Error for TarError { + /// Returns the underlying error cause, if any + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + TarError::IoError(err) => Some(err), + _ => None, + } + } +} + +impl UError for TarError { + /// Returns the exit code for this error type + fn code(&self) -> i32 { + match self { + TarError::IoError(_) => 2, + TarError::InvalidArchive(_) => 2, + TarError::FileNotFound(_) => 2, + TarError::PermissionDenied(_) => 2, + TarError::TarOperationError(_) => 2, + } + } +} + +impl From for TarError { + /// Converts io::Error into the appropriate TarError variant + fn from(err: io::Error) -> Self { + // For generic io::Error without context, just wrap it + TarError::IoError(err) + } +} diff --git a/src/uu/tar/src/operations/create.rs b/src/uu/tar/src/operations/create.rs new file mode 100644 index 0000000..022cb62 --- /dev/null +++ b/src/uu/tar/src/operations/create.rs @@ -0,0 +1,81 @@ +// This file is part of the uutils tar package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::errors::TarError; +use std::fs::File; +use std::path::Path; +use tar::Builder; +use uucore::error::UResult; + +/// Create a tar archive from the specified files +/// +/// # Arguments +/// +/// * `archive_path` - Path where the tar archive should be created +/// * `files` - Slice of file paths to add to the archive +/// * `verbose` - Whether to print verbose output during creation +/// +/// # Errors +/// +/// Returns an error if: +/// - The archive file cannot be created +/// - Any input file cannot be read +/// - Files cannot be added due to I/O or permission errors +pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UResult<()> { + // Create the output file + let file = File::create(archive_path).map_err(|e| { + TarError::TarOperationError(format!( + "Cannot create archive '{}': {}", + archive_path.display(), + e + )) + })?; + + // Create Builder instance + let mut builder = Builder::new(file); + + if verbose { + println!("Creating archive: {}", archive_path.display()); + } + + // Add each file or directory to the archive + for &path in files { + if verbose { + println!("{}", path.display()); + } + + // Check if path exists + if !path.exists() { + return Err(TarError::FileNotFound(path.display().to_string()).into()); + } + + // If it's a directory, recursively add all contents + if path.is_dir() { + builder.append_dir_all(path, path).map_err(|e| { + TarError::TarOperationError(format!( + "Failed to add directory '{}': {}", + path.display(), + e + )) + })?; + } else { + // For files, add them directly + builder.append_path(path).map_err(|e| { + TarError::TarOperationError(format!( + "Failed to add file '{}': {}", + path.display(), + e + )) + })?; + } + } + + // Finish writing the archive + builder + .finish() + .map_err(|e| TarError::TarOperationError(format!("Failed to finalize archive: {e}")))?; + + Ok(()) +} diff --git a/src/uu/tar/src/operations/extract.rs b/src/uu/tar/src/operations/extract.rs new file mode 100644 index 0000000..2668009 --- /dev/null +++ b/src/uu/tar/src/operations/extract.rs @@ -0,0 +1,62 @@ +// This file is part of the uutils tar package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::errors::TarError; +use std::fs::File; +use std::path::Path; +use tar::Archive; +use uucore::error::UResult; + +/// Extract files from a tar archive +/// +/// # Arguments +/// +/// * `archive_path` - Path to the tar archive to extract +/// * `verbose` - Whether to print verbose output during extraction +/// +/// # Errors +/// +/// Returns an error if: +/// - The archive file cannot be opened +/// - The archive format is invalid +/// - Files cannot be extracted due to I/O or permission errors +pub fn extract_archive(archive_path: &Path, verbose: bool) -> UResult<()> { + // Open the archive file + let file = File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?; + + // Create Archive instance + let mut archive = Archive::new(file); + + // Extract to current directory + if verbose { + println!("Extracting archive: {}", archive_path.display()); + } + + // Iterate through entries for verbose output and error handling + for entry_result in archive + .entries() + .map_err(|e| TarError::InvalidArchive(format!("Failed to read archive entries: {e}")))? + { + let mut entry = entry_result + .map_err(|e| TarError::InvalidArchive(format!("Failed to read entry: {e}")))?; + + // Get the path before unpacking (clone it so we can use it after borrowing entry mutably) + let path = entry + .path() + .map_err(|e| TarError::InvalidArchive(format!("Failed to read entry path: {e}")))? + .to_path_buf(); + + if verbose { + println!("{}", path.display()); + } + + // Unpack the entry + entry.unpack_in(".").map_err(|e| { + TarError::TarOperationError(format!("Failed to extract '{}': {}", path.display(), e)) + })?; + } + + Ok(()) +} diff --git a/src/uu/tar/src/operations/mod.rs b/src/uu/tar/src/operations/mod.rs new file mode 100644 index 0000000..b11efb7 --- /dev/null +++ b/src/uu/tar/src/operations/mod.rs @@ -0,0 +1,7 @@ +// This file is part of the uutils tar package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +pub mod create; +pub mod extract; diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 90c3224..4db1bd5 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -3,29 +3,73 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use clap::{arg, crate_version, Arg, ArgAction, Command}; -use std::path::PathBuf; +pub mod errors; +mod operations; + +use clap::{arg, crate_version, ArgAction, Command}; +use std::path::{Path, PathBuf}; use uucore::error::UResult; use uucore::format_usage; const ABOUT: &str = "an archiving utility"; -const USAGE: &str = "tar {A|c|d|r|t|u|x}[GnSkUWOmpsMBiajJzZhPlRvwo] [ARG...]"; +const USAGE: &str = "tar {c|x}[v] -f ARCHIVE [FILE...]"; #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let matches = uu_app().try_get_matches_from(args)?; + // Collect args - the test framework may add util_name as args[1], so skip it if present + let args_vec: Vec<_> = args.collect(); + let util_name = uucore::util_name(); - // For now, just print a basic message indicating the command was parsed - println!("tar: basic implementation - command line parsed successfully"); + // Skip duplicate util name if present (can be "tar" or "tarapp") + let args_to_parse = if args_vec.len() > 1 + && (args_vec[1] == util_name || args_vec[1] == "tar" || args_vec[1] == "tarapp") + { + let mut result = vec![args_vec[0].clone()]; + result.extend_from_slice(&args_vec[2..]); + result + } else { + args_vec + }; - // Check if any files were specified - if let Some(files) = matches.get_many::("file") { - for file in files { - println!("File: {}", file.display()); - } + let matches = uu_app().try_get_matches_from(args_to_parse)?; + + let verbose = matches.get_flag("verbose"); + + // Handle extract operation + if matches.get_flag("extract") { + let archive_path = matches.get_one::("file").ok_or_else(|| { + uucore::error::USimpleError::new(1, "option requires an argument -- 'f'") + })?; + + return operations::extract::extract_archive(archive_path, verbose); } - Ok(()) + // Handle create operation + if matches.get_flag("create") { + let archive_path = matches.get_one::("file").ok_or_else(|| { + uucore::error::USimpleError::new(1, "option requires an argument -- 'f'") + })?; + + let files: Vec<&Path> = matches + .get_many::("files") + .map(|v| v.map(|p| p.as_path()).collect()) + .unwrap_or_default(); + + if files.is_empty() { + return Err(uucore::error::USimpleError::new( + 1, + "Cowardly refusing to create an empty archive", + )); + } + + return operations::create::create_archive(archive_path, &files, verbose); + } + + // If no operation specified, show error + Err(uucore::error::USimpleError::new( + 1, + "You must specify one of the '-c' or '-x' options", + )) } #[allow(clippy::cognitive_complexity)] @@ -38,33 +82,29 @@ pub fn uu_app() -> Command { .disable_help_flag(true) .args([ // Main operation modes - arg!(-A --catenate "Append tar files to archive"), arg!(-c --create "Create a new archive"), - arg!(-d --diff "Find differences between archive and file system").alias("compare"), - arg!(-r --append "Append files to end of archive"), - arg!(-t --list "List contents of archive"), - arg!(-u --update "Only append files newer than copy in archive"), + // arg!(-d --diff "Find differences between archive and file system").alias("compare"), + // arg!(-r --append "Append files to end of archive"), + // arg!(-t --list "List contents of archive"), + // arg!(-u --update "Only append files newer than copy in archive"), arg!(-x --extract "Extract files from archive").alias("get"), // Archive file - arg!(-f --file "Use archive file").value_parser(clap::value_parser!(PathBuf)), + arg!(-f --file "Use archive file or device ARCHIVE") + .value_parser(clap::value_parser!(PathBuf)), // Compression options - arg!(-z --gzip "Filter through gzip"), - arg!(-j --bzip2 "Filter through bzip2"), - arg!(-J --xz "Filter through xz"), + // arg!(-z --gzip "Filter through gzip"), + // arg!(-j --bzip2 "Filter through bzip2"), + // arg!(-J --xz "Filter through xz"), // Common options arg!(-v --verbose "Verbosely list files processed"), - arg!(-h --dereference "Follow symlinks"), - arg!(-p --"preserve-permissions" "Extract information about file permissions"), - arg!(-P --"absolute-names" "Don't strip leading '/' from file names"), + // arg!(-h --dereference "Follow symlinks"), + // arg!(-p --"preserve-permissions" "Extract information about file permissions"), + // arg!(-P --"absolute-names" "Don't strip leading '/' from file names"), // Help arg!(--help "Print help information").action(ArgAction::Help), // Files to process - Arg::new("files") - .help("Files to archive or extract") - .value_parser(clap::value_parser!(PathBuf)) - .num_args(0..), + arg!([files]... "Files to archive or extract") + .action(ArgAction::Append) + .value_parser(clap::value_parser!(PathBuf)), ]) } - -#[cfg(test)] -mod tests {} diff --git a/src/uu/tar/tests/test_cli.rs b/src/uu/tar/tests/test_cli.rs new file mode 100644 index 0000000..9b42cd7 --- /dev/null +++ b/src/uu/tar/tests/test_cli.rs @@ -0,0 +1,34 @@ +// This file is part of the uutils tar package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use uu_tar::uu_app; + +#[test] +fn test_extract_flag_parsing() { + let app = uu_app(); + let result = app.try_get_matches_from(vec!["tar", "-xf", "archive.tar"]); + assert!(result.is_ok()); + let matches = result.unwrap(); + assert!(matches.get_flag("extract")); +} + +#[test] +fn test_create_flag_parsing() { + let app = uu_app(); + let result = app.try_get_matches_from(vec!["tar", "-cf", "archive.tar", "file.txt"]); + assert!(result.is_ok()); + let matches = result.unwrap(); + assert!(matches.get_flag("create")); +} + +#[test] +fn test_verbose_flag_parsing() { + let app = uu_app(); + let result = app.try_get_matches_from(vec!["tar", "-cvf", "archive.tar", "file.txt"]); + assert!(result.is_ok()); + let matches = result.unwrap(); + assert!(matches.get_flag("verbose")); + assert!(matches.get_flag("create")); +} diff --git a/src/uu/tar/tests/test_errors.rs b/src/uu/tar/tests/test_errors.rs new file mode 100644 index 0000000..405d7df --- /dev/null +++ b/src/uu/tar/tests/test_errors.rs @@ -0,0 +1,92 @@ +// This file is part of the uutils tar package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use std::io; +use uu_tar::errors::TarError; + +#[test] +fn test_tar_error_display() { + let err = TarError::FileNotFound("test.txt".to_string()); + assert_eq!( + err.to_string(), + "test.txt: Cannot open: No such file or directory" + ); + + let err = TarError::InvalidArchive("corrupted header".to_string()); + assert_eq!(err.to_string(), "corrupted header"); + + let err = TarError::PermissionDenied("/root/file".to_string()); + assert_eq!( + err.to_string(), + "/root/file: Cannot open: Permission denied" + ); + + let err = TarError::TarOperationError("failed to write".to_string()); + assert_eq!(err.to_string(), "failed to write"); +} + +#[test] +fn test_tar_error_code() { + use uucore::error::UError; + + assert_eq!(TarError::FileNotFound("test".to_string()).code(), 2); + assert_eq!(TarError::InvalidArchive("test".to_string()).code(), 2); + assert_eq!(TarError::PermissionDenied("test".to_string()).code(), 2); + assert_eq!(TarError::TarOperationError("test".to_string()).code(), 2); +} + +#[test] +fn test_io_error_conversion_not_found() { + let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found"); + let tar_err = TarError::from(io_err); + + match tar_err { + TarError::IoError(e) => assert_eq!(e.kind(), io::ErrorKind::NotFound), + _ => panic!("Expected IoError variant"), + } +} + +#[test] +fn test_io_error_conversion_permission_denied() { + let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "access denied"); + let tar_err = TarError::from(io_err); + + match tar_err { + TarError::IoError(e) => assert_eq!(e.kind(), io::ErrorKind::PermissionDenied), + _ => panic!("Expected IoError variant"), + } +} + +#[test] +fn test_io_error_conversion_other() { + let io_err = io::Error::new(io::ErrorKind::BrokenPipe, "pipe broken"); + let tar_err = TarError::from(io_err); + + match tar_err { + TarError::IoError(e) => assert_eq!(e.kind(), io::ErrorKind::BrokenPipe), + _ => panic!("Expected IoError variant"), + } +} + +#[test] +fn test_error_source() { + let io_err = io::Error::other("some error"); + let tar_err = TarError::IoError(io_err); + + // IoError should have a source + assert!(std::error::Error::source(&tar_err).is_some()); + + // Other variants should not have a source + let tar_err = TarError::FileNotFound("test".to_string()); + assert!(std::error::Error::source(&tar_err).is_none()); +} + +#[test] +fn test_tar_error_is_debug() { + let err = TarError::TarOperationError("test".to_string()); + let debug_str = format!("{err:?}"); + assert!(debug_str.contains("TarOperationError")); + assert!(debug_str.contains("test")); +} diff --git a/tests/by-util/test_tar.rs b/tests/by-util/test_tar.rs index 8760d48..741f2b4 100644 --- a/tests/by-util/test_tar.rs +++ b/tests/by-util/test_tar.rs @@ -3,9 +3,10 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use uutests::new_ucmd; +use uutests::{at_and_ucmd, new_ucmd}; + +// Basic CLI Tests -// Basic tar tests #[test] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); @@ -13,10 +14,103 @@ fn test_invalid_arg() { #[test] fn test_help() { - new_ucmd!().arg("--help").succeeds().code_is(0); + new_ucmd!() + .arg("--help") + .succeeds() + .code_is(0) + .stdout_contains("an archiving utility"); } #[test] fn test_version() { - new_ucmd!().arg("--version").succeeds().code_is(0); + new_ucmd!() + .arg("--version") + .succeeds() + .code_is(0) + .stdout_contains("tar"); +} + +// Create operation tests + +#[test] +fn test_create_single_file() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.write("file1.txt", "test content"); + + ucmd.args(&["-cf", "archive.tar", "file1.txt"]).succeeds(); + + assert!(at.file_exists("archive.tar")); +} + +#[test] +fn test_create_multiple_files() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.write("file1.txt", "content1"); + at.write("file2.txt", "content2"); + + ucmd.args(&["-cf", "archive.tar", "file1.txt", "file2.txt"]) + .succeeds(); + + assert!(at.file_exists("archive.tar")); +} + +// Extract operation tests + +#[test] +fn test_extract_single_file() { + let (at, mut ucmd) = at_and_ucmd!(); + + // Create an archive first + at.write("original.txt", "test content"); + ucmd.args(&["-cf", "archive.tar", "original.txt"]) + .succeeds(); + + // Remove original and extract + at.remove("original.txt"); + + new_ucmd!() + .arg("-xf") + .arg(at.plus("archive.tar")) + .current_dir(at.as_string()) + .succeeds(); + + assert!(at.file_exists("original.txt")); + assert_eq!(at.read("original.txt"), "test content"); +} + +#[test] +fn test_extract_multiple_files() { + let (at, mut ucmd) = at_and_ucmd!(); + + // Create an archive with multiple files + at.write("file1.txt", "content1"); + at.write("file2.txt", "content2"); + ucmd.args(&["-cf", "archive.tar", "file1.txt", "file2.txt"]) + .succeeds(); + + // Remove originals + at.remove("file1.txt"); + at.remove("file2.txt"); + + // Extract + new_ucmd!() + .arg("-xf") + .arg(at.plus("archive.tar")) + .current_dir(at.as_string()) + .succeeds(); + + assert!(at.file_exists("file1.txt")); + assert!(at.file_exists("file2.txt")); + assert_eq!(at.read("file1.txt"), "content1"); + assert_eq!(at.read("file2.txt"), "content2"); +} + +#[test] +fn test_extract_nonexistent_archive() { + new_ucmd!() + .args(&["-xf", "nonexistent.tar"]) + .fails() + .code_is(2); }