diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml
index fac09b36a..7f667076a 100644
--- a/.github/workflows/CICD.yml
+++ b/.github/workflows/CICD.yml
@@ -4,7 +4,7 @@ name: CICD
# spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic Dwarnings RUSTDOCFLAGS RUSTFLAGS Zpanic CARGOFLAGS
# spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain fuzzers dedupe devel profdata
# spell-checker:ignore (people) Peltoche rivy dtolnay Anson dawidd
-# spell-checker:ignore (shell/tools) binutils choco clippy dmake esac fakeroot fdesc fdescfs gmake grcov halium lcov libclang libfuse libssl limactl mkdir nextest nocross pacman popd printf pushd redoxer rsync rustc rustfmt rustup shopt sccache utmpdump xargs
+# spell-checker:ignore (shell/tools) binutils choco clippy dmake esac fakeroot fdesc fdescfs gmake grcov halium lcov libclang libfuse libssl limactl mkdir nextest nocross pacman popd printf pushd redoxer rsync rustc rustfmt rustup shopt sccache utmpdump xargs zstd
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils defconfig DESTDIR gecos getenforce gnueabihf issuecomment maint manpages msys multisize noconfirm nofeatures nullglob onexitbegin onexitend pell runtest Swatinem tempfile testsuite toybox uutils libsystemd codspeed
env:
@@ -297,7 +297,6 @@ jobs:
mv -T target target.cache
fi
# Check that we don't cross-build uudoc
- # also do not try to generate manpages for part of hashsum
env CARGO_BUILD_TARGET=aarch64-unknown-linux-gnu make install-manpages PREFIX=/tmp/usr UTILS=true
# build (host)
make build
@@ -371,25 +370,19 @@ jobs:
run: |
set -x
DESTDIR=/tmp/ make PROFILE=release MULTICALL=n install
- # Check that the utils are present
- test -f /tmp/usr/local/bin/hashsum
- # Check that hashsum symlinks are present
- test -h /tmp/usr/local/bin/b2sum
- test -h /tmp/usr/local/bin/md5sum
- test -h /tmp/usr/local/bin/sha1sum
- test -h /tmp/usr/local/bin/sha224sum
- test -h /tmp/usr/local/bin/sha256sum
- test -h /tmp/usr/local/bin/sha384sum
- test -h /tmp/usr/local/bin/sha512sum
+ # Check that *sum are present
+ for s in {md5,b2,sha1,sha224,sha256,sha384,sha512}sum
+ do test -e /tmp/usr/local/bin/${s}
+ done
- name: "`make install MULTICALL=y LN=ln -svf`"
shell: bash
run: |
set -x
DESTDIR=/tmp/ make PROFILE=release MULTICALL=y LN="ln -svf" install
- # Check that relative symlinks of hashsum are present
- [ $(readlink /tmp/usr/local/bin/b2sum) = coreutils ]
- [ $(readlink /tmp/usr/local/bin/md5sum) = coreutils ]
- [ $(readlink /tmp/usr/local/bin/sha512sum) = coreutils ]
+ # Check that symlinks of *sum are present
+ for s in {md5,b2,sha1,sha224,sha256,sha384,sha512}sum
+ do test $(readlink /tmp/usr/local/bin/${s}) = coreutils
+ done
- name: "`make UTILS=XXX`"
shell: bash
run: |
@@ -637,6 +630,8 @@ jobs:
- uses: actions/checkout@v6
with:
persist-credentials: false
+ - name: Avoid no space left on device
+ run: sudo rm -rf /usr/share/dotnet /usr/local/lib/android &
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_MIN_SRV }}
@@ -692,7 +687,7 @@ jobs:
outputs TARGET_ARCH TARGET_OS
# package name
PKG_suffix=".tar.gz" ; case '${{ matrix.job.target }}' in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
- PKG_BASENAME=${PROJECT_NAME}-${REF_TAG:-$REF_SHAS}-${{ matrix.job.target }}
+ PKG_BASENAME=${PROJECT_NAME}-${{ matrix.job.target }}
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
outputs PKG_suffix PKG_BASENAME PKG_NAME
# deployable tag? (ie, leading "vM" or "M"; M == version number)
@@ -861,8 +856,6 @@ jobs:
if: matrix.job.skip-tests != true
shell: bash
run: |
- command -v sudo && sudo rm -rf /usr/local/lib/android /usr/share/dotnet # avoid no space left
- df -h ||:
## Test individual utilities
${{ steps.vars.outputs.CARGO_CMD }} ${{ steps.vars.outputs.CARGO_CMD_OPTIONS }} test --target=${{ matrix.job.target }} \
${{ matrix.job.cargo-options }} ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }}
@@ -897,6 +890,20 @@ jobs:
*) tar czf '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* ;;
esac
popd >/dev/null
+ - name: Package manpages and completions
+ if: matrix.job.target == 'x86_64-unknown-linux-gnu' && matrix.job.features == 'feat_os_unix,uudoc'
+ run: |
+ mkdir -p share/{man/man1,bash-completion/completions,fish/vendor_completions.d,zsh/site-functions,elvish/lib}
+ _uudoc=target/${{ matrix.job.target }}/release/uudoc
+ for bin in $('target/${{ matrix.job.target }}/release/coreutils' --list) coreutils;do
+ ${_uudoc} manpage ${bin} > share/man/man1/${bin}.1
+ ${_uudoc} completion ${bin} bash > share/bash-completion/completions/${bin}.bash
+ ${_uudoc} completion ${bin} fish > share/fish/vendor_completions.d/${bin}.fish
+ ${_uudoc} completion ${bin} zsh > share/zsh/site-functions/_${bin}
+ ${_uudoc} completion ${bin} elvish > share/elvish/lib/${bin}.elv
+ done
+ rm share/zsh/site-functions/_[ # not supported
+ tar --zstd -cf docs.tar.zst share
- name: Publish
uses: softprops/action-gh-release@v2
if: steps.vars.outputs.DEPLOY && matrix.job.skip-publish != true
@@ -904,18 +911,19 @@ jobs:
draft: true
files: |
${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_NAME }}
+ docs.tar.zst
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish latest commit
uses: softprops/action-gh-release@v2
- if: steps.vars.outputs.DEPLOY && matrix.job.skip-publish != true
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.job.skip-publish != true
with:
tag_name: latest-commit
- force_update: true
draft: false
prerelease: true
files: |
${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_NAME }}
+ docs.tar.zst
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1264,13 +1272,13 @@ jobs:
- uses: actions/checkout@v6
with:
persist-credentials: false
+ - name: Avoid no space left on device
+ run: sudo rm -rf /usr/share/dotnet /usr/local/lib/android &
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: build and test all features individually
shell: bash
run: |
- command -v sudo && sudo rm -rf /usr/local/lib/android /usr/share/dotnet # avoid no space left
- df -h ||:
CARGO_FEATURES_OPTION='--features=${{ matrix.job.features }}' ;
for f in $(util/show-utils.sh ${CARGO_FEATURES_OPTION})
do
diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml
index 39251c584..0f8ed7fd1 100644
--- a/.github/workflows/GnuTests.yml
+++ b/.github/workflows/GnuTests.yml
@@ -233,15 +233,6 @@ jobs:
lima ls -laZ /etc/selinux
lima sudo sestatus
- # Ensure we're running in enforcing mode
- lima sudo setenforce 1
- lima getenforce
-
- # Create test files with SELinux contexts for testing
- lima sudo mkdir -p /var/test_selinux
- lima sudo touch /var/test_selinux/test_file
- lima sudo chcon -t etc_t /var/test_selinux/test_file
- lima ls -Z /var/test_selinux/test_file # Verify context
- name: Install dependencies in VM
run: |
lima sudo dnf -y update
@@ -267,8 +258,16 @@ jobs:
lima bash -c "cd ~/work/uutils/ && echo 'Found SELinux tests:'; wc -l selinux-tests.txt"
- name: Run GNU SELinux tests
run: |
+ # Ensure we're running in enforcing mode
lima sudo setenforce 1
lima getenforce
+
+ # Create test files with SELinux contexts for testing
+ lima sudo mkdir -p /var/test_selinux
+ lima sudo touch /var/test_selinux/test_file
+ lima sudo chcon -t etc_t /var/test_selinux/test_file
+ lima ls -Z /var/test_selinux/test_file # Verify context
+
lima cat /proc/filesystems
lima bash -c "cd ~/work/uutils/ && bash util/run-gnu-test.sh \$(cat selinux-tests.txt)"
- name: Extract testing info from individual logs into JSON
diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml
index ccbf7be9b..549f2ba85 100644
--- a/.github/workflows/freebsd.yml
+++ b/.github/workflows/freebsd.yml
@@ -134,7 +134,7 @@ jobs:
with:
persist-credentials: false
- name: Avoid no space left on device (Ubuntu runner)
- run: sudo rm -rf /usr/share/dotnet /usr/local/lib/android
+ run: sudo rm -rf /usr/share/dotnet /usr/local/lib/android &
- uses: Swatinem/rust-cache@v2
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.9
diff --git a/.github/workflows/ignore-intermittent.txt b/.github/workflows/ignore-intermittent.txt
index 1e5086c10..e6cb5dc64 100644
--- a/.github/workflows/ignore-intermittent.txt
+++ b/.github/workflows/ignore-intermittent.txt
@@ -8,3 +8,4 @@ tests/tty/tty-eof
tests/misc/stdbuf
tests/misc/usage_vs_getopt
tests/misc/tee
+tests/tail/follow-name
diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt
index 33f495948..e4987609f 100644
--- a/.vscode/cspell.dictionaries/jargon.wordlist.txt
+++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt
@@ -184,6 +184,7 @@ inacc
maint
proc
procs
+TOCTOU
# * constants
xffff
@@ -202,6 +203,7 @@ nofield
# * clippy
uninlined
nonminimal
+rposition
# * CPU/hardware features
ASIMD
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a7a006221..a8e463707 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -78,11 +78,11 @@ issues and writing documentation are just as important as writing code.
We can't fix bugs we don't know about, so good issues are super helpful! Here
are some tips for writing good issues:
-- If you find a bug, make sure it's still a problem on the `main` branch.
+- If you find a bug, make sure it's still a problem on the [`main` branch](https://github.com/uutils/coreutils/releases/tag/latest-commit).
- Search through the existing issues to see whether it has already been
reported.
- Make sure to include all relevant information, such as:
- - Which version of uutils did you check?
+ - Which version or commit hash of uutils did you check?
- Which version of GNU coreutils are you comparing with?
- What platform are you on?
- Provide a way to reliably reproduce the issue.
@@ -250,8 +250,8 @@ gitignore: add temporary files
- It's up to you whether you want to use `git merge main` or
`git rebase main`.
- Feel free to ask for help with merge conflicts.
-- You do not need to ping maintainers to request a review, but it's fine to do
- so if you don't get a response within a few days.
+- You do not need to ping maintainers to request a review immediately after submission. If you do not get a response to your patch within a few days, it is fine to request a review.
+ - If after a week your patch has still not been reviewed, we recommend that you ping the maintainers on our Discord channel in `#coreutils-chat`.
## Platforms
diff --git a/Cargo.toml b/Cargo.toml
index 77738518f..2a3625625 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -549,6 +549,7 @@ uutests.workspace = true
uucore = { workspace = true, features = [
"mode",
"entries",
+ "pipes",
"process",
"signals",
"utmpx",
diff --git a/README.md b/README.md
index 59a2e2c6b..450dae317 100644
--- a/README.md
+++ b/README.md
@@ -29,8 +29,9 @@ options might be missing or different behavior might be experienced.
-We provide prebuilt binaries at https://github.com/uutils/coreutils/releases/latest .
-It is recommended to install from main branch if you install from source.
+We provide prebuilt binaries, manpages, and shell completions from main branch at https://github.com/uutils/coreutils/releases/tag/latest-commit .
+The latest stable tag https://github.com/uutils/coreutils/releases/latest exists only for reproducible products and packagers.
+You should use binary from latest commit generally.
diff --git a/src/uu/comm/src/comm.rs b/src/uu/comm/src/comm.rs
index 80b20b53f..37ac4de1c 100644
--- a/src/uu/comm/src/comm.rs
+++ b/src/uu/comm/src/comm.rs
@@ -136,6 +136,11 @@ pub fn are_files_identical(path1: &Path, path2: &Path) -> io::Result {
return Ok(false);
}
+ // only proceed if both are regular files
+ if !metadata1.is_file() || !metadata2.is_file() {
+ return Ok(false);
+ }
+
let file1 = File::open(path1)?;
let file2 = File::open(path2)?;
diff --git a/src/uu/cp/benches/cp_bench.rs b/src/uu/cp/benches/cp_bench.rs
index d673c14e4..84954f0bb 100644
--- a/src/uu/cp/benches/cp_bench.rs
+++ b/src/uu/cp/benches/cp_bench.rs
@@ -82,20 +82,25 @@ fn cp_preserve_metadata(
#[divan::bench(args = [16])]
fn cp_large_file(bencher: Bencher, size_mb: usize) {
- let temp_dir = TempDir::new().unwrap();
- let source = temp_dir.path().join("source.bin");
- let dest = temp_dir.path().join("dest.bin");
+ bencher
+ .with_inputs(|| {
+ let temp_dir = TempDir::new().unwrap();
+ let source = temp_dir.path().join("source.bin");
+ binary_data::create_file(&source, size_mb, b'x');
+ (temp_dir, source)
+ })
+ .counter(divan::counter::BytesCount::new(size_mb * 1024 * 1024))
+ .bench_values(|(temp_dir, source)| {
+ // Use unique destination name to avoid filesystem allocation variance
+ let dest = temp_dir.path().join(format!(
+ "dest_{}.bin",
+ std::ptr::addr_of!(temp_dir) as usize
+ ));
+ let source_str = source.to_str().unwrap();
+ let dest_str = dest.to_str().unwrap();
- binary_data::create_file(&source, size_mb, b'x');
-
- let source_str = source.to_str().unwrap();
- let dest_str = dest.to_str().unwrap();
-
- bencher.bench(|| {
- fs_utils::remove_path(&dest);
-
- black_box(run_util_function(uumain, &[source_str, dest_str]));
- });
+ black_box(run_util_function(uumain, &[source_str, dest_str]));
+ });
}
fn main() {
diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs
index f5f4f9365..ebcc737fd 100644
--- a/src/uu/dd/src/dd.rs
+++ b/src/uu/dd/src/dd.rs
@@ -30,7 +30,7 @@ use std::cmp;
use std::env;
use std::ffi::OsString;
use std::fs::{File, OpenOptions};
-use std::io::{self, Read, Seek, SeekFrom, Stdout, Write};
+use std::io::{self, Read, Seek, SeekFrom, Write};
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::fd::AsFd;
#[cfg(any(target_os = "linux", target_os = "android"))]
@@ -601,7 +601,7 @@ enum Density {
/// Data destinations.
enum Dest {
/// Output to stdout.
- Stdout(Stdout),
+ Stdout(File),
/// Output to a file.
///
@@ -829,7 +829,8 @@ struct Output<'a> {
impl<'a> Output<'a> {
/// Instantiate this struct with stdout as a destination.
fn new_stdout(settings: &'a Settings) -> UResult {
- let mut dst = Dest::Stdout(io::stdout());
+ let fx = OwnedFileDescriptorOrHandle::from(io::stdout())?;
+ let mut dst = Dest::Stdout(fx.into_file());
dst.seek(settings.seek, settings.obs)
.map_err_context(|| translate!("dd-error-write-error"))?;
Ok(Self { dst, settings })
diff --git a/src/uu/dirname/src/dirname.rs b/src/uu/dirname/src/dirname.rs
index 3399b4a03..6bd91d910 100644
--- a/src/uu/dirname/src/dirname.rs
+++ b/src/uu/dirname/src/dirname.rs
@@ -4,8 +4,9 @@
// file that was distributed with this source code.
use clap::{Arg, ArgAction, Command};
+use std::borrow::Cow;
use std::ffi::OsString;
-use std::path::Path;
+#[cfg(unix)]
use uucore::display::print_verbatim;
use uucore::error::{UResult, UUsageError};
use uucore::format_usage;
@@ -18,51 +19,84 @@ mod options {
pub const DIR: &str = "dir";
}
-/// Handle the special case where a path ends with "/."
+/// Perform dirname as pure string manipulation per POSIX/GNU behavior.
+///
+/// dirname should NOT normalize paths. It does simple string manipulation:
+/// 1. Strip trailing slashes (unless path is all slashes)
+/// 2. If ends with `/.` (possibly `//.` or `///.`), strip the `/+.` pattern
+/// 3. Otherwise, remove everything after the last `/`
+/// 4. If no `/` found, return `.`
+/// 5. Strip trailing slashes from result (unless result would be empty)
+///
+/// Examples:
+/// - `foo/.` → `foo`
+/// - `foo/./bar` → `foo/.`
+/// - `foo/bar` → `foo`
+/// - `a/b/c` → `a/b`
///
-/// This matches GNU/POSIX behavior where `dirname("/home/dos/.")` returns "/home/dos"
-/// rather than "/home" (which would be the result of `Path::parent()` due to normalization).
/// Per POSIX.1-2017 dirname specification and GNU coreutils manual:
/// - POSIX:
/// - GNU:
///
-/// dirname should do simple string manipulation without path normalization.
/// See issue #8910 and similar fix in basename (#8373, commit c5268a897).
-///
-/// Returns `Some(())` if the special case was handled (output already printed),
-/// or `None` if normal `Path::parent()` logic should be used.
-fn handle_trailing_dot(path_bytes: &[u8]) -> Option<()> {
- if !path_bytes.ends_with(b"/.") {
- return None;
+fn dirname_string_manipulation(path_bytes: &[u8]) -> Cow<'_, [u8]> {
+ if path_bytes.is_empty() {
+ return Cow::Borrowed(b".");
}
- // Strip the "/." suffix and print the result
- if path_bytes.len() == 2 {
- // Special case: "/." -> "/"
- print!("/");
- Some(())
- } else {
- // General case: "/home/dos/." -> "/home/dos"
- let stripped = &path_bytes[..path_bytes.len() - 2];
- #[cfg(unix)]
- {
- use std::os::unix::ffi::OsStrExt;
- let result = std::ffi::OsStr::from_bytes(stripped);
- print_verbatim(result).unwrap();
- Some(())
- }
- #[cfg(not(unix))]
- {
- // On non-Unix, fall back to lossy conversion
- if let Ok(s) = std::str::from_utf8(stripped) {
- print!("{s}");
- Some(())
- } else {
- // Can't handle non-UTF-8 on non-Unix, fall through to normal logic
- None
+ let mut bytes = path_bytes;
+
+ // Step 1: Strip trailing slashes (but not if the entire path is slashes)
+ let all_slashes = bytes.iter().all(|&b| b == b'/');
+ if all_slashes {
+ return Cow::Borrowed(b"/");
+ }
+
+ while bytes.len() > 1 && bytes.ends_with(b"/") {
+ bytes = &bytes[..bytes.len() - 1];
+ }
+
+ // Step 2: Check if it ends with `/.` and strip the `/+.` pattern
+ if bytes.ends_with(b".") && bytes.len() >= 2 {
+ let dot_pos = bytes.len() - 1;
+ if bytes[dot_pos - 1] == b'/' {
+ // Find where the slashes before the dot start
+ let mut slash_start = dot_pos - 1;
+ while slash_start > 0 && bytes[slash_start - 1] == b'/' {
+ slash_start -= 1;
}
+ // Return the stripped result
+ if slash_start == 0 {
+ // Result would be empty
+ return if path_bytes.starts_with(b"/") {
+ Cow::Borrowed(b"/")
+ } else {
+ Cow::Borrowed(b".")
+ };
+ }
+ return Cow::Borrowed(&bytes[..slash_start]);
}
}
+
+ // Step 3: Normal dirname - find last / and remove everything after it
+ if let Some(last_slash_pos) = bytes.iter().rposition(|&b| b == b'/') {
+ // Found a slash, remove everything after it
+ let mut result = &bytes[..last_slash_pos];
+
+ // Strip trailing slashes from result (but keep at least one if at the start)
+ while result.len() > 1 && result.ends_with(b"/") {
+ result = &result[..result.len() - 1];
+ }
+
+ if result.is_empty() {
+ return Cow::Borrowed(b"/");
+ }
+
+ return Cow::Borrowed(result);
+ }
+
+ // No slash found, return "."
+ Cow::Borrowed(b".")
}
#[uucore::main]
@@ -83,27 +117,25 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
for path in &dirnames {
let path_bytes = uucore::os_str_as_bytes(path.as_os_str()).unwrap_or(&[]);
+ let result = dirname_string_manipulation(path_bytes);
- if handle_trailing_dot(path_bytes).is_none() {
- // Normal path handling using Path::parent()
- let p = Path::new(path);
- match p.parent() {
- Some(d) => {
- if d.components().next().is_none() {
- print!(".");
- } else {
- print_verbatim(d).unwrap();
- }
- }
- None => {
- if p.is_absolute() || path.as_os_str() == "/" {
- print!("/");
- } else {
- print!(".");
- }
- }
+ #[cfg(unix)]
+ {
+ use std::os::unix::ffi::OsStrExt;
+ let result_os = std::ffi::OsStr::from_bytes(&result);
+ print_verbatim(result_os).unwrap();
+ }
+ #[cfg(not(unix))]
+ {
+ // On non-Unix, fall back to lossy conversion
+ if let Ok(s) = std::str::from_utf8(&result) {
+ print!("{s}");
+ } else {
+ // Fallback for non-UTF-8 paths on non-Unix systems
+ print!(".");
}
}
+
print!("{line_ending}");
}
diff --git a/src/uu/du/benches/du_bench.rs b/src/uu/du/benches/du_bench.rs
index 8a2d29246..0b63ce9a2 100644
--- a/src/uu/du/benches/du_bench.rs
+++ b/src/uu/du/benches/du_bench.rs
@@ -61,25 +61,49 @@ fn du_human_balanced_tree(
/// Benchmark du on wide directory structures (many files/dirs, shallow)
#[divan::bench(args = [(5000, 500)])]
fn du_wide_tree(bencher: Bencher, (total_files, total_dirs): (usize, usize)) {
- let temp_dir = TempDir::new().unwrap();
- fs_tree::create_wide_tree(temp_dir.path(), total_files, total_dirs);
- bench_du_with_args(bencher, &temp_dir, &[]);
+ bencher
+ .with_inputs(|| {
+ let temp_dir = TempDir::new().unwrap();
+ fs_tree::create_wide_tree(temp_dir.path(), total_files, total_dirs);
+ temp_dir
+ })
+ .bench_values(|temp_dir| {
+ let temp_path_str = temp_dir.path().to_str().unwrap();
+ let args = vec![temp_path_str];
+ black_box(run_util_function(uumain, &args));
+ });
}
/// Benchmark du -a on wide directory structures
#[divan::bench(args = [(5000, 500)])]
fn du_all_wide_tree(bencher: Bencher, (total_files, total_dirs): (usize, usize)) {
- let temp_dir = TempDir::new().unwrap();
- fs_tree::create_wide_tree(temp_dir.path(), total_files, total_dirs);
- bench_du_with_args(bencher, &temp_dir, &["-a"]);
+ bencher
+ .with_inputs(|| {
+ let temp_dir = TempDir::new().unwrap();
+ fs_tree::create_wide_tree(temp_dir.path(), total_files, total_dirs);
+ temp_dir
+ })
+ .bench_values(|temp_dir| {
+ let temp_path_str = temp_dir.path().to_str().unwrap();
+ let args = vec![temp_path_str, "-a"];
+ black_box(run_util_function(uumain, &args));
+ });
}
/// Benchmark du on deep directory structures
#[divan::bench(args = [(100, 3)])]
fn du_deep_tree(bencher: Bencher, (depth, files_per_level): (usize, usize)) {
- let temp_dir = TempDir::new().unwrap();
- fs_tree::create_deep_tree(temp_dir.path(), depth, files_per_level);
- bench_du_with_args(bencher, &temp_dir, &[]);
+ bencher
+ .with_inputs(|| {
+ let temp_dir = TempDir::new().unwrap();
+ fs_tree::create_deep_tree(temp_dir.path(), depth, files_per_level);
+ temp_dir
+ })
+ .bench_values(|temp_dir| {
+ let temp_path_str = temp_dir.path().to_str().unwrap();
+ let args = vec![temp_path_str];
+ black_box(run_util_function(uumain, &args));
+ });
}
/// Benchmark du -s (summarize) on balanced tree
diff --git a/src/uu/install/locales/en-US.ftl b/src/uu/install/locales/en-US.ftl
index 0261f7320..76265a2b1 100644
--- a/src/uu/install/locales/en-US.ftl
+++ b/src/uu/install/locales/en-US.ftl
@@ -30,7 +30,7 @@ install-error-chown-failed = failed to chown { $path }: { $error }
install-error-invalid-target = invalid target { $path }: No such file or directory
install-error-target-not-dir = target { $path } is not a directory
install-error-backup-failed = cannot backup { $from } to { $to }
-install-error-install-failed = cannot install { $from } to { $to }
+install-error-install-failed = cannot install { $from } to { $to }: { $error }
install-error-strip-failed = strip program failed: { $error }
install-error-strip-abnormal = strip process terminated abnormally - exit code: { $code }
install-error-metadata-failed = metadata error
diff --git a/src/uu/install/locales/fr-FR.ftl b/src/uu/install/locales/fr-FR.ftl
index 208712c21..330ceb7b4 100644
--- a/src/uu/install/locales/fr-FR.ftl
+++ b/src/uu/install/locales/fr-FR.ftl
@@ -30,7 +30,7 @@ install-error-chown-failed = échec du chown { $path } : { $error }
install-error-invalid-target = cible invalide { $path } : Aucun fichier ou répertoire de ce type
install-error-target-not-dir = la cible { $path } n'est pas un répertoire
install-error-backup-failed = impossible de sauvegarder { $from } vers { $to }
-install-error-install-failed = impossible d'installer { $from } vers { $to }
+install-error-install-failed = impossible d'installer { $from } vers { $to }: { $error }
install-error-strip-failed = échec du programme strip : { $error }
install-error-strip-abnormal = le processus strip s'est terminé anormalement - code de sortie : { $code }
install-error-metadata-failed = erreur de métadonnées
diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs
index d3e0b3e89..e128470fc 100644
--- a/src/uu/install/src/install.rs
+++ b/src/uu/install/src/install.rs
@@ -14,8 +14,8 @@ use filetime::{FileTime, set_file_times};
use selinux::SecurityContext;
use std::ffi::OsString;
use std::fmt::Debug;
-use std::fs::File;
use std::fs::{self, metadata};
+use std::fs::{File, OpenOptions};
use std::path::{MAIN_SEPARATOR, Path, PathBuf};
use std::process;
use thiserror::Error;
@@ -36,7 +36,7 @@ use uucore::translate;
use uucore::{format_usage, show, show_error, show_if_err};
#[cfg(unix)]
-use std::os::unix::fs::{FileTypeExt, MetadataExt};
+use std::os::unix::fs::MetadataExt;
#[cfg(unix)]
use std::os::unix::prelude::OsStrExt;
@@ -88,8 +88,8 @@ enum InstallError {
#[error("{}", translate!("install-error-backup-failed", "from" => .0.quote(), "to" => .1.quote()))]
BackupFailed(PathBuf, PathBuf, #[source] std::io::Error),
- #[error("{}", translate!("install-error-install-failed", "from" => .0.quote(), "to" => .1.quote()))]
- InstallFailed(PathBuf, PathBuf, #[source] std::io::Error),
+ #[error("{}", translate!("install-error-install-failed", "from" => .0.quote(), "to" => .1.quote(), "error" => .2.clone()))]
+ InstallFailed(PathBuf, PathBuf, String),
#[error("{}", translate!("install-error-strip-failed", "error" => .0.clone()))]
StripProgramFailed(String),
@@ -796,22 +796,6 @@ fn perform_backup(to: &Path, b: &Behavior) -> UResult