mirror of
https://github.com/uutils/tar.git
synced 2026-06-10 16:14:35 -07:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
815a1b3036 | ||
|
|
25f4bd7c61 | ||
|
|
26bc94342d | ||
|
|
cef2373196 | ||
|
|
8d4fef7aba | ||
|
|
3be98e9e5c | ||
|
|
3eb916a8c2 | ||
|
|
9161cb6e2d | ||
|
|
ea51e0cd4c | ||
|
|
31c9e09527 | ||
|
|
14185c5626 |
@@ -104,7 +104,7 @@ jobs:
|
||||
grcov . --binary-path="${COVERAGE_REPORT_DIR}" --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" --source-dir .
|
||||
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
||||
- name: Upload coverage results (to Codecov.io)
|
||||
uses: codecov/codecov-action@v6
|
||||
uses: codecov/codecov-action@v7
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ${{ steps.coverage.outputs.report }}
|
||||
|
||||
Generated
+27
-65
@@ -185,9 +185,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
@@ -409,33 +409,16 @@ dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1c888a2a4f677017373fb6c01e13e318dd9e78758445ed5eb985e355d3f8281"
|
||||
dependencies = [
|
||||
"ctor-proc-macro",
|
||||
"dtor",
|
||||
"link-section 0.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a"
|
||||
dependencies = [
|
||||
"link-section 0.18.0",
|
||||
"link-section",
|
||||
"linktime-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor-proc-macro"
|
||||
version = "0.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7ab264ea985f1bd27887d7b21ea2bb046728e05d11909ca138d700c494730db"
|
||||
|
||||
[[package]]
|
||||
name = "ctutils"
|
||||
version = "0.4.2"
|
||||
@@ -523,21 +506,6 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtor"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30e4690622ab6700ced40fc370a3f07b7d111f0154bb6fb08f73b4c8834f75b6"
|
||||
dependencies = [
|
||||
"dtor-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtor-proc-macro"
|
||||
version = "0.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c98b077c7463d01d22dde8a24378ddf1ca7263dc687cffbed38819ea6c21131"
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.5"
|
||||
@@ -563,7 +531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -847,7 +815,7 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde_core",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -916,9 +884,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -932,12 +900,6 @@ dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-section"
|
||||
version = "0.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52437d47b0358721ec869cc7374b2a21f7b2237af9b439c0391341a1fbfbf1b"
|
||||
|
||||
[[package]]
|
||||
name = "link-section"
|
||||
version = "0.18.0"
|
||||
@@ -989,9 +951,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.31.2"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
|
||||
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -1219,9 +1181,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
version = "1.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1248,9 +1210,9 @@ checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.10"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "rlimit"
|
||||
@@ -1283,7 +1245,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1456,7 +1418,7 @@ dependencies = [
|
||||
"clap_complete",
|
||||
"clap_mangen",
|
||||
"codspeed-divan-compat",
|
||||
"ctor 1.0.7",
|
||||
"ctor",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"pretty_assertions",
|
||||
@@ -1481,10 +1443,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.4.2",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1494,7 +1456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1681,20 +1643,20 @@ dependencies = [
|
||||
"clap",
|
||||
"regex",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uucore"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/uutils/coreutils#b13588cc944be76a123c841f94889b10dfbf7932"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/uutils/coreutils#48d030dfb2ca7659ee2735f09cda706c8245a3fc"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dns-lookup",
|
||||
"dunce",
|
||||
"fluent",
|
||||
"fluent-bundle",
|
||||
"fluent-syntax",
|
||||
"jiff",
|
||||
"libc",
|
||||
@@ -1713,8 +1675,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uucore_procs"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/uutils/coreutils#b13588cc944be76a123c841f94889b10dfbf7932"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/uutils/coreutils#48d030dfb2ca7659ee2735f09cda706c8245a3fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1722,10 +1684,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uutests"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/uutils/coreutils#b13588cc944be76a123c841f94889b10dfbf7932"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/uutils/coreutils#48d030dfb2ca7659ee2735f09cda706c8245a3fc"
|
||||
dependencies = [
|
||||
"ctor 0.9.1",
|
||||
"ctor",
|
||||
"libc",
|
||||
"nix",
|
||||
"pretty_assertions",
|
||||
@@ -1861,7 +1823,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+20
-8
@@ -4,7 +4,7 @@
|
||||
// at various scales to track performance over time.
|
||||
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tar::operations;
|
||||
use tempfile::TempDir;
|
||||
@@ -39,7 +39,9 @@ fn collect_files(dir: &Path) -> Vec<PathBuf> {
|
||||
fn build_archive(archive_path: &Path, source_dir: &Path) {
|
||||
let files = collect_files(source_dir);
|
||||
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
||||
operations::create::create_archive(archive_path, &refs, false, false).unwrap();
|
||||
let output = File::create(archive_path).unwrap();
|
||||
let status_output = io::sink();
|
||||
operations::create::create_archive(output, status_output, &refs, true, false).unwrap();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -57,7 +59,9 @@ fn create_archive_10_files(bencher: divan::Bencher) {
|
||||
|
||||
bencher.bench_local(|| {
|
||||
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
||||
operations::create::create_archive(&archive_path, &refs, false, false).unwrap();
|
||||
let output = File::create(&archive_path).unwrap();
|
||||
let status_output = io::sink();
|
||||
operations::create::create_archive(output, status_output, &refs, true, false).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,7 +76,9 @@ fn create_archive_100_files(bencher: divan::Bencher) {
|
||||
|
||||
bencher.bench_local(|| {
|
||||
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
||||
operations::create::create_archive(&archive_path, &refs, false, false).unwrap();
|
||||
let output = File::create(&archive_path).unwrap();
|
||||
let status_output = io::sink();
|
||||
operations::create::create_archive(output, status_output, &refs, true, false).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -87,7 +93,10 @@ fn create_archive_directory(bencher: divan::Bencher) {
|
||||
let archive_path = out.path().join("bench.tar");
|
||||
|
||||
bencher.bench_local(|| {
|
||||
operations::create::create_archive(&archive_path, &[sub.as_path()], false, false).unwrap();
|
||||
let output = File::create(&archive_path).unwrap();
|
||||
let status_output = io::sink();
|
||||
operations::create::create_archive(output, status_output, &[sub.as_path()], true, false)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,7 +113,8 @@ fn list_archive_50_files(bencher: divan::Bencher) {
|
||||
build_archive(&archive_path, source.path());
|
||||
|
||||
bencher.bench_local(|| {
|
||||
operations::list::list_archive(&archive_path, false).unwrap();
|
||||
let input = File::open(&archive_path).unwrap();
|
||||
operations::list::list_archive(input, false).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -117,7 +127,8 @@ fn list_archive_verbose_50_files(bencher: divan::Bencher) {
|
||||
build_archive(&archive_path, source.path());
|
||||
|
||||
bencher.bench_local(|| {
|
||||
operations::list::list_archive(&archive_path, true).unwrap();
|
||||
let input = File::open(&archive_path).unwrap();
|
||||
operations::list::list_archive(input, true).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -138,7 +149,8 @@ fn extract_archive_20_files(bencher: divan::Bencher) {
|
||||
.with_inputs(|| TempDir::new().unwrap())
|
||||
.bench_local_values(|extract_dir| {
|
||||
std::env::set_current_dir(extract_dir.path()).unwrap();
|
||||
operations::extract::extract_archive(&archive_path, false).unwrap();
|
||||
let input = File::open(&archive_path).unwrap();
|
||||
operations::extract::extract_archive(input, &archive_path, false).unwrap();
|
||||
});
|
||||
std::env::set_current_dir(original_dir).unwrap();
|
||||
}
|
||||
|
||||
Generated
+113
-157
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -14,8 +14,8 @@ libc = "0.2.153"
|
||||
tempfile = "3.15.0"
|
||||
rand = { version = "0.10.0" }
|
||||
similar = "3.0.0"
|
||||
uucore = { version = "0.8.0", features = ["libc"] }
|
||||
uufuzz = "0.8.0"
|
||||
uucore = { version = "0.9.0", features = ["libc"] }
|
||||
uufuzz = "0.9.0"
|
||||
|
||||
uu_tar = { path = "../src/uu/tar/" }
|
||||
|
||||
|
||||
@@ -26,3 +26,6 @@ path = "src/tar.rs"
|
||||
[[bin]]
|
||||
name = "tar"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = { workspace = true }
|
||||
|
||||
@@ -54,6 +54,10 @@ pub enum TarError {
|
||||
/// Cannot finalize the archive
|
||||
#[error("tar: Cannot finalize archive: {0}")]
|
||||
CannotFinalizeArchive(io::Error),
|
||||
|
||||
/// Refusing to write archive contents to terminal
|
||||
#[error("tar: Refusing to write archive contents to terminal (missing -f option?)")]
|
||||
RefuseWriteArchiveToTerminal,
|
||||
}
|
||||
|
||||
impl TarError {
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
use crate::errors::TarError;
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, BufWriter, Write};
|
||||
use std::fs;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::Component::{self, ParentDir, Prefix, RootDir};
|
||||
use std::path::{self, Path, PathBuf};
|
||||
use tar::Builder;
|
||||
@@ -28,23 +28,19 @@ use uucore::error::UResult;
|
||||
/// - Any input file cannot be read
|
||||
/// - Files cannot be added due to I/O or permission errors
|
||||
pub fn create_archive(
|
||||
archive_path: &Path,
|
||||
output: impl Write,
|
||||
status_output: impl Write,
|
||||
files: &[&Path],
|
||||
allow_absolute: bool,
|
||||
verbose: bool,
|
||||
) -> UResult<()> {
|
||||
// Create the output file
|
||||
let file = File::create(archive_path).map_err(|e| TarError::CannotCreateArchive {
|
||||
path: archive_path.to_path_buf(),
|
||||
source: e,
|
||||
})?;
|
||||
let mut output = BufWriter::new(output);
|
||||
let mut status_output = BufWriter::new(status_output);
|
||||
|
||||
// Create Builder instance
|
||||
let mut builder = Builder::new(file);
|
||||
let mut builder = Builder::new(&mut output);
|
||||
builder.preserve_absolute(allow_absolute);
|
||||
|
||||
let mut out = BufWriter::new(io::stdout().lock());
|
||||
|
||||
// Add each file or directory to the archive
|
||||
for &path in files {
|
||||
// Check if path exists
|
||||
@@ -68,7 +64,7 @@ pub fn create_archive(
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
writeln!(out, "{to_print}").map_err(TarError::Io)?;
|
||||
writeln!(status_output, "{to_print}").map_err(TarError::Io)?;
|
||||
}
|
||||
|
||||
// Normalize path if needed (so far, handles only absolute paths)
|
||||
@@ -81,8 +77,8 @@ pub fn create_archive(
|
||||
.iter()
|
||||
.collect();
|
||||
writeln!(
|
||||
out,
|
||||
"Removing leading `{}' from member names",
|
||||
std::io::stderr(),
|
||||
"tar: Removing leading `{}' from member names",
|
||||
removed.display()
|
||||
)
|
||||
.map_err(TarError::Io)?;
|
||||
@@ -112,9 +108,11 @@ pub fn create_archive(
|
||||
}
|
||||
}
|
||||
|
||||
// Finish writing the archive
|
||||
out.flush().map_err(TarError::Io)?;
|
||||
builder.finish().map_err(TarError::CannotFinalizeArchive)?;
|
||||
drop(builder);
|
||||
|
||||
status_output.flush().map_err(TarError::Io)?;
|
||||
output.flush().map_err(TarError::Io)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -148,3 +146,32 @@ fn normalize_path(path: &Path, allow_absolute: bool) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::{self, Write};
|
||||
use tempfile::TempDir;
|
||||
|
||||
struct FailFlushWriter;
|
||||
impl Write for FailFlushWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Err(io::Error::other("flush failed"))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_archive_flush_failed() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let file_path = dir.path().join("test.txt");
|
||||
fs::write(&file_path, "hello").unwrap();
|
||||
|
||||
let output = FailFlushWriter;
|
||||
let status_output = io::sink();
|
||||
|
||||
let res = create_archive(output, status_output, &[file_path.as_path()], false, false);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::errors::TarError;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufWriter, Write};
|
||||
use std::io::{self, BufReader, BufWriter, Read, Write};
|
||||
use std::path::Path;
|
||||
use tar::Archive;
|
||||
use uucore::error::UResult;
|
||||
@@ -23,12 +22,9 @@ use uucore::error::UResult;
|
||||
/// - 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))?;
|
||||
|
||||
pub fn extract_archive(input: impl Read, archive_path: &Path, verbose: bool) -> UResult<()> {
|
||||
// Create Archive instance
|
||||
let mut archive = Archive::new(file);
|
||||
let mut archive = Archive::new(BufReader::new(input));
|
||||
let mut out = BufWriter::new(io::stdout().lock());
|
||||
|
||||
// Extract to current directory
|
||||
|
||||
@@ -5,18 +5,14 @@
|
||||
|
||||
use crate::errors::TarError;
|
||||
use chrono::{TimeZone, Utc};
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufWriter, Write};
|
||||
use std::path::Path;
|
||||
use std::io::{self, BufReader, BufWriter, Read, Write};
|
||||
use tar::Archive;
|
||||
use uucore::error::UResult;
|
||||
use uucore::fs::display_permissions_unix;
|
||||
|
||||
/// List the contents of a tar archive, printing one entry per line.
|
||||
pub fn list_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
|
||||
let file: File =
|
||||
File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
|
||||
let mut archive = Archive::new(file);
|
||||
pub fn list_archive(input: impl Read, verbose: bool) -> UResult<()> {
|
||||
let mut archive = Archive::new(BufReader::new(input));
|
||||
let mut out = BufWriter::new(io::stdout().lock());
|
||||
|
||||
for entry_result in archive.entries().map_err(TarError::CannotReadEntries)? {
|
||||
|
||||
+46
-3
@@ -6,7 +6,10 @@
|
||||
pub mod errors;
|
||||
pub mod operations;
|
||||
|
||||
use crate::errors::TarError;
|
||||
use clap::{arg, crate_version, ArgAction, Command};
|
||||
use std::fs::File;
|
||||
use std::io::{self, IsTerminal};
|
||||
use std::path::{Path, PathBuf};
|
||||
use uucore::error::UResult;
|
||||
use uucore::format_usage;
|
||||
@@ -139,7 +142,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
uucore::error::USimpleError::new(64, "option requires an argument -- 'f'")
|
||||
})?;
|
||||
|
||||
return operations::extract::extract_archive(archive_path, verbose);
|
||||
return if archive_path == Path::new("-") {
|
||||
operations::extract::extract_archive(io::stdin(), archive_path, verbose)
|
||||
} else {
|
||||
let file =
|
||||
File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
|
||||
operations::extract::extract_archive(file, archive_path, verbose)
|
||||
};
|
||||
}
|
||||
|
||||
// Handle create operation
|
||||
@@ -160,7 +169,35 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
));
|
||||
}
|
||||
|
||||
return operations::create::create_archive(archive_path, &files, allow_absolute, verbose);
|
||||
let output_is_stdout = archive_path == Path::new("-");
|
||||
return if output_is_stdout {
|
||||
if io::stdout().is_terminal() {
|
||||
Err(TarError::RefuseWriteArchiveToTerminal.into())
|
||||
} else {
|
||||
let output = io::stdout().lock();
|
||||
let status_output = io::stderr();
|
||||
operations::create::create_archive(
|
||||
output,
|
||||
status_output,
|
||||
&files,
|
||||
allow_absolute,
|
||||
verbose,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let output = File::create(archive_path).map_err(|e| TarError::CannotCreateArchive {
|
||||
path: archive_path.clone(),
|
||||
source: e,
|
||||
})?;
|
||||
let status_output = io::stdout().lock();
|
||||
operations::create::create_archive(
|
||||
output,
|
||||
status_output,
|
||||
&files,
|
||||
allow_absolute,
|
||||
verbose,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// Handle list operation
|
||||
@@ -169,7 +206,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
uucore::error::USimpleError::new(64, "option requires an argument -- 'f'")
|
||||
})?;
|
||||
|
||||
return operations::list::list_archive(archive_path, verbose);
|
||||
return if archive_path == Path::new("-") {
|
||||
operations::list::list_archive(io::stdin(), verbose)
|
||||
} else {
|
||||
let file =
|
||||
File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
|
||||
operations::list::list_archive(file, verbose)
|
||||
};
|
||||
}
|
||||
|
||||
// If no operation specified, show error
|
||||
|
||||
+124
-1
@@ -145,6 +145,17 @@ fn test_create_verbose() {
|
||||
assert!(at.file_exists("archive.tar"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_verbose_to_stdout_uses_stderr() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
at.write("file.txt", "hello");
|
||||
|
||||
ucmd.args(&["-cvf", "-", "file.txt"])
|
||||
.succeeds()
|
||||
.stderr_contains("file.txt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_empty_archive_fails() {
|
||||
new_ucmd!()
|
||||
@@ -179,7 +190,7 @@ fn test_create_absolute_path() {
|
||||
&file_abs_path.display().to_string(),
|
||||
])
|
||||
.succeeds()
|
||||
.stdout_contains("Removing leading");
|
||||
.stderr_contains("tar: Removing leading");
|
||||
|
||||
assert!(at.file_exists("archive-trimed.tar"));
|
||||
|
||||
@@ -814,3 +825,115 @@ fn test_list_conflicts_with_extract() {
|
||||
.code_is(2)
|
||||
.stderr_contains("cannot be used with");
|
||||
}
|
||||
|
||||
// stdin/stdout (-f -) tests
|
||||
|
||||
#[test]
|
||||
fn test_create_to_stdout() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write("file.txt", "hello");
|
||||
|
||||
let result = ucmd.args(&["-cf", "-", "file.txt"]).succeeds();
|
||||
let bytes = result.stdout();
|
||||
// A tar archive is at least two blocks: one header + two end-of-archive blocks
|
||||
assert!(
|
||||
bytes.len() >= TAR_BLOCK_SIZE,
|
||||
"stdout should contain tar data (got {} bytes)",
|
||||
bytes.len()
|
||||
);
|
||||
// The first 100 bytes of a tar header are the file name
|
||||
let header_prefix = std::str::from_utf8(&bytes[..100]).unwrap_or("");
|
||||
assert!(
|
||||
header_prefix.contains("file.txt"),
|
||||
"tar header should contain filename"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_verbose_to_stdout_keeps_stdout_as_tar_data() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write("file.txt", "hello");
|
||||
|
||||
let result = ucmd.args(&["-cvf", "-", "file.txt"]).succeeds();
|
||||
let bytes = result.stdout();
|
||||
let header_prefix = std::str::from_utf8(&bytes[..100]).unwrap_or("");
|
||||
|
||||
assert!(
|
||||
bytes.len() >= TAR_BLOCK_SIZE,
|
||||
"stdout should contain tar data (got {} bytes)",
|
||||
bytes.len()
|
||||
);
|
||||
assert!(
|
||||
header_prefix.contains("file.txt"),
|
||||
"tar header should contain filename"
|
||||
);
|
||||
assert_eq!(&bytes[..8], b"file.txt");
|
||||
assert_eq!(
|
||||
bytes[8], 0,
|
||||
"stdout should not contain verbose output prefix"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_absolute_path_to_stdout_uses_stderr_for_normalization_notice() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let mut file_abs_path = PathBuf::from(at.root_dir_resolved());
|
||||
file_abs_path.push("file1.txt");
|
||||
|
||||
at.write(&file_abs_path.display().to_string(), "content1");
|
||||
|
||||
let result = ucmd
|
||||
.args(&["-cf", "-", &file_abs_path.display().to_string()])
|
||||
.succeeds();
|
||||
result.stderr_contains("Removing leading");
|
||||
let bytes = result.stdout();
|
||||
let header_prefix = std::str::from_utf8(&bytes[..100]).unwrap_or("");
|
||||
|
||||
assert!(
|
||||
bytes.len() >= TAR_BLOCK_SIZE,
|
||||
"stdout should contain tar data (got {} bytes)",
|
||||
bytes.len()
|
||||
);
|
||||
assert!(
|
||||
!header_prefix.contains("Removing leading"),
|
||||
"stdout should not contain normalization notices"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_from_stdin() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write("file.txt", "hello");
|
||||
|
||||
// Create archive to a file in the same sandbox
|
||||
new_ucmd!()
|
||||
.args(&["-cf", &at.plus_as_string("archive.tar"), "file.txt"])
|
||||
.current_dir(at.as_string())
|
||||
.succeeds();
|
||||
|
||||
let archive_bytes = at.read_bytes("archive.tar");
|
||||
ucmd.args(&["-tf", "-"])
|
||||
.pipe_in(archive_bytes)
|
||||
.succeeds()
|
||||
.stdout_contains("file.txt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_from_stdin() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write("file.txt", "hello world");
|
||||
|
||||
// Create archive to a file in the same sandbox
|
||||
new_ucmd!()
|
||||
.args(&["-cf", &at.plus_as_string("archive.tar"), "file.txt"])
|
||||
.current_dir(at.as_string())
|
||||
.succeeds();
|
||||
|
||||
at.remove("file.txt");
|
||||
|
||||
let archive_bytes = at.read_bytes("archive.tar");
|
||||
ucmd.args(&["-xf", "-"]).pipe_in(archive_bytes).succeeds();
|
||||
|
||||
assert_eq!(at.read("file.txt"), "hello world");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user