From d5c9c233cc6cc4a3f00d97becfd3370802a2f7f5 Mon Sep 17 00:00:00 2001 From: Christopher Dryden Date: Fri, 6 Feb 2026 20:34:06 +0000 Subject: [PATCH 01/46] dd: remove signal-hook dependency, use raw sigaction handler --- Cargo.lock | 15 ++------ Cargo.toml | 1 - src/uu/dd/Cargo.toml | 3 +- src/uu/dd/src/dd.rs | 30 ++++++---------- src/uu/dd/src/progress.rs | 50 ++++++-------------------- src/uucore/src/lib/features/signals.rs | 18 +++++++++- 6 files changed, 42 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 489778c5a..15fe97597 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -755,7 +755,7 @@ dependencies = [ "mio", "parking_lot", "rustix", - "signal-hook 0.3.18", + "signal-hook", "signal-hook-mio", "winapi", ] @@ -2765,16 +2765,6 @@ dependencies = [ "signal-hook-registry", ] -[[package]] -name = "signal-hook" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-mio" version = "0.2.5" @@ -2783,7 +2773,7 @@ checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", - "signal-hook 0.3.18", + "signal-hook", ] [[package]] @@ -3410,7 +3400,6 @@ dependencies = [ "gcd", "libc", "nix", - "signal-hook 0.4.3", "tempfile", "thiserror 2.0.18", "uucore", diff --git a/Cargo.toml b/Cargo.toml index 5b2b1c83c..936a27d68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -377,7 +377,6 @@ self_cell = "1.0.4" # FIXME we use the exact version because the new 0.5.3 requires an MSRV of 1.88 selinux = "=0.5.2" string-interner = "0.19.0" -signal-hook = "0.4.1" tempfile = "3.15.0" terminal_size = "0.4.0" textwrap = { version = "0.16.1", features = ["terminal_size"] } diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 5d5819c30..9da9143a8 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -32,8 +32,7 @@ thiserror = { workspace = true } fluent = { workspace = true } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] -nix = { workspace = true, features = ["fs"] } -signal-hook = { workspace = true } +nix = { workspace = true, features = ["fs", "signal"] } [[bin]] name = "dd" diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 48f7ef1b9..9f83b475d 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -23,6 +23,8 @@ use nix::fcntl::OFlag; use parseargs::Parser; use progress::ProgUpdateType; use progress::{ProgUpdate, ReadStat, StatusLevel, WriteStat, gen_prog_updater}; +#[cfg(target_os = "linux")] +use progress::{check_and_reset_sigusr1, install_sigusr1_handler}; use uucore::io::OwnedFileDescriptorOrHandle; use uucore::translate; @@ -122,18 +124,9 @@ impl Alarm { Self { interval, trigger } } - /// Returns a closure that allows to manually trigger the alarm - /// - /// This is useful for cases where more than one alarm even source exists - /// In case of `dd` there is the SIGUSR1/SIGINFO case where we want to - /// trigger an manual progress report. - pub fn manual_trigger_fn(&self) -> Box { - let weak_trigger = Arc::downgrade(&self.trigger); - Box::new(move || { - if let Some(trigger) = weak_trigger.upgrade() { - trigger.store(ALARM_TRIGGER_SIGNAL, Relaxed); - } - }) + /// Manually trigger the alarm as a signal event + pub fn manual_trigger(&self) { + self.trigger.store(ALARM_TRIGGER_SIGNAL, Relaxed); } /// Use this function to poll for any pending alarm event @@ -1183,14 +1176,9 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> { // This avoids the need to query the OS monotonic clock for every block. let alarm = Alarm::with_interval(Duration::from_secs(1)); - // The signal handler spawns an own thread that waits for signals. - // When the signal is received, it calls a handler function. - // We inject a handler function that manually triggers the alarm. #[cfg(target_os = "linux")] - let signal_handler = progress::SignalHandler::install_signal_handler(alarm.manual_trigger_fn()); - #[cfg(target_os = "linux")] - if let Err(e) = &signal_handler { - if Some(StatusLevel::None) != i.settings.status { + if let Err(e) = install_sigusr1_handler() { + if i.settings.status != Some(StatusLevel::None) { eprintln!("{}\n\t{e}", translate!("dd-warning-signal-handler")); } } @@ -1270,6 +1258,10 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> { // error. rstat += rstat_update; wstat += wstat_update; + #[cfg(target_os = "linux")] + if check_and_reset_sigusr1() { + alarm.manual_trigger(); + } match alarm.get_trigger() { ALARM_TRIGGER_NONE => {} t @ (ALARM_TRIGGER_TIMER | ALARM_TRIGGER_SIGNAL) => { diff --git a/src/uu/dd/src/progress.rs b/src/uu/dd/src/progress.rs index c7aef3387..cc5527f55 100644 --- a/src/uu/dd/src/progress.rs +++ b/src/uu/dd/src/progress.rs @@ -10,13 +10,10 @@ //! [`gen_prog_updater`] function can be used to implement a progress //! updater that runs in its own thread. use std::io::Write; +#[cfg(target_os = "linux")] +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc; -#[cfg(target_os = "linux")] -use std::thread::JoinHandle; use std::time::Duration; - -#[cfg(target_os = "linux")] -use signal_hook::iterator::Handle; use uucore::{ error::{UResult, set_exit_code}, format::num_format::{FloatVariant, Formatter}, @@ -448,47 +445,22 @@ pub(crate) fn gen_prog_updater( } } -/// signal handler listens for SIGUSR1 signal and runs provided closure. #[cfg(target_os = "linux")] -pub(crate) struct SignalHandler { - handle: Handle, - thread: Option>, +static SIGUSR1_RECEIVED: AtomicBool = AtomicBool::new(false); + +#[cfg(target_os = "linux")] +pub(crate) fn check_and_reset_sigusr1() -> bool { + SIGUSR1_RECEIVED.swap(false, Ordering::Relaxed) } #[cfg(target_os = "linux")] -impl SignalHandler { - pub(crate) fn install_signal_handler( - f: Box, - ) -> Result { - use signal_hook::consts::signal::SIGUSR1; - use signal_hook::iterator::Signals; - - let mut signals = Signals::new([SIGUSR1])?; - let handle = signals.handle(); - let thread = std::thread::spawn(move || { - for signal in &mut signals { - match signal { - SIGUSR1 => (*f)(), - _ => unreachable!(), - } - } - }); - - Ok(Self { - handle, - thread: Some(thread), - }) - } +extern "C" fn sigusr1_handler(_: std::os::raw::c_int) { + SIGUSR1_RECEIVED.store(true, Ordering::Relaxed); } #[cfg(target_os = "linux")] -impl Drop for SignalHandler { - fn drop(&mut self) { - self.handle.close(); - if let Some(thread) = std::mem::take(&mut self.thread) { - thread.join().unwrap(); - } - } +pub(crate) fn install_sigusr1_handler() -> Result<(), nix::errno::Errno> { + uucore::signals::install_signal_handler(nix::sys::signal::Signal::SIGUSR1, sigusr1_handler) } /// Return a closure that can be used in its own thread to print progress info. diff --git a/src/uucore/src/lib/features/signals.rs b/src/uucore/src/lib/features/signals.rs index 6d0956b39..319b36048 100644 --- a/src/uucore/src/lib/features/signals.rs +++ b/src/uucore/src/lib/features/signals.rs @@ -14,7 +14,8 @@ use nix::errno::Errno; #[cfg(unix)] use nix::sys::signal::{ - SigHandler::SigDfl, SigHandler::SigIgn, Signal::SIGINT, Signal::SIGPIPE, signal, + SaFlags, SigAction, SigHandler, SigHandler::SigDfl, SigHandler::SigIgn, SigSet, Signal, + Signal::SIGINT, Signal::SIGPIPE, sigaction, signal, }; /// The default signal value. @@ -435,6 +436,21 @@ pub fn ignore_interrupts() -> Result<(), Errno> { unsafe { signal(SIGINT, SigIgn) }.map(|_| ()) } +/// Installs a signal handler. The handler must be async-signal-safe. +#[cfg(unix)] +pub fn install_signal_handler( + sig: Signal, + handler: extern "C" fn(std::os::raw::c_int), +) -> Result<(), Errno> { + let action = SigAction::new( + SigHandler::Handler(handler), + SaFlags::SA_RESTART, + SigSet::empty(), + ); + unsafe { sigaction(sig, &action) }?; + Ok(()) +} + // Detect closed stdin/stdout before Rust reopens them as /dev/null (see issue #2873) #[cfg(unix)] use std::sync::atomic::{AtomicBool, Ordering}; From d422445c73dc06073a0dc1c69f1540a679f2e1cb Mon Sep 17 00:00:00 2001 From: Christopher Dryden Date: Fri, 6 Feb 2026 21:04:44 +0000 Subject: [PATCH 02/46] fix doc comment referencing removed manual_trigger_fn --- src/uu/dd/src/dd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 9f83b475d..52c8bc4b2 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -92,7 +92,7 @@ struct Settings { /// /// After being constructed with [`Alarm::with_interval`], [`Alarm::get_trigger`] /// will return [`ALARM_TRIGGER_TIMER`] once per the given [`Duration`]. -/// Alarm can be manually triggered with closure returned by [`Alarm::manual_trigger_fn`]. +/// Alarm can be manually triggered with [`Alarm::manual_trigger`]. /// [`Alarm::get_trigger`] will return [`ALARM_TRIGGER_SIGNAL`] in this case. /// /// Can be cloned, but the trigger status is shared across all instances so only From bdf3b826f005bf8d9a107c851c246cce12f8c13f Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Sat, 7 Feb 2026 07:54:25 +0900 Subject: [PATCH 03/46] Drop release-fast profile for simplicity (#10476) --- .github/workflows/CICD.yml | 10 +++++----- Cargo.toml | 11 ++++++----- README.md | 14 ++++---------- docs/src/packaging.md | 13 +++---------- 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 8361e0b7a..d699a104a 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -322,13 +322,13 @@ jobs: disable_search: true flags: makefile,${{ matrix.job.os }} fail_ci_if_error: false - - name: "`make install PROG_PREFIX=uu- PROFILE=release-fast COMPLETIONS=n MANPAGES=n LOCALES=n`" + - name: "`make install PROG_PREFIX=uu- PROFILE=release-small COMPLETIONS=n MANPAGES=n LOCALES=n`" shell: bash run: | set -x - DESTDIR=/tmp/ make install PROG_PREFIX=uu- PROFILE=release-fast COMPLETIONS=n MANPAGES=n LOCALES=n + DESTDIR=/tmp/ make install PROG_PREFIX=uu- PROFILE=release-small COMPLETIONS=n MANPAGES=n LOCALES=n # Check that utils are built with given profile - ./target/release-fast/true + ./target/release-small/true # Check that the progs have prefix test -f /tmp/usr/local/bin/uu-tty test -f /tmp/usr/local/libexec/uu-coreutils/libstdbuf.* @@ -501,10 +501,10 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.9 - - name: "`make install PROFILE=release-fast`" + - name: "`make install PROFILE=release`" shell: bash run: | - export CARGO_TARGET_DIR=cargo-target RUSTFLAGS="${RUSTFLAGS} -C strip=symbols" PROFILE=release-fast MANPAGES=n COMPLETIONS=n LOCALES=n + export CARGO_TARGET_DIR=cargo-target RUSTFLAGS="${RUSTFLAGS} -C strip=symbols" PROFILE=release MANPAGES=n COMPLETIONS=n LOCALES=n mkdir -p "${CARGO_TARGET_DIR}" && sudo mount -t tmpfs -o noatime,size=16G tmpfs "${CARGO_TARGET_DIR}" make install DESTDIR=target/size-release/ make install COMPLETIONS=n MULTICALL=y LN="ln -vf" DESTDIR=target/size-multi-release/ diff --git a/Cargo.toml b/Cargo.toml index d253149b3..cc5e1f454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -605,18 +605,19 @@ name = "uudoc" path = "src/bin/uudoc.rs" required-features = ["uudoc"] -# The default release profile with some optimizations. [profile.release] lto = true panic = "abort" - -[profile.release-fast] -inherits = "release" codegen-units = 1 +# FIXME: https://github.com/uutils/coreutils/issues/10654 +[profile.release.package.uu_expand] +codegen-units = 16 +[profile.release.package.uu_unexpand] +codegen-units = 16 # A release-like profile that is as small as possible. [profile.release-small] -inherits = "release-fast" +inherits = "release" opt-level = "z" strip = true diff --git a/README.md b/README.md index c9e2f66b0..6708d9dca 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ other Rust program: cargo build --release ``` -Replace `--release` with `--profile=release-fast` or `--profile=release-small` to use all optimizations or save binary size. +Replace `--release` with `--profile=release-small` to optimize binary size. This command builds the most portable common core set of uutils into a multicall (BusyBox-type) binary, named 'coreutils', on most Rust-supported platforms. @@ -154,10 +154,10 @@ To simply build all available utilities (with debug profile): make ``` -In release-fast mode: +In release mode: ```shell -make PROFILE=release-fast +make PROFILE=release ``` To build all but a few of the available utilities: @@ -191,18 +191,12 @@ manpages or shell completion to work, use `GNU Make` or see ### Install with GNU Make -To install all available utilities: +To install all available utilities (PROFILE=release by default): ```shell make install ``` -To install all utilities with all possible optimizations: - -```shell -make PROFILE=release-fast install -``` - To install using `sudo` switch `-E` must be used: ```shell diff --git a/docs/src/packaging.md b/docs/src/packaging.md index ed664863e..2ea169668 100644 --- a/docs/src/packaging.md +++ b/docs/src/packaging.md @@ -55,17 +55,10 @@ view the full documentation in the We provide three release profiles out of the box, though you may want to tweak them: -- `release`: This is the standard Rust release profile, but with link-time - optimization enabled. It is a balance between compile time, performance and a - reasonable amount of debug info. The main drawback of this profile is that the - binary is quite large (roughly 2x the GNU coreutils). -- `release-fast`: Every setting is tuned for the best performance, at the cost - of compile time. This binary is still quite large. -- `release-small`: Generates the smallest binary possible. This strips _all_ - debug info from the binary, resulting in less informative backtraces. The performance of - this profile is also really good as it is close to the `release-fast` profile, - but with all debuginfo stripped. +- `release`: The profile with all performance optimization enabled. +- `release-small`: Optimize binary size. +They include panic abort which removes stack traces on old rust [https://blog.rust-lang.org/2025/12/11/Rust-1.92.0/]. For the precise definition of these profiles, you can look at the root [`Cargo.toml`](https://github.com/uutils/coreutils/blob/main/Cargo.toml). From 181b247531abd75e55d8152f5269b7aaffc88508 Mon Sep 17 00:00:00 2001 From: Etienne Cordonnier Date: Fri, 6 Feb 2026 23:55:44 +0100 Subject: [PATCH 04/46] stdbuf: support libstdbuf in same directory as stdbuf (#10352) Make stdbuf search for libstdbuf first in the directory where stdbuf is running. This matches GNU coreutils behavior. Add a symlink to deps/libstdbuf in order to make it easier to run the stdbuf tests with feat_external_stdbuf enabled. Remove tests which are assuming that libstdbuf is not found. Those tests are now always failing, because the build directory contains a symlink to deps/libstdbuf and libstdbuf is always found. Fixes https://github.com/uutils/coreutils/issues/10345 Signed-off-by: Etienne Cordonnier --- src/uu/stdbuf/build.rs | 36 +++++++++++ src/uu/stdbuf/src/stdbuf.rs | 29 +++++++-- tests/by-util/test_stdbuf.rs | 118 ++++++++++++++++++++++++++++------- 3 files changed, 155 insertions(+), 28 deletions(-) diff --git a/src/uu/stdbuf/build.rs b/src/uu/stdbuf/build.rs index d844f3790..43eea3f38 100644 --- a/src/uu/stdbuf/build.rs +++ b/src/uu/stdbuf/build.rs @@ -145,4 +145,40 @@ fn main() { found, "Could not find built libstdbuf library. Searched in: {possible_paths:?}." ); + + // Create a symlink to libstdbuf in the main target directory for development convenience + // This allows running stdbuf directly from the build directory (e.g., target/debug/coreutils) + // without needing to install the library to LIBSTDBUF_DIR. This is particularly useful for + // running tests and manual testing during development. + #[cfg(all(unix, feature = "feat_external_libstdbuf"))] + { + use std::path::PathBuf; + + // Get the main target directory (e.g., target/debug or target/release) + // OUT_DIR is something like target/debug/build/uu_stdbuf-/out + let out_dir_path = PathBuf::from(&out_dir); + if let Some(target_dir) = out_dir_path + .parent() + .and_then(|p| p.parent()) + .and_then(|p| p.parent()) + { + let lib_filename = format!("libstdbuf{}", platform::DYLIB_EXT); + let source = target_dir.join("deps").join(&lib_filename); + let dest = target_dir.join(&lib_filename); + + // Remove old symlink if it exists (in case it points to the wrong place) + let _ = fs::remove_file(&dest); + + // Create symlink if the source exists + if source.exists() { + #[cfg(unix)] + { + use std::os::unix::fs::symlink; + if let Err(e) = symlink(&source, &dest) { + eprintln!("Warning: Failed to create symlink for libstdbuf: {e}"); + } + } + } + } + } } diff --git a/src/uu/stdbuf/src/stdbuf.rs b/src/uu/stdbuf/src/stdbuf.rs index bffe1f7b1..b5a8965cc 100644 --- a/src/uu/stdbuf/src/stdbuf.rs +++ b/src/uu/stdbuf/src/stdbuf.rs @@ -179,13 +179,34 @@ fn get_preload_env(_tmp_dir: &TempDir) -> UResult<(String, PathBuf)> { // Use the directory provided at compile time via LIBSTDBUF_DIR environment variable // This will fail to compile if LIBSTDBUF_DIR is not set, which is the desired behavior const LIBSTDBUF_DIR: &str = env!("LIBSTDBUF_DIR"); + + // Search paths in order: + // 1. Directory where stdbuf is located (program_path) + // 2. Compile-time directory from LIBSTDBUF_DIR + let mut search_paths: Vec = Vec::new(); + + // First, try to get the directory where stdbuf is running from + if let Ok(exe_path) = std::env::current_exe() { + if let Some(exe_dir) = exe_path.parent() { + search_paths.push(exe_dir.to_path_buf()); + } + } + + // Add the compile-time directory as fallback + search_paths.push(PathBuf::from(LIBSTDBUF_DIR)); + + // Search for libstdbuf in each path + for base_path in search_paths { + let path_buf = base_path.join("libstdbuf").with_extension(extension); + if path_buf.exists() { + return Ok((preload.to_owned(), path_buf)); + } + } + + // If not found in any path, report error let path_buf = PathBuf::from(LIBSTDBUF_DIR) .join("libstdbuf") .with_extension(extension); - if path_buf.exists() { - return Ok((preload.to_owned(), path_buf)); - } - Err(USimpleError::new( 1, translate!("stdbuf-error-external-libstdbuf-not-found", "path" => path_buf.display()), diff --git a/tests/by-util/test_stdbuf.rs b/tests/by-util/test_stdbuf.rs index 00e117f47..2f234f183 100644 --- a/tests/by-util/test_stdbuf.rs +++ b/tests/by-util/test_stdbuf.rs @@ -25,20 +25,101 @@ fn test_permission() { .stderr_contains("Permission denied"); } -// TODO: Tests below are brittle when feat_external_libstdbuf is enabled and libstdbuf is not installed. -// Align stdbuf with GNU search order to enable deterministic testing without installation: -// 1) search for libstdbuf next to the stdbuf binary, 2) then in LIBSTDBUF_DIR, 3) then system locations. -// After implementing this, rework tests to provide a temporary symlink rather than depending on system state. - -#[cfg(feature = "feat_external_libstdbuf")] +// LD_DEBUG is not available on macOS, OpenBSD, Android, or musl +#[cfg(all( + feature = "feat_external_libstdbuf", + not(target_os = "windows"), + not(target_os = "openbsd"), + not(target_os = "macos"), + not(target_os = "android"), + not(target_env = "musl") +))] #[test] -fn test_permission_external_missing_lib() { - // When built with external libstdbuf, running stdbuf fails early if lib is not installed - new_ucmd!() - .arg("-o1") - .arg(".") - .fails_with_code(1) - .stderr_contains("External libstdbuf not found"); +fn test_stdbuf_search_order_exe_dir_first() { + use std::fs; + use std::os::unix::fs::PermissionsExt; + + // Test that stdbuf searches for libstdbuf in its own directory first, + // before checking LIBSTDBUF_DIR. + let ts = TestScenario::new(util_name!()); + let temp_dir = tempfile::tempdir().unwrap(); + let temp_path = temp_dir.path(); + + // Determine the correct library extension for this platform + let lib_extension = if cfg!(target_vendor = "apple") { + "dylib" + } else { + "so" + }; + let lib_name = format!("libstdbuf.{lib_extension}"); + + // Look for libstdbuf in the build directory deps folder + // During build, libstdbuf.so is in target/debug/deps/ or target/release/deps/ + // This allows running tests without requiring installation to a root-owned path + // ts.bin_path is the path to the binary file, so we get its parent directory first + let source_lib = ts + .bin_path + .parent() + .expect("Binary should have a parent directory") + .join("deps") + .join(&lib_name); + + // Fail test if the library doesn't exist - it should have been built + assert!( + source_lib.exists(), + "libstdbuf not found at {}. It should have been built.", + source_lib.display() + ); + + // Copy stdbuf binary to temp directory + // ts.bin_path is the full path to the coreutils binary + let stdbuf_copy = temp_path.join("stdbuf"); + fs::copy(&ts.bin_path, &stdbuf_copy).unwrap(); + + // Make the copied binary executable + let mut perms = fs::metadata(&stdbuf_copy).unwrap().permissions(); + perms.set_mode(0o755); + fs::set_permissions(&stdbuf_copy, perms).unwrap(); + + // Copy libstdbuf to the same directory as stdbuf + let lib_copy = temp_path.join(&lib_name); + fs::copy(&source_lib, &lib_copy).unwrap(); + + // Run the copied stdbuf with LD_DEBUG to verify it loads the local libstdbuf + // This proves the exe-dir search happens first, before checking LIBSTDBUF_DIR + let output = std::process::Command::new(&stdbuf_copy) + .env("LD_DEBUG", "libs") + .args(["-o0", "echo", "test_output"]) + .output() + .unwrap(); + + let stderr = String::from_utf8_lossy(&output.stderr); + + // Verify the library was loaded from the temp directory (same dir as exe) + // LD_DEBUG output will show something like: + // " trying file=/tmp/.../libstdbuf.so" + let temp_dir_str = temp_path.to_string_lossy(); + let loaded_from_exe_dir = stderr + .lines() + .any(|line| line.contains(&*lib_name) && line.contains(&*temp_dir_str)); + + assert!( + loaded_from_exe_dir, + "libstdbuf should be loaded from exe directory ({}), not from LIBSTDBUF_DIR. LD_DEBUG output:\n{}", + temp_path.display(), + stderr + ); + + // The command should succeed and produce the expected output + assert!( + output.status.success(), + "stdbuf should succeed when libstdbuf is in the same directory. stderr: {stderr}" + ); + assert_eq!( + String::from_utf8_lossy(&output.stdout).trim(), + "test_output", + "stdbuf should execute echo successfully" + ); } #[cfg(not(feature = "feat_external_libstdbuf"))] @@ -51,17 +132,6 @@ fn test_no_such() { .stderr_contains("No such file or directory"); } -#[cfg(feature = "feat_external_libstdbuf")] -#[test] -fn test_no_such_external_missing_lib() { - // With external lib mode and missing installation, stdbuf fails before spawning the command - new_ucmd!() - .arg("-o1") - .arg("no_such") - .fails_with_code(1) - .stderr_contains("External libstdbuf not found"); -} - // Disabled on x86_64-unknown-linux-musl because the cross-rs Docker image for this target // does not provide musl-compiled system utilities (like head), leading to dynamic linker errors // when preloading musl-compiled libstdbuf.so into glibc-compiled binaries. Same thing for FreeBSD. From 3af5b71c621b2d2235ef53983a9f7a1b82b19cd4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 20:01:14 +0000 Subject: [PATCH 05/46] chore(deps): update rust crate memchr to v2.8.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c3e94dd5..421e88e6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1903,9 +1903,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" From a619d9618e5b5e1a15cf38a8e05c118618506e0d Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sat, 7 Feb 2026 08:57:48 +0000 Subject: [PATCH 06/46] actions: add security audit workflow (#10767) Co-authored-by: Sylvestre Ledru --- .github/workflows/audit.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/audit.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 000000000..d30fb0453 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,15 @@ +name: Security audit + +# spell-checker:ignore (misc) rustsec + +on: + schedule: + - cron: "0 0 * * *" +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: rustsec/audit-check@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} From 05204864d68186a0a542b77e9d073bc7e5428cea Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 7 Feb 2026 04:05:44 -0500 Subject: [PATCH 07/46] refactor: inline format! args in a few places (#10730) * refactor: inline format! args in a few places * in one spot remove redundant mem alloc * run clippy fix with mixed --- .clippy.toml | 1 + src/common/validation.rs | 3 +-- src/uu/chmod/src/chmod.rs | 13 +++---------- src/uu/cp/src/cp.rs | 2 +- src/uu/dd/src/dd.rs | 2 +- src/uu/env/src/env.rs | 2 +- src/uu/id/src/id.rs | 4 +--- src/uu/join/benches/join_bench.rs | 2 +- src/uu/mknod/src/mknod.rs | 4 ++-- src/uu/mv/src/hardlink.rs | 9 ++++----- src/uu/od/src/multifile_reader.rs | 2 +- src/uu/od/src/od.rs | 2 +- src/uu/od/src/output_info.rs | 3 +-- src/uu/od/src/parse_inputs.rs | 8 ++++---- src/uu/pr/src/pr.rs | 7 ++----- src/uu/sort/src/ext_sort.rs | 3 +-- src/uu/sort/src/sort.rs | 9 ++++----- src/uu/stat/src/stat.rs | 3 +-- src/uu/stty/src/stty.rs | 2 +- src/uu/tr/src/operation.rs | 4 ++-- src/uu/tsort/benches/tsort_bench.rs | 10 +++------- src/uucore/src/lib/features/format/num_format.rs | 2 +- src/uucore/src/lib/features/perms.rs | 5 ++--- src/uucore/src/lib/features/selinux.rs | 2 +- tests/by-util/test_cp.rs | 3 +-- tests/by-util/test_date.rs | 3 +-- tests/by-util/test_du.rs | 4 ++-- tests/by-util/test_ls.rs | 4 ++-- tests/by-util/test_mkdir.rs | 4 +--- tests/by-util/test_rm.rs | 2 +- tests/test_uudoc.rs | 6 ++---- 31 files changed, 51 insertions(+), 79 deletions(-) diff --git a/.clippy.toml b/.clippy.toml index 113f003ad..f5d31e363 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -2,3 +2,4 @@ avoid-breaking-exported-api = false check-private-items = true cognitive-complexity-threshold = 24 missing-docs-in-crate-items = true +allow-mixed-uninlined-format-args = false diff --git a/src/common/validation.rs b/src/common/validation.rs index 41f686e8d..81ae1351c 100644 --- a/src/common/validation.rs +++ b/src/common/validation.rs @@ -32,8 +32,7 @@ pub fn not_found(util: &OsStr) -> ! { /// Prints an "unrecognized option" error and exits pub fn unrecognized_option(binary_name: &str, option: &OsStr) -> ! { eprintln!( - "{}: unrecognized option '{}'", - binary_name, + "{binary_name}: unrecognized option '{}'", option.to_string_lossy() ); process::exit(1); diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index 78dccffdc..127464dc1 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -318,19 +318,13 @@ impl Chmoder { if new_mode != old_mode { println!( - "mode of {} changed from {:04o} ({}) to {:04o} ({})", + "mode of {} changed from {old_mode:04o} ({current_permissions}) to {new_mode:04o} ({new_permissions})", file_path.quote(), - old_mode, - current_permissions, - new_mode, - new_permissions ); } else if self.verbose { println!( - "mode of {} retained as {:04o} ({})", + "mode of {} retained as {old_mode:04o} ({current_permissions})", file_path.quote(), - old_mode, - current_permissions ); } } @@ -600,9 +594,8 @@ impl Chmoder { if let Err(_e) = dir_fd.chmod_at(entry_name, new_mode, follow_symlinks) { if self.verbose { println!( - "failed to change mode of {} to {:o}", + "failed to change mode of {} to {new_mode:o}", file_path.quote(), - new_mode ); } return Err(ChmodError::PermissionDenied(file_path.into()).into()); diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 8fb5629fa..5c6c0979b 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1344,7 +1344,7 @@ fn show_error_if_needed(error: &CpError) { // Format IoErrContext using strip_errno to remove "(os error N)" suffix // for GNU-compatible output CpError::IoErrContext(io_err, context) => { - show_error!("{}: {}", context, uucore::error::strip_errno(io_err)); + show_error!("{context}: {}", uucore::error::strip_errno(io_err)); } _ => { show_error!("{error}"); diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 48f7ef1b9..bddbe37ac 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -748,7 +748,7 @@ fn handle_o_direct_write(f: &mut File, buf: &[u8], original_error: io::Error) -> // Log any restoration errors without failing the operation if let Err(os_err) = fcntl(&mut *f, FcntlArg::F_SETFL(oflags)) { // Just log the error, don't fail the whole operation - show_error!("Failed to restore O_DIRECT flag: {}", os_err); + show_error!("Failed to restore O_DIRECT flag: {os_err}"); } write_result diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 5e56fbd04..c12cc671b 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -1091,7 +1091,7 @@ fn list_signal_handling(log: &SignalActionLog) { SignalActionKind::Block => "BLOCK", }; let signal_name = signal_name_by_value(sig_value).unwrap_or("?"); - eprintln!("{:<10} ({}): {}", signal_name, sig_value as i32, action); + eprintln!("{signal_name:<10} ({}): {action}", sig_value as i32); } } diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 880768333..5bd27bc3e 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -223,9 +223,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } else { show_error!( "{}", - translate!("id-error-no-such-user", - "user" => users[i].quote() - ) + translate!("id-error-no-such-user", "user" => users[i].quote()) ); set_exit_code(1); if i + 1 >= users.len() { diff --git a/src/uu/join/benches/join_bench.rs b/src/uu/join/benches/join_bench.rs index 800bfa96d..4a18fb51e 100644 --- a/src/uu/join/benches/join_bench.rs +++ b/src/uu/join/benches/join_bench.rs @@ -50,7 +50,7 @@ fn create_partial_overlap_files( // File 2: keys (num_lines - overlap_count) to (2*num_lines - overlap_count - 1) let start = num_lines - overlap_count; for i in 0..num_lines { - writeln!(file2, "{:08} f2_data_{}", start + i, i).unwrap(); + writeln!(file2, "{:08} f2_data_{i}", start + i).unwrap(); } ( diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index 9ab0af057..50aa70755 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -95,7 +95,7 @@ fn mknod(file_name: &str, config: Config) -> i32 { ) { // if it fails, delete the file let _ = std::fs::remove_file(file_name); - eprintln!("{}: {}", uucore::util_name(), e); + eprintln!("{}: {e}", uucore::util_name()); return 1; } } @@ -108,7 +108,7 @@ fn mknod(file_name: &str, config: Config) -> i32 { std::fs::remove_file(p) }) { - eprintln!("{}: {}", uucore::util_name(), e); + eprintln!("{}: {e}", uucore::util_name()); return 1; } } diff --git a/src/uu/mv/src/hardlink.rs b/src/uu/mv/src/hardlink.rs index 8c4ea7b33..61ad74d47 100644 --- a/src/uu/mv/src/hardlink.rs +++ b/src/uu/mv/src/hardlink.rs @@ -65,7 +65,7 @@ impl std::fmt::Display for HardlinkError { ) } Self::Metadata { path, error } => { - write!(f, "Metadata access error for {}: {}", path.quote(), error) + write!(f, "Metadata access error for {}: {error}", path.quote()) } } } @@ -99,9 +99,8 @@ impl From for io::Error { )), HardlinkError::Metadata { path, error } => Self::other(format!( - "Metadata access error for {}: {}", + "Metadata access error for {}: {error}", path.quote(), - error )), } } @@ -127,7 +126,7 @@ impl HardlinkTracker { Err(e) => { // Gracefully handle metadata errors by logging and continuing without hardlink tracking if options.verbose { - eprintln!("warning: cannot get metadata for {}: {}", source.quote(), e); + eprintln!("warning: cannot get metadata for {}: {e}", source.quote()); } return None; } @@ -180,7 +179,7 @@ impl HardlinkGroupScanner { if let Err(e) = self.scan_single_path(file) { if options.verbose { // Only show warnings for verbose mode - eprintln!("warning: failed to scan {}: {}", file.quote(), e); + eprintln!("warning: failed to scan {}: {e}", file.quote()); } // For non-verbose mode, silently continue for missing files // This provides graceful degradation - we'll lose hardlink info for this file diff --git a/src/uu/od/src/multifile_reader.rs b/src/uu/od/src/multifile_reader.rs index 48e1f1225..4213fde90 100644 --- a/src/uu/od/src/multifile_reader.rs +++ b/src/uu/od/src/multifile_reader.rs @@ -93,7 +93,7 @@ impl MultifileReader<'_> { io::ErrorKind::PermissionDenied => "Permission denied", _ => "I/O error", }; - show_error!("{}: {}", fname.maybe_quote().external(true), error_msg); + show_error!("{}: {error_msg}", fname.maybe_quote().external(true)); self.any_err = true; } } diff --git a/src/uu/od/src/od.rs b/src/uu/od/src/od.rs index 4da705aae..67992d769 100644 --- a/src/uu/od/src/od.rs +++ b/src/uu/od/src/od.rs @@ -658,7 +658,7 @@ fn extract_strings_from_input( // Note: GNU od does not output unterminated strings at EOF // Strings must be null-terminated to be output if mf.has_error() { - show_error!("{}", e); + show_error!("{e}"); return Err(1.into()); } break; diff --git a/src/uu/od/src/output_info.rs b/src/uu/od/src/output_info.rs index ef63c1602..86c481b0d 100644 --- a/src/uu/od/src/output_info.rs +++ b/src/uu/od/src/output_info.rs @@ -222,10 +222,9 @@ fn assert_alignment( assert_eq!( expected, &spacing[..byte_size_block], - "unexpected spacing for byte_size={} print_width={} block_width={}", + "unexpected spacing for byte_size={} print_width={} block_width={print_width_block}", type_info.byte_size, type_info.print_width, - print_width_block ); assert!( spacing[byte_size_block..].iter().all(|&s| s == 0), diff --git a/src/uu/od/src/parse_inputs.rs b/src/uu/od/src/parse_inputs.rs index b6f763b2e..3d6263e56 100644 --- a/src/uu/od/src/parse_inputs.rs +++ b/src/uu/od/src/parse_inputs.rs @@ -91,7 +91,7 @@ pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result Result Err(format!("{}: {}", input_strings[1], e)), + (_, Err(e)) => Err(format!("{}: {e}", input_strings[1])), } } 3 => { @@ -148,8 +148,8 @@ pub fn parse_inputs_traditional(input_strings: &[&str]) -> Result Err(format!("{}: {}", input_strings[1], e)), - (_, Err(e)) => Err(format!("{}: {}", input_strings[2], e)), + (Err(e), _) => Err(format!("{}: {e}", input_strings[1])), + (_, Err(e)) => Err(format!("{}: {e}", input_strings[2])), } } _ => Err(translate!("od-error-too-many-inputs", "input" => input_strings[3])), diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index 25a1e2e3e..f000abbd7 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -1264,11 +1264,8 @@ fn header_content(options: &OutputOptions, page: usize) -> Vec { let padding_after_filename = space_for_filename - filename_len - padding_before_filename; format!( - "{date_part}{:width1$}{filename}{:width2$}{page_part}", - "", - "", - width1 = padding_before_filename, - width2 = padding_after_filename + "{date_part}{:padding_before_filename$}{filename}{:padding_after_filename$}{page_part}", + "", "" ) } else { // If content is too long, just use single spaces diff --git a/src/uu/sort/src/ext_sort.rs b/src/uu/sort/src/ext_sort.rs index d61f7d200..6e4e98a58 100644 --- a/src/uu/sort/src/ext_sort.rs +++ b/src/uu/sort/src/ext_sort.rs @@ -70,8 +70,7 @@ pub fn ext_sort( Err(err) => { // Print the error and disable compression eprintln!( - "sort: could not run compress program '{}': {}", - prog, + "sort: could not run compress program '{prog}': {}", strip_errno(&err) ); effective_settings.compress_prog = None; diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index ceaf51256..26b060a45 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1467,7 +1467,7 @@ struct LegacyKeyWarning { impl LegacyKeyWarning { fn legacy_key_display(&self) -> String { match self.to_field { - Some(to) => format!("+{} -{}", self.from_field, to), + Some(to) => format!("+{} -{to}", self.from_field), None => format!("+{}", self.from_field), } } @@ -1569,14 +1569,13 @@ fn legacy_key_to_k(from: &LegacyKeyPart, to: Option<&LegacyKeyPart>) -> String { let start_char = from.char_pos.saturating_add(1); let mut keydef = format!( - "{}{}{}", - start_field, + "{start_field}{}{}", if from.char_pos == 0 { String::new() } else { format!(".{start_char}") }, - from.opts + from.opts, ); if let Some(to) = to { @@ -2150,7 +2149,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { #[cfg(target_os = "linux")] { - show_error!("{}", batch_too_large); + show_error!("{batch_too_large}"); translate!( "sort-maximum-batch-size-rlimit", diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 8e044ed77..65b6e0540 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -1303,13 +1303,12 @@ impl Stater { }; format!( - " {}: %N\n {}: %-10s\t{}: %-10b {} {}: %-6o %F\n{}{}: (%04a/%10.10A) {}: (%5u/%8U) {}: (%5g/%8G)\n{}: %x\n{}: %y\n{}: %z\n {}: %w\n", + " {}: %N\n {}: %-10s\t{}: %-10b {} {}: %-6o %F\n{device_line}{}: (%04a/%10.10A) {}: (%5u/%8U) {}: (%5g/%8G)\n{}: %x\n{}: %y\n{}: %z\n {}: %w\n", translate!("stat-word-file"), translate!("stat-word-size"), translate!("stat-word-blocks"), translate!("stat-word-io"), translate!("stat-word-block"), - device_line, translate!("stat-word-access"), translate!("stat-word-uid"), translate!("stat-word-gid"), diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index af54db3e9..e6336aab1 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -595,7 +595,7 @@ impl WrappedPrinter { self.first_in_line = true; } - print!("{}{}", self.prefix(), token); + print!("{}{token}", self.prefix()); self.current += token_len; self.first_in_line = false; } diff --git a/src/uu/tr/src/operation.rs b/src/uu/tr/src/operation.rs index 0c62840e0..e2ba2bd83 100644 --- a/src/uu/tr/src/operation.rs +++ b/src/uu/tr/src/operation.rs @@ -547,8 +547,8 @@ impl Sequence { (Ok(c), Ok(())) => Ok(Self::Char(c)), (Ok(c), Err(v)) => Err(BadSequence::MultipleCharInEquivalence(format!( "{}{}", - String::from_utf8_lossy(&[c]).into_owned(), - String::from_utf8_lossy(v).into_owned() + String::from_utf8_lossy(&[c]), + String::from_utf8_lossy(v), ))), }, ) diff --git a/src/uu/tsort/benches/tsort_bench.rs b/src/uu/tsort/benches/tsort_bench.rs index 18d121d66..28395cffd 100644 --- a/src/uu/tsort/benches/tsort_bench.rs +++ b/src/uu/tsort/benches/tsort_bench.rs @@ -12,7 +12,7 @@ fn generate_linear_chain(num_nodes: usize) -> Vec { let mut data = Vec::new(); for i in 0..num_nodes.saturating_sub(1) { - data.extend_from_slice(format!("node{} node{}\n", i, i + 1).as_bytes()); + data.extend_from_slice(format!("node{i} node{}\n", i + 1).as_bytes()); } data @@ -85,10 +85,8 @@ fn generate_wide_dag(num_nodes: usize) -> Vec { for i in chain_start..chain_end.saturating_sub(1) { data.extend_from_slice( format!( - "chain{}_{} chain{}_{}\n", - chain, + "chain{chain}_{} chain{chain}_{}\n", i - chain_start, - chain, i + 1 - chain_start ) .as_bytes(), @@ -102,10 +100,8 @@ fn generate_wide_dag(num_nodes: usize) -> Vec { let curr_mid = chain_start + chain_length / 4; data.extend_from_slice( format!( - "chain{}_{} chain{}_{}\n", - prev_chain, + "chain{prev_chain}_{} chain{chain}_{}\n", prev_end - prev_chain * chain_length, - chain, curr_mid - chain_start ) .as_bytes(), diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index 9a09b3f7d..9d44491b4 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -425,7 +425,7 @@ fn format_float_scientific( return if force_decimal == ForceDecimal::Yes && precision == 0 { format!("0.{exp_char}+00") } else { - format!("{:.*}{exp_char}+00", precision, 0.0) + format!("{:.precision$}{exp_char}+00", 0.0) }; } diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index e9aaf875d..467406b45 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -388,14 +388,13 @@ impl ChownExecutor { // Use fchown (safe) to change the directory's ownership if let Err(e) = dir_fd.fchown(self.dest_uid, self.dest_gid) { let mut error_msg = format!( - "changing {} of {}: {}", + "changing {} of {}: {e}", if self.verbosity.groups_only { "group" } else { "ownership" }, path.quote(), - e ); if self.verbosity.level == VerbosityLevel::Verbose { @@ -521,7 +520,7 @@ impl ChownExecutor { entry_path.quote(), strip_errno(&e) ); - show_error!("{}", msg); + show_error!("{msg}"); } } else { // Report the successful ownership change using the shared helper diff --git a/src/uucore/src/lib/features/selinux.rs b/src/uucore/src/lib/features/selinux.rs index ed79be414..da0297d00 100644 --- a/src/uucore/src/lib/features/selinux.rs +++ b/src/uucore/src/lib/features/selinux.rs @@ -365,7 +365,7 @@ pub fn preserve_security_context(from_path: &Path, to_path: &Path) -> Result<(), /// use uucore::selinux::get_getfattr_output; /// /// let context = get_getfattr_output("/path/to/file"); -/// println!("SELinux context: {}", context); +/// println!("SELinux context: {context}"); /// ``` pub fn get_getfattr_output(f: &str) -> String { use std::process::Command; diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index f01619af2..1508b8fc0 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -6500,8 +6500,7 @@ fn test_cp_archive_preserves_directory_permissions() { assert_eq!( mode & 0o777, 0o755, - "Directory {} has incorrect permissions: {:o}", - path, + "Directory {path} has incorrect permissions: {:o}", mode & 0o777 ); }; diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index 923922f2b..7a5625a01 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -419,8 +419,7 @@ fn test_date_set_hyphen_prefixed_values() { // permission error, not argument parsing error assert!( result.stderr_str().starts_with("date: cannot set date: "), - "Expected permission error for '{}', but got: {}", - date_str, + "Expected permission error for '{date_str}', but got: {}", result.stderr_str() ); } diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index 2be6ef02c..e73655c44 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -1782,7 +1782,7 @@ fn test_du_long_path_safe_traversal() { at.mkdir(&deep_path); for i in 0..15 { - let long_dir_name = format!("{}{}", "a".repeat(100), i); + let long_dir_name = format!("{}{i}", "a".repeat(100)); deep_path = format!("{deep_path}/{long_dir_name}"); at.mkdir_all(&deep_path); } @@ -1830,7 +1830,7 @@ fn test_du_safe_traversal_with_symlinks() { at.mkdir(&deep_path); for i in 0..8 { - let dir_name = format!("{}{}", "b".repeat(50), i); + let dir_name = format!("{}{i}", "b".repeat(50)); deep_path = format!("{deep_path}/{dir_name}"); at.mkdir_all(&deep_path); } diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 41b72af6b..56215eac4 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -4785,8 +4785,8 @@ fn test_ls_selinux_context_indicator() { // The 11th character (0-indexed position 10) should be "." for SELinux context assert_eq!( chars[10], '.', - "Expected '.' indicator for SELinux context in position 11, got '{}' in line: {}", - chars[10], first_line + "Expected '.' indicator for SELinux context in position 11, got '{}' in line: {first_line}", + chars[10], ); } diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index 0756cb5d6..231fe3495 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -468,9 +468,7 @@ fn test_selinux() { let context_value = get_getfattr_output(&at.plus_as_string(dest)); assert!( context_value.contains("unconfined_u"), - "Expected '{}' not found in getfattr output:\n{}", - "unconfined_u", - context_value + "Expected 'unconfined_u' not found in getfattr output:\n{context_value}", ); at.rmdir(dest); } diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index c303a4bab..3d2f1df5e 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -1103,7 +1103,7 @@ fn test_rm_recursive_long_path_safe_traversal() { at.mkdir(&deep_path); for i in 0..12 { - let long_dir_name = format!("{}{}", "z".repeat(80), i); + let long_dir_name = format!("{}{i}", "z".repeat(80)); deep_path = format!("{deep_path}/{long_dir_name}"); at.mkdir_all(&deep_path); } diff --git a/tests/test_uudoc.rs b/tests/test_uudoc.rs index b4f12c099..a1e32f484 100644 --- a/tests/test_uudoc.rs +++ b/tests/test_uudoc.rs @@ -62,10 +62,8 @@ fn get_doc_file_from_output(output: &str) -> (String, String) { Ok(content) => content, Err(e) => { panic!( - "Failed to read file {}: {} from {:?}", - correct_path_test, - e, - env::current_dir() + "Failed to read file {correct_path_test}: {e} from {:?}", + env::current_dir(), ); } }; From 0bec48f48b8e0e632aadeaddf35417cb01abf111 Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:06:16 +0900 Subject: [PATCH 08/46] Publish *.wasm (#10656) Co-authored-by: Sylvestre Ledru --- .github/workflows/CICD.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index d699a104a..31dcb14f6 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -5,7 +5,7 @@ name: CICD # 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 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 +# 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 wasip env: PROJECT_NAME: coreutils @@ -631,7 +631,7 @@ jobs: - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: "feat_os_unix,uudoc" , use-cross: no, workspace-tests: true } - { os: ubuntu-latest , target: x86_64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross } - { os: ubuntu-latest , target: x86_64-unknown-redox , features: feat_os_unix_redox , use-cross: redoxer , skip-tests: true } - - { os: ubuntu-latest , target: wasm32-unknown-unknown , default-features: false, features: uucore/format, skip-tests: true, skip-package: true, skip-publish: true } + - { os: ubuntu-latest , target: wasm32-wasip1, default-features: false, features: "basenc,cksum", skip-tests: true } - { os: macos-latest , target: aarch64-apple-darwin , features: feat_os_macos, workspace-tests: true } # M1 CPU # PR #7964: Mac should still build even if the feature is not enabled. Do not publish this. - { os: macos-latest , target: aarch64-apple-darwin , workspace-tests: true, skip-publish: true } # M1 CPU @@ -672,7 +672,11 @@ jobs: STAGING='_staging' outputs STAGING # determine EXE suffix - EXE_suffix="" ; case '${{ matrix.job.target }}' in *-pc-windows-*) EXE_suffix=".exe" ;; esac; + unset EXE_suffix + case '${{ matrix.job.target }}' in + *-pc-windows-*) EXE_suffix=".exe" ;; + *wasm-*) EXE_suffix=".wasm" ;; + esac; outputs EXE_suffix # parse commit reference info echo GITHUB_REF=${GITHUB_REF} @@ -868,7 +872,7 @@ jobs: run: | ## Package artifact(s) # binaries - cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' + cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}'* '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' cp 'target/${{ matrix.job.target }}/release/uudoc${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' || : # README and LICENSE # * spell-checker:ignore EADME ICENSE From 2d5c28f3445124766bf03f7ad6a3346b64610e66 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 7 Feb 2026 14:07:19 +0100 Subject: [PATCH 09/46] stdbuf: fix warning from uninlined_format_args (#10784) --- tests/by-util/test_stdbuf.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/by-util/test_stdbuf.rs b/tests/by-util/test_stdbuf.rs index 2f234f183..0b60ef464 100644 --- a/tests/by-util/test_stdbuf.rs +++ b/tests/by-util/test_stdbuf.rs @@ -105,9 +105,8 @@ fn test_stdbuf_search_order_exe_dir_first() { assert!( loaded_from_exe_dir, - "libstdbuf should be loaded from exe directory ({}), not from LIBSTDBUF_DIR. LD_DEBUG output:\n{}", - temp_path.display(), - stderr + "libstdbuf should be loaded from exe directory ({}), not from LIBSTDBUF_DIR. LD_DEBUG output:\n{stderr}", + temp_path.display() ); // The command should succeed and produce the expected output From 31c5102bee2a6f6be39ab7725196d2c78fe3ec39 Mon Sep 17 00:00:00 2001 From: Nikola Lukovic Date: Sat, 7 Feb 2026 14:28:24 +0100 Subject: [PATCH 10/46] cp: fix recursive copy of readonly directories (#10529) Co-authored-by: Sylvestre Ledru --- src/uu/cp/src/copydir.rs | 26 ++++++---- src/uu/cp/src/cp.rs | 20 ++++++-- tests/by-util/test_cp.rs | 101 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 15 deletions(-) diff --git a/src/uu/cp/src/copydir.rs b/src/uu/cp/src/copydir.rs index 42e30b452..614bd597d 100644 --- a/src/uu/cp/src/copydir.rs +++ b/src/uu/cp/src/copydir.rs @@ -237,6 +237,7 @@ impl Entry { #[allow(clippy::too_many_arguments)] /// Copy a single entry during a directory traversal. +/// Returns a bool value indicating whether this function created a directory or not fn copy_direntry( progress_bar: Option<&ProgressBar>, entry: &Entry, @@ -248,7 +249,7 @@ fn copy_direntry( copied_destinations: &HashSet, copied_files: &mut HashMap, created_parent_dirs: &mut HashSet, -) -> CopyResult<()> { +) -> CopyResult { let source_is_symlink = entry_is_symlink; let source_is_dir = if source_is_symlink && !options.dereference { false @@ -276,7 +277,7 @@ fn copy_direntry( context_for(&entry.source_relative, &entry.local_to_target) ); } - Ok(()) + Ok(true) }; } @@ -326,7 +327,7 @@ fn copy_direntry( // In any other case, there is nothing to do, so we just return to // continue the traversal. - Ok(()) + Ok(false) } /// Read the contents of the directory `root` and recursively copy the @@ -421,7 +422,7 @@ pub(crate) fn copy_directory( let mut last_iter: Option = None; // Keep track of all directories we've created that need permission fixes - let mut dirs_needing_permissions: Vec<(PathBuf, PathBuf)> = Vec::new(); + let mut dirs_needing_permissions: Vec<(PathBuf, PathBuf, bool)> = Vec::new(); // Traverse the contents of the directory, copying each one. for direntry_result in WalkDir::new(root) @@ -442,7 +443,7 @@ pub(crate) fn copy_directory( }; let entry = Entry::new(&context, direntry_path, options.no_target_dir)?; - copy_direntry( + let created = copy_direntry( progress_bar, &entry, entry_is_symlink, @@ -475,12 +476,16 @@ pub(crate) fn copy_directory( &entry.source_absolute, &entry.local_to_target, &options.attributes, + false, )?; continue; } // Add this directory to our list for permission fixing later - dirs_needing_permissions - .push((entry.source_absolute.clone(), entry.local_to_target.clone())); + dirs_needing_permissions.push(( + entry.source_absolute.clone(), + entry.local_to_target.clone(), + created, + )); // If true, last_iter is not a parent of this iter. // The means we just exited a directory. @@ -513,6 +518,7 @@ pub(crate) fn copy_directory( &entry.source_absolute, &entry.local_to_target, &options.attributes, + false, )?; } } @@ -528,8 +534,8 @@ pub(crate) fn copy_directory( // Fix permissions for all directories we created // This ensures that even sibling directories get their permissions fixed - for (source_path, dest_path) in dirs_needing_permissions { - copy_attributes(&source_path, &dest_path, &options.attributes)?; + for (source_path, dest_path, created) in dirs_needing_permissions { + copy_attributes(&source_path, &dest_path, &options.attributes, created)?; } // Also fix permissions for parent directories, @@ -538,7 +544,7 @@ pub(crate) fn copy_directory( let dest = target.join(root.file_name().unwrap()); for (x, y) in aligned_ancestors(root, dest.as_path()) { if let Ok(src) = canonicalize(x, MissingHandling::Normal, ResolveMode::Physical) { - copy_attributes(&src, y, &options.attributes)?; + copy_attributes(&src, y, &options.attributes, false)?; } } } diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 5c6c0979b..c57d962d0 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1550,7 +1550,7 @@ fn copy_source( if options.parents { for (x, y) in aligned_ancestors(source, dest.as_path()) { if let Ok(src) = canonicalize(x, MissingHandling::Normal, ResolveMode::Physical) { - copy_attributes(&src, y, &options.attributes)?; + copy_attributes(&src, y, &options.attributes, false)?; } } } @@ -1717,11 +1717,21 @@ pub(crate) fn copy_attributes( source: &Path, dest: &Path, attributes: &Attributes, + created: bool, ) -> CopyResult<()> { let context = &*format!("{} -> {}", source.quote(), dest.quote()); let source_metadata = fs::symlink_metadata(source).map_err(|e| CpError::IoErrContext(e, context.to_owned()))?; + let is_explicit = matches!(attributes.mode, Preserve::No { explicit: true }); + + // preserve is true by default if the destination is created by us and it's a directory + let mode = if !is_explicit && dest.is_dir() && created { + Preserve::Yes { required: false } + } else { + attributes.mode + }; + // Ownership must be changed first to avoid interfering with mode change. #[cfg(unix)] handle_preserve(attributes.ownership, || -> CopyResult<()> { @@ -1759,7 +1769,7 @@ pub(crate) fn copy_attributes( Ok(()) })?; - handle_preserve(attributes.mode, || -> CopyResult<()> { + handle_preserve(mode, || -> CopyResult<()> { // The `chmod()` system call that underlies the // `fs::set_permissions()` call is unable to change the // permissions of a symbolic link. In that case, we just @@ -2573,14 +2583,14 @@ fn copy_file( .ok() .filter(|p| p.exists()) .unwrap_or_else(|| source.to_path_buf()); - copy_attributes(&src_for_attrs, dest, &options.attributes)?; + copy_attributes(&src_for_attrs, dest, &options.attributes, false)?; } else if source_is_stream && !source.exists() { // Some stream files may not exist after we have copied it, // like anonymous pipes. Thus, we can't really copy its // attributes. However, this is already handled in the stream // copy function (see `copy_stream` under platform/linux.rs). } else { - copy_attributes(source, dest, &options.attributes)?; + copy_attributes(source, dest, &options.attributes, false)?; } #[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))] @@ -2762,7 +2772,7 @@ fn copy_link( delete_path(dest, options)?; } symlink_file(&link, dest, symlinked_files)?; - copy_attributes(source, dest, &options.attributes) + copy_attributes(source, dest, &options.attributes, false) } /// Generate an error message if `target` is not the correct `target_type` diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 1508b8fc0..b49f4606c 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -7570,3 +7570,104 @@ fn test_cp_xattr_enotsup_handling() { std::fs::remove_file(f).ok(); } } + +#[test] +#[cfg(not(target_os = "windows"))] +fn test_cp_preserve_directory_permissions_by_default() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let dir = "a/b/c/d"; + let file = "foo.txt"; + + at.mkdir_all(dir); + + let file_path = format!("{dir}/{file}"); + + at.touch(file_path); + + scene.cmd("chmod").arg("-R").arg("555").arg("a").succeeds(); + scene.cmd("cp").arg("-r").arg("a").arg("b").succeeds(); + + scene.ucmd().arg("-r").arg("a").arg("c").succeeds(); + + // only verify owner bits on Android + if cfg!(target_os = "android") { + assert_eq!(at.metadata("b").mode() & 0o700, 0o500); + assert_eq!(at.metadata("b/b").mode() & 0o700, 0o500); + assert_eq!(at.metadata("b/b/c").mode() & 0o700, 0o500); + assert_eq!(at.metadata("b/b/c/d").mode() & 0o700, 0o500); + + assert_eq!(at.metadata("c").mode() & 0o700, 0o500); + assert_eq!(at.metadata("c/b").mode() & 0o700, 0o500); + assert_eq!(at.metadata("c/b/c").mode() & 0o700, 0o500); + assert_eq!(at.metadata("c/b/c/d").mode() & 0o700, 0o500); + } else { + assert_eq!(at.metadata("b").mode(), 0o40555); + assert_eq!(at.metadata("b/b").mode(), 0o40555); + assert_eq!(at.metadata("b/b/c").mode(), 0o40555); + assert_eq!(at.metadata("b/b/c/d").mode(), 0o40555); + + assert_eq!(at.metadata("c").mode(), 0o40555); + assert_eq!(at.metadata("c/b").mode(), 0o40555); + assert_eq!(at.metadata("c/b/c").mode(), 0o40555); + assert_eq!(at.metadata("c/b/c/d").mode(), 0o40555); + } +} + +#[test] +#[cfg(not(target_os = "windows"))] +fn test_cp_existing_perm_dir() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + scene + .cmd("mkdir") + .arg("-p") + .arg("-m") + .arg("ug-s,u=rwx,g=rwx,o=rx") + .arg("src/dir") + .umask(0o022) + .succeeds(); + scene + .cmd("mkdir") + .arg("-p") + .arg("-m") + .arg("ug-s,u=rwx,g=,o=") + .arg("dst/dir") + .umask(0o022) + .succeeds(); + + scene.ucmd().arg("-r").arg("src/.").arg("dst/").succeeds(); + + let mode = at.metadata("dst/dir").mode(); + + assert_eq!(mode, 0o40700); +} + +#[test] +#[cfg(not(target_os = "windows"))] +fn test_cp_gnu_preserve_mode() { + use std::io; + + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + scene.cmd("mkdir").arg("d1").succeeds(); + scene.cmd("mkdir").arg("d2").succeeds(); + scene.cmd("chmod").arg("705").arg("d2").succeeds(); + + scene + .ucmd() + .arg("--no-preserve=mode") + .arg("-r") + .arg("d2") + .arg("d3") + .set_stdout(io::stdout()) + .succeeds(); + + let d1_mode = at.metadata("d1").mode(); + let d3_mode = at.metadata("d3").mode(); + + assert_eq!(d1_mode, d3_mode); +} From c73b3ab2e6abb5dcc1ebe63ea24ca5cd88deb1d6 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sat, 7 Feb 2026 13:42:18 +0000 Subject: [PATCH 11/46] clippy: fix manual_is_multiple_of lint (#10775) * clippy: fix manual_is_multiple_of lint https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_multiple_of * fmt * taplo --- .cargo/config.toml | 7 +------ Cargo.toml | 3 +-- src/uu/df/src/blocks.rs | 2 +- src/uu/nl/src/nl.rs | 6 ++++-- src/uu/wc/src/count_fast.rs | 2 +- src/uucore/src/lib/features/checksum/validate.rs | 6 +++--- src/uucore/src/lib/features/encoding.rs | 4 ++-- 7 files changed, 13 insertions(+), 17 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 32631f684..f3a0a563e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -15,9 +15,4 @@ LIBSTDBUF_DIR = "/usr/local/libexec/coreutils" # remove me [build] -rustflags = [ - "-A", - "clippy::collapsible_if", - "-A", - "clippy::manual_is_multiple_of", -] +rustflags = ["-A", "clippy::collapsible_if"] diff --git a/Cargo.toml b/Cargo.toml index 332c2a378..088097fa1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -642,8 +642,7 @@ unexpected_cfgs = { level = "warn", check-cfg = [ unused_qualifications = "warn" [workspace.lints.clippy] -manual_is_multiple_of = { level = "allow", priority = 127 } # remove me -collapsible_if = { level = "allow", priority = 127 } # remove me +collapsible_if = { level = "allow", priority = 127 } # remove me # The counts were generated with this command: # cargo clippy --all-targets --workspace --message-format=json --quiet \ # | jq -r '.message.code.code | select(. != null and startswith("clippy::"))' \ diff --git a/src/uu/df/src/blocks.rs b/src/uu/df/src/blocks.rs index 328090ffd..24ebbf442 100644 --- a/src/uu/df/src/blocks.rs +++ b/src/uu/df/src/blocks.rs @@ -107,7 +107,7 @@ pub(crate) fn to_magnitude_and_suffix( if quot >= 100 && rem > 0 { format!("{}{suffix}", quot + 1) - } else if rem % (bases[i] / 10) == 0 { + } else if rem.is_multiple_of(bases[i] / 10) { format!("{quot}.{tenths_place}{suffix}") } else if tenths_place + 1 == 10 || quot >= 10 { let quot = quot + 1; diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index 5288aaefb..616f8d410 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -172,7 +172,7 @@ impl SectionDelimiter { fn parse(bytes: &[u8], pattern: &OsStr) -> Option { let pattern = pattern.as_encoded_bytes(); - if bytes.is_empty() || pattern.is_empty() || bytes.len() % pattern.len() != 0 { + if bytes.is_empty() || pattern.is_empty() || !bytes.len().is_multiple_of(pattern.len()) { return None; } @@ -426,7 +426,9 @@ fn nl(reader: &mut BufReader, stats: &mut Stats, settings: &Settings NumberingStyle::All if line.is_empty() && settings.join_blank_lines > 0 - && stats.consecutive_empty_lines % settings.join_blank_lines != 0 => + && !stats + .consecutive_empty_lines + .is_multiple_of(settings.join_blank_lines) => { false } diff --git a/src/uu/wc/src/count_fast.rs b/src/uu/wc/src/count_fast.rs index d20c53d4f..5e2ecd080 100644 --- a/src/uu/wc/src/count_fast.rs +++ b/src/uu/wc/src/count_fast.rs @@ -136,7 +136,7 @@ pub(crate) fn count_bytes_fast(handle: &mut T) -> (usize, Opti && stat.st_size > 0 { let sys_page_size = unsafe { sysconf(_SC_PAGESIZE) as usize }; - if stat.st_size as usize % sys_page_size > 0 { + if !(stat.st_size as usize).is_multiple_of(sys_page_size) { // regular file or file from /proc, /sys and similar pseudo-filesystems // with size that is NOT a multiple of system page size return (stat.st_size as usize, None); diff --git a/src/uucore/src/lib/features/checksum/validate.rs b/src/uucore/src/lib/features/checksum/validate.rs index 7fede1c49..64dc47631 100644 --- a/src/uucore/src/lib/features/checksum/validate.rs +++ b/src/uucore/src/lib/features/checksum/validate.rs @@ -417,7 +417,7 @@ impl LineFormat { // checksums that are fully alphanumeric. Another check happens later // when we are provided with a length hint to detect ambiguous // base64-encoded checksums. - if is_base64 && checksum.len() % 4 != 0 { + if is_base64 && !checksum.len().is_multiple_of(4) { return None; } @@ -493,7 +493,7 @@ fn get_raw_expected_digest(checksum: &str, byte_len_hint: Option) -> Opti // If the length of the digest is not a multiple of 2, then it must be // improperly formatted (1 byte is 2 hex digits, and base64 strings should // always be a multiple of 4). - if checksum.len() % 2 != 0 { + if !checksum.len().is_multiple_of(2) { return None; } @@ -516,7 +516,7 @@ fn get_raw_expected_digest(checksum: &str, byte_len_hint: Option) -> Opti // It is important to check it before trying to decode, because the // forgiving mode of decoding will ignore if padding characters '=' are // MISSING, but to match GNU's behavior, we must reject it. - if checksum.len() % 4 != 0 { + if !checksum.len().is_multiple_of(4) { return None; } diff --git a/src/uucore/src/lib/features/encoding.rs b/src/uucore/src/lib/features/encoding.rs index 2f7caae2b..635f595e8 100644 --- a/src/uucore/src/lib/features/encoding.rs +++ b/src/uucore/src/lib/features/encoding.rs @@ -100,7 +100,7 @@ impl SupportsFastDecodeAndEncode for Base64SimdWrapper { // If there are no more '=' bytes the tail might still be padded // (len % 4 == 0) or purposely unpadded (GNU --ignore-garbage or // concatenated streams), so select the matching alphabet. - let decoder = if remaining.len() % 4 == 0 { + let decoder = if remaining.len().is_multiple_of(4) { Self::decode_with_standard } else { Self::decode_with_no_pad @@ -448,7 +448,7 @@ impl SupportsFastDecodeAndEncode for Z85Wrapper { fn encode_to_vec_deque(&self, input: &[u8], output: &mut VecDeque) -> UResult<()> { // According to the spec we should not accept inputs whose len is not a multiple of 4. // However, the z85 crate implements a padded encoding and accepts such inputs. We have to manually check for them. - if input.len() % 4 != 0 { + if !input.len().is_multiple_of(4) { return Err(USimpleError::new( 1, "error: invalid input (length must be multiple of 4 characters)".to_owned(), From 5fa809ffed9bedbb894fe21220e0808e44fcdb5d Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 7 Feb 2026 14:54:07 +0100 Subject: [PATCH 12/46] deny.toml: remove signal-hook from skip list (#10785) --- deny.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/deny.toml b/deny.toml index 242227509..abc0acbd2 100644 --- a/deny.toml +++ b/deny.toml @@ -104,8 +104,6 @@ skip = [ { name = "zerocopy", version = "0.7.35" }, # zerocopy { name = "zerocopy-derive", version = "0.7.35" }, - # crossterm - { name = "signal-hook", version = "0.3.18" }, ] # spell-checker: enable From 3d56517f7ffc6b99341d9627433c8c357ae35b05 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Fri, 6 Feb 2026 22:58:51 +0000 Subject: [PATCH 13/46] Add rust-version field to all Cargo.toml files --- Cargo.toml | 5 +++-- fuzz/Cargo.toml | 2 ++ fuzz/uufuzz/Cargo.toml | 1 + src/uu/arch/Cargo.toml | 1 + src/uu/b2sum/Cargo.toml | 1 + src/uu/base32/Cargo.toml | 1 + src/uu/base64/Cargo.toml | 1 + src/uu/basename/Cargo.toml | 1 + src/uu/basenc/Cargo.toml | 1 + src/uu/cat/Cargo.toml | 1 + src/uu/chcon/Cargo.toml | 1 + src/uu/checksum_common/Cargo.toml | 1 + src/uu/chgrp/Cargo.toml | 1 + src/uu/chmod/Cargo.toml | 1 + src/uu/chown/Cargo.toml | 1 + src/uu/chroot/Cargo.toml | 1 + src/uu/cksum/Cargo.toml | 1 + src/uu/comm/Cargo.toml | 1 + src/uu/cp/Cargo.toml | 1 + src/uu/csplit/Cargo.toml | 1 + src/uu/cut/Cargo.toml | 1 + src/uu/date/Cargo.toml | 1 + src/uu/dd/Cargo.toml | 1 + src/uu/df/Cargo.toml | 1 + src/uu/dir/Cargo.toml | 1 + src/uu/dircolors/Cargo.toml | 1 + src/uu/dirname/Cargo.toml | 1 + src/uu/du/Cargo.toml | 1 + src/uu/echo/Cargo.toml | 1 + src/uu/env/Cargo.toml | 1 + src/uu/expand/Cargo.toml | 1 + src/uu/expr/Cargo.toml | 1 + src/uu/factor/Cargo.toml | 1 + src/uu/false/Cargo.toml | 1 + src/uu/fmt/Cargo.toml | 1 + src/uu/fold/Cargo.toml | 1 + src/uu/groups/Cargo.toml | 1 + src/uu/head/Cargo.toml | 1 + src/uu/hostid/Cargo.toml | 1 + src/uu/hostname/Cargo.toml | 1 + src/uu/id/Cargo.toml | 1 + src/uu/install/Cargo.toml | 1 + src/uu/join/Cargo.toml | 1 + src/uu/kill/Cargo.toml | 1 + src/uu/link/Cargo.toml | 1 + src/uu/ln/Cargo.toml | 1 + src/uu/logname/Cargo.toml | 1 + src/uu/ls/Cargo.toml | 1 + src/uu/md5sum/Cargo.toml | 1 + src/uu/mkdir/Cargo.toml | 1 + src/uu/mkfifo/Cargo.toml | 1 + src/uu/mknod/Cargo.toml | 1 + src/uu/mktemp/Cargo.toml | 1 + src/uu/more/Cargo.toml | 1 + src/uu/mv/Cargo.toml | 1 + src/uu/nice/Cargo.toml | 1 + src/uu/nl/Cargo.toml | 1 + src/uu/nohup/Cargo.toml | 1 + src/uu/nproc/Cargo.toml | 1 + src/uu/numfmt/Cargo.toml | 1 + src/uu/od/Cargo.toml | 1 + src/uu/paste/Cargo.toml | 1 + src/uu/pathchk/Cargo.toml | 1 + src/uu/pinky/Cargo.toml | 1 + src/uu/pr/Cargo.toml | 1 + src/uu/printenv/Cargo.toml | 1 + src/uu/printf/Cargo.toml | 1 + src/uu/ptx/Cargo.toml | 1 + src/uu/pwd/Cargo.toml | 1 + src/uu/readlink/Cargo.toml | 1 + src/uu/realpath/Cargo.toml | 1 + src/uu/rm/Cargo.toml | 1 + src/uu/rmdir/Cargo.toml | 1 + src/uu/runcon/Cargo.toml | 1 + src/uu/seq/Cargo.toml | 1 + src/uu/sha1sum/Cargo.toml | 1 + src/uu/sha224sum/Cargo.toml | 1 + src/uu/sha256sum/Cargo.toml | 1 + src/uu/sha384sum/Cargo.toml | 1 + src/uu/sha512sum/Cargo.toml | 1 + src/uu/shred/Cargo.toml | 1 + src/uu/shuf/Cargo.toml | 1 + src/uu/sleep/Cargo.toml | 1 + src/uu/sort/Cargo.toml | 1 + src/uu/split/Cargo.toml | 1 + src/uu/stat/Cargo.toml | 1 + src/uu/stdbuf/Cargo.toml | 1 + src/uu/stdbuf/src/libstdbuf/Cargo.toml | 1 + src/uu/stty/Cargo.toml | 1 + src/uu/sum/Cargo.toml | 1 + src/uu/sync/Cargo.toml | 1 + src/uu/tac/Cargo.toml | 1 + src/uu/tail/Cargo.toml | 1 + src/uu/tee/Cargo.toml | 1 + src/uu/test/Cargo.toml | 1 + src/uu/timeout/Cargo.toml | 1 + src/uu/touch/Cargo.toml | 1 + src/uu/tr/Cargo.toml | 1 + src/uu/true/Cargo.toml | 1 + src/uu/truncate/Cargo.toml | 1 + src/uu/tsort/Cargo.toml | 1 + src/uu/tty/Cargo.toml | 1 + src/uu/uname/Cargo.toml | 1 + src/uu/unexpand/Cargo.toml | 1 + src/uu/uniq/Cargo.toml | 1 + src/uu/unlink/Cargo.toml | 1 + src/uu/uptime/Cargo.toml | 1 + src/uu/users/Cargo.toml | 1 + src/uu/vdir/Cargo.toml | 1 + src/uu/wc/Cargo.toml | 1 + src/uu/who/Cargo.toml | 1 + src/uu/whoami/Cargo.toml | 1 + src/uu/yes/Cargo.toml | 1 + src/uucore/Cargo.toml | 1 + src/uucore_procs/Cargo.toml | 1 + tests/uutests/Cargo.toml | 1 + 116 files changed, 119 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 088097fa1..bf6a17121 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,14 +8,14 @@ name = "coreutils" description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust" default-run = "coreutils" repository = "https://github.com/uutils/coreutils" -rust-version = "1.88.0" +edition.workspace = true +rust-version.workspace = true version.workspace = true authors.workspace = true license.workspace = true homepage.workspace = true keywords.workspace = true categories.workspace = true -edition.workspace = true [package.metadata.docs.rs] all-features = true @@ -303,6 +303,7 @@ members = [ authors = ["uutils developers"] categories = ["command-line-utilities"] edition = "2024" +rust-version = "1.88.0" homepage = "https://github.com/uutils/coreutils" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] license = "MIT" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 6d5e2d4d6..81edb902c 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.0" description = "uutils ~ 'core' uutils fuzzers" repository = "https://github.com/uutils/coreutils/tree/main/fuzz/" edition.workspace = true +rust-version.workspace = true license.workspace = true publish = false @@ -13,6 +14,7 @@ members = ["."] [workspace.package] edition = "2024" +rust-version = "1.88.0" license = "MIT" [package.metadata] diff --git a/fuzz/uufuzz/Cargo.toml b/fuzz/uufuzz/Cargo.toml index 5e66b0b49..3c2240998 100644 --- a/fuzz/uufuzz/Cargo.toml +++ b/fuzz/uufuzz/Cargo.toml @@ -5,6 +5,7 @@ description = "uutils ~ 'core' uutils fuzzing library" repository = "https://github.com/uutils/coreutils/tree/main/fuzz/uufuzz" version = "0.6.0" edition.workspace = true +rust-version.workspace = true license.workspace = true [dependencies] diff --git a/src/uu/arch/Cargo.toml b/src/uu/arch/Cargo.toml index 39a2410ba..b906a3929 100644 --- a/src/uu/arch/Cargo.toml +++ b/src/uu/arch/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/b2sum/Cargo.toml b/src/uu/b2sum/Cargo.toml index 81f6f51fc..866d3558f 100644 --- a/src/uu/b2sum/Cargo.toml +++ b/src/uu/b2sum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/base32/Cargo.toml b/src/uu/base32/Cargo.toml index 2318911b5..bec73709c 100644 --- a/src/uu/base32/Cargo.toml +++ b/src/uu/base32/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/base64/Cargo.toml b/src/uu/base64/Cargo.toml index 67442ec22..38742d357 100644 --- a/src/uu/base64/Cargo.toml +++ b/src/uu/base64/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/basename/Cargo.toml b/src/uu/basename/Cargo.toml index 9fe4adc03..00230ea76 100644 --- a/src/uu/basename/Cargo.toml +++ b/src/uu/basename/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/basenc/Cargo.toml b/src/uu/basenc/Cargo.toml index f68a0f110..c36eb9c23 100644 --- a/src/uu/basenc/Cargo.toml +++ b/src/uu/basenc/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/cat/Cargo.toml b/src/uu/cat/Cargo.toml index 7dbd1ecbf..880972fa6 100644 --- a/src/uu/cat/Cargo.toml +++ b/src/uu/cat/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/chcon/Cargo.toml b/src/uu/chcon/Cargo.toml index b18da48f8..71e2c70e1 100644 --- a/src/uu/chcon/Cargo.toml +++ b/src/uu/chcon/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true homepage.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/checksum_common/Cargo.toml b/src/uu/checksum_common/Cargo.toml index b8f99e821..428e7c425 100644 --- a/src/uu/checksum_common/Cargo.toml +++ b/src/uu/checksum_common/Cargo.toml @@ -8,6 +8,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/chgrp/Cargo.toml b/src/uu/chgrp/Cargo.toml index 2a629542b..f6989c88c 100644 --- a/src/uu/chgrp/Cargo.toml +++ b/src/uu/chgrp/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/chmod/Cargo.toml b/src/uu/chmod/Cargo.toml index e1be6896f..7ac7d8323 100644 --- a/src/uu/chmod/Cargo.toml +++ b/src/uu/chmod/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/chown/Cargo.toml b/src/uu/chown/Cargo.toml index d11be8c44..211475c3f 100644 --- a/src/uu/chown/Cargo.toml +++ b/src/uu/chown/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/chroot/Cargo.toml b/src/uu/chroot/Cargo.toml index d251c9823..4b91c1a1f 100644 --- a/src/uu/chroot/Cargo.toml +++ b/src/uu/chroot/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/cksum/Cargo.toml b/src/uu/cksum/Cargo.toml index 5f509e313..37212eec8 100644 --- a/src/uu/cksum/Cargo.toml +++ b/src/uu/cksum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/comm/Cargo.toml b/src/uu/comm/Cargo.toml index 8f8f6fba7..5f1436e44 100644 --- a/src/uu/comm/Cargo.toml +++ b/src/uu/comm/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/cp/Cargo.toml b/src/uu/cp/Cargo.toml index 592b9cba9..79e00d6bb 100644 --- a/src/uu/cp/Cargo.toml +++ b/src/uu/cp/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/csplit/Cargo.toml b/src/uu/csplit/Cargo.toml index b07c47648..e2aeab2ab 100644 --- a/src/uu/csplit/Cargo.toml +++ b/src/uu/csplit/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/cut/Cargo.toml b/src/uu/cut/Cargo.toml index f7ea5b203..e45672e54 100644 --- a/src/uu/cut/Cargo.toml +++ b/src/uu/cut/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/date/Cargo.toml b/src/uu/date/Cargo.toml index 3cf27595e..c1a604035 100644 --- a/src/uu/date/Cargo.toml +++ b/src/uu/date/Cargo.toml @@ -10,6 +10,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 9da9143a8..e92ac0f0f 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/df/Cargo.toml b/src/uu/df/Cargo.toml index 0b0df7268..48b793145 100644 --- a/src/uu/df/Cargo.toml +++ b/src/uu/df/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/dir/Cargo.toml b/src/uu/dir/Cargo.toml index 9bbec793b..79cac6e99 100644 --- a/src/uu/dir/Cargo.toml +++ b/src/uu/dir/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/dircolors/Cargo.toml b/src/uu/dircolors/Cargo.toml index 077d4e59e..393b3f42f 100644 --- a/src/uu/dircolors/Cargo.toml +++ b/src/uu/dircolors/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/dirname/Cargo.toml b/src/uu/dirname/Cargo.toml index 85fc99a1c..76db2b955 100644 --- a/src/uu/dirname/Cargo.toml +++ b/src/uu/dirname/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/du/Cargo.toml b/src/uu/du/Cargo.toml index 99bb5a5de..06f996edc 100644 --- a/src/uu/du/Cargo.toml +++ b/src/uu/du/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/echo/Cargo.toml b/src/uu/echo/Cargo.toml index 30ce94bbe..09babded4 100644 --- a/src/uu/echo/Cargo.toml +++ b/src/uu/echo/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/env/Cargo.toml b/src/uu/env/Cargo.toml index b2e4208b9..b6b5a8cce 100644 --- a/src/uu/env/Cargo.toml +++ b/src/uu/env/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/expand/Cargo.toml b/src/uu/expand/Cargo.toml index 8776557d3..694c47e10 100644 --- a/src/uu/expand/Cargo.toml +++ b/src/uu/expand/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index bdea66698..037286d52 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index 15d09f7a0..988e809d0 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/false/Cargo.toml b/src/uu/false/Cargo.toml index 55a46e98e..46e119b28 100644 --- a/src/uu/false/Cargo.toml +++ b/src/uu/false/Cargo.toml @@ -10,6 +10,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/fmt/Cargo.toml b/src/uu/fmt/Cargo.toml index 78fa1cdea..e882966af 100644 --- a/src/uu/fmt/Cargo.toml +++ b/src/uu/fmt/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/fold/Cargo.toml b/src/uu/fold/Cargo.toml index 845ce2c96..e4e36b205 100644 --- a/src/uu/fold/Cargo.toml +++ b/src/uu/fold/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/groups/Cargo.toml b/src/uu/groups/Cargo.toml index f86b5652f..a94827160 100644 --- a/src/uu/groups/Cargo.toml +++ b/src/uu/groups/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/head/Cargo.toml b/src/uu/head/Cargo.toml index 2c0e18c1a..353404a77 100644 --- a/src/uu/head/Cargo.toml +++ b/src/uu/head/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/hostid/Cargo.toml b/src/uu/hostid/Cargo.toml index 2a0bdc449..f4a6414f5 100644 --- a/src/uu/hostid/Cargo.toml +++ b/src/uu/hostid/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/hostname/Cargo.toml b/src/uu/hostname/Cargo.toml index 4077143bc..2a5b89529 100644 --- a/src/uu/hostname/Cargo.toml +++ b/src/uu/hostname/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/id/Cargo.toml b/src/uu/id/Cargo.toml index de1752df1..ce31a65f5 100644 --- a/src/uu/id/Cargo.toml +++ b/src/uu/id/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/install/Cargo.toml b/src/uu/install/Cargo.toml index 9eb7679a4..41d1af3d1 100644 --- a/src/uu/install/Cargo.toml +++ b/src/uu/install/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/join/Cargo.toml b/src/uu/join/Cargo.toml index 8599c5c6a..0f5cd8a2c 100644 --- a/src/uu/join/Cargo.toml +++ b/src/uu/join/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/kill/Cargo.toml b/src/uu/kill/Cargo.toml index 10de8379d..0b20e594f 100644 --- a/src/uu/kill/Cargo.toml +++ b/src/uu/kill/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/link/Cargo.toml b/src/uu/link/Cargo.toml index 6c23e965b..c1da6b5e0 100644 --- a/src/uu/link/Cargo.toml +++ b/src/uu/link/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/ln/Cargo.toml b/src/uu/ln/Cargo.toml index 6cae47f78..6527ee89a 100644 --- a/src/uu/ln/Cargo.toml +++ b/src/uu/ln/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/logname/Cargo.toml b/src/uu/logname/Cargo.toml index 67e0b5be9..8d1e17962 100644 --- a/src/uu/logname/Cargo.toml +++ b/src/uu/logname/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index 0b53e8749..a82ad13c3 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -11,6 +11,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/md5sum/Cargo.toml b/src/uu/md5sum/Cargo.toml index 23ee431af..de269a099 100644 --- a/src/uu/md5sum/Cargo.toml +++ b/src/uu/md5sum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/mkdir/Cargo.toml b/src/uu/mkdir/Cargo.toml index b2723e1cf..6e2e082d0 100644 --- a/src/uu/mkdir/Cargo.toml +++ b/src/uu/mkdir/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/mkfifo/Cargo.toml b/src/uu/mkfifo/Cargo.toml index ece483810..594b91ae7 100644 --- a/src/uu/mkfifo/Cargo.toml +++ b/src/uu/mkfifo/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index a32aa3e9a..2ea3daddc 100644 --- a/src/uu/mknod/Cargo.toml +++ b/src/uu/mknod/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/mktemp/Cargo.toml b/src/uu/mktemp/Cargo.toml index 651db9e21..5c42d14b6 100644 --- a/src/uu/mktemp/Cargo.toml +++ b/src/uu/mktemp/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index bee3ff755..5057cee8a 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/mv/Cargo.toml b/src/uu/mv/Cargo.toml index 0ed038fe6..5cba2d472 100644 --- a/src/uu/mv/Cargo.toml +++ b/src/uu/mv/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/nice/Cargo.toml b/src/uu/nice/Cargo.toml index 00f96718e..58dfb69f6 100644 --- a/src/uu/nice/Cargo.toml +++ b/src/uu/nice/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/nl/Cargo.toml b/src/uu/nl/Cargo.toml index 82d06cc7f..5818dfa91 100644 --- a/src/uu/nl/Cargo.toml +++ b/src/uu/nl/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/nohup/Cargo.toml b/src/uu/nohup/Cargo.toml index 50eb258a9..4b9d0213b 100644 --- a/src/uu/nohup/Cargo.toml +++ b/src/uu/nohup/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/nproc/Cargo.toml b/src/uu/nproc/Cargo.toml index 641fc413e..496a8409f 100644 --- a/src/uu/nproc/Cargo.toml +++ b/src/uu/nproc/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/numfmt/Cargo.toml b/src/uu/numfmt/Cargo.toml index fed39ad68..765d4dcce 100644 --- a/src/uu/numfmt/Cargo.toml +++ b/src/uu/numfmt/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/od/Cargo.toml b/src/uu/od/Cargo.toml index 13d12a413..31cdc8c78 100644 --- a/src/uu/od/Cargo.toml +++ b/src/uu/od/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/paste/Cargo.toml b/src/uu/paste/Cargo.toml index ca0efa67c..76f0919da 100644 --- a/src/uu/paste/Cargo.toml +++ b/src/uu/paste/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/pathchk/Cargo.toml b/src/uu/pathchk/Cargo.toml index b75bfab51..dcf6ced60 100644 --- a/src/uu/pathchk/Cargo.toml +++ b/src/uu/pathchk/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/pinky/Cargo.toml b/src/uu/pinky/Cargo.toml index acb0eb207..5fc84f3bd 100644 --- a/src/uu/pinky/Cargo.toml +++ b/src/uu/pinky/Cargo.toml @@ -11,6 +11,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/pr/Cargo.toml b/src/uu/pr/Cargo.toml index 4eb7539dd..e3953c62b 100644 --- a/src/uu/pr/Cargo.toml +++ b/src/uu/pr/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/printenv/Cargo.toml b/src/uu/printenv/Cargo.toml index 09f073ffc..b73975636 100644 --- a/src/uu/printenv/Cargo.toml +++ b/src/uu/printenv/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/printf/Cargo.toml b/src/uu/printf/Cargo.toml index 05d033fc6..dfeacb30e 100644 --- a/src/uu/printf/Cargo.toml +++ b/src/uu/printf/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/ptx/Cargo.toml b/src/uu/ptx/Cargo.toml index 44dbf9a5e..0e2d7ac8e 100644 --- a/src/uu/ptx/Cargo.toml +++ b/src/uu/ptx/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/pwd/Cargo.toml b/src/uu/pwd/Cargo.toml index 1a1fabcfa..5d34c0c23 100644 --- a/src/uu/pwd/Cargo.toml +++ b/src/uu/pwd/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/readlink/Cargo.toml b/src/uu/readlink/Cargo.toml index 31d3d851c..a4b92b6f0 100644 --- a/src/uu/readlink/Cargo.toml +++ b/src/uu/readlink/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/realpath/Cargo.toml b/src/uu/realpath/Cargo.toml index d0a37a8b4..af675def1 100644 --- a/src/uu/realpath/Cargo.toml +++ b/src/uu/realpath/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/rm/Cargo.toml b/src/uu/rm/Cargo.toml index cf53e0323..e04b0e52d 100644 --- a/src/uu/rm/Cargo.toml +++ b/src/uu/rm/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/rmdir/Cargo.toml b/src/uu/rmdir/Cargo.toml index 6001af169..2841e3412 100644 --- a/src/uu/rmdir/Cargo.toml +++ b/src/uu/rmdir/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/runcon/Cargo.toml b/src/uu/runcon/Cargo.toml index fdb2f5174..6ed1c2e3d 100644 --- a/src/uu/runcon/Cargo.toml +++ b/src/uu/runcon/Cargo.toml @@ -6,6 +6,7 @@ keywords = ["coreutils", "uutils", "cli", "utility"] authors.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true homepage.workspace = true license.workspace = true version.workspace = true diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index cdc1c29af..87b6a52e1 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -10,6 +10,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sha1sum/Cargo.toml b/src/uu/sha1sum/Cargo.toml index 7cb6aceab..0704d6ba6 100644 --- a/src/uu/sha1sum/Cargo.toml +++ b/src/uu/sha1sum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sha224sum/Cargo.toml b/src/uu/sha224sum/Cargo.toml index 3436c9d2b..6120aad80 100644 --- a/src/uu/sha224sum/Cargo.toml +++ b/src/uu/sha224sum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sha256sum/Cargo.toml b/src/uu/sha256sum/Cargo.toml index 43a5c8577..6a93bf916 100644 --- a/src/uu/sha256sum/Cargo.toml +++ b/src/uu/sha256sum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sha384sum/Cargo.toml b/src/uu/sha384sum/Cargo.toml index ff9d57871..00e9f5472 100644 --- a/src/uu/sha384sum/Cargo.toml +++ b/src/uu/sha384sum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sha512sum/Cargo.toml b/src/uu/sha512sum/Cargo.toml index 281544adb..cf204df69 100644 --- a/src/uu/sha512sum/Cargo.toml +++ b/src/uu/sha512sum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/shred/Cargo.toml b/src/uu/shred/Cargo.toml index 59f0fb6c2..6ebecb01a 100644 --- a/src/uu/shred/Cargo.toml +++ b/src/uu/shred/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/shuf/Cargo.toml b/src/uu/shuf/Cargo.toml index bfe41d46e..d59f022e1 100644 --- a/src/uu/shuf/Cargo.toml +++ b/src/uu/shuf/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sleep/Cargo.toml b/src/uu/sleep/Cargo.toml index bf97f856f..ccd878bd4 100644 --- a/src/uu/sleep/Cargo.toml +++ b/src/uu/sleep/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index e487a1bfe..2aba9c73b 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -11,6 +11,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/split/Cargo.toml b/src/uu/split/Cargo.toml index 2c51bb780..d9878b47b 100644 --- a/src/uu/split/Cargo.toml +++ b/src/uu/split/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/stat/Cargo.toml b/src/uu/stat/Cargo.toml index 21adecd90..d01a713ec 100644 --- a/src/uu/stat/Cargo.toml +++ b/src/uu/stat/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/stdbuf/Cargo.toml b/src/uu/stdbuf/Cargo.toml index 802796199..b0742e89d 100644 --- a/src/uu/stdbuf/Cargo.toml +++ b/src/uu/stdbuf/Cargo.toml @@ -10,6 +10,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/stdbuf/src/libstdbuf/Cargo.toml b/src/uu/stdbuf/src/libstdbuf/Cargo.toml index 8a92fcbb5..2e76b2ee6 100644 --- a/src/uu/stdbuf/src/libstdbuf/Cargo.toml +++ b/src/uu/stdbuf/src/libstdbuf/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true [lints] workspace = true diff --git a/src/uu/stty/Cargo.toml b/src/uu/stty/Cargo.toml index 94812b2ac..f6c472a89 100644 --- a/src/uu/stty/Cargo.toml +++ b/src/uu/stty/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sum/Cargo.toml b/src/uu/sum/Cargo.toml index 983a7208c..b3b923ec2 100644 --- a/src/uu/sum/Cargo.toml +++ b/src/uu/sum/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/sync/Cargo.toml b/src/uu/sync/Cargo.toml index 6d00339ab..21df8266d 100644 --- a/src/uu/sync/Cargo.toml +++ b/src/uu/sync/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/tac/Cargo.toml b/src/uu/tac/Cargo.toml index 82ec5c5bc..e65ff6c88 100644 --- a/src/uu/tac/Cargo.toml +++ b/src/uu/tac/Cargo.toml @@ -11,6 +11,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index f01b4f603..5e11a5411 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -10,6 +10,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index 38a946edf..9be41804a 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/test/Cargo.toml b/src/uu/test/Cargo.toml index 3348efd7b..002e42fd0 100644 --- a/src/uu/test/Cargo.toml +++ b/src/uu/test/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/timeout/Cargo.toml b/src/uu/timeout/Cargo.toml index e0f3db171..90a0b378c 100644 --- a/src/uu/timeout/Cargo.toml +++ b/src/uu/timeout/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/touch/Cargo.toml b/src/uu/touch/Cargo.toml index 1bde504fb..9f66c3795 100644 --- a/src/uu/touch/Cargo.toml +++ b/src/uu/touch/Cargo.toml @@ -10,6 +10,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/tr/Cargo.toml b/src/uu/tr/Cargo.toml index 0ab2ca9dd..e470db1ce 100644 --- a/src/uu/tr/Cargo.toml +++ b/src/uu/tr/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/true/Cargo.toml b/src/uu/true/Cargo.toml index 468edddc2..c77ad1ba7 100644 --- a/src/uu/true/Cargo.toml +++ b/src/uu/true/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/truncate/Cargo.toml b/src/uu/truncate/Cargo.toml index 07ab63e6d..c2bd37149 100644 --- a/src/uu/truncate/Cargo.toml +++ b/src/uu/truncate/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/tsort/Cargo.toml b/src/uu/tsort/Cargo.toml index 74209a62f..84a313496 100644 --- a/src/uu/tsort/Cargo.toml +++ b/src/uu/tsort/Cargo.toml @@ -10,6 +10,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/tty/Cargo.toml b/src/uu/tty/Cargo.toml index 77165c605..fe5a97b07 100644 --- a/src/uu/tty/Cargo.toml +++ b/src/uu/tty/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/uname/Cargo.toml b/src/uu/uname/Cargo.toml index f640412e2..fe704f96e 100644 --- a/src/uu/uname/Cargo.toml +++ b/src/uu/uname/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/unexpand/Cargo.toml b/src/uu/unexpand/Cargo.toml index d7ea1533b..81e44d920 100644 --- a/src/uu/unexpand/Cargo.toml +++ b/src/uu/unexpand/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/uniq/Cargo.toml b/src/uu/uniq/Cargo.toml index 0bd197827..a484c2a14 100644 --- a/src/uu/uniq/Cargo.toml +++ b/src/uu/uniq/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/unlink/Cargo.toml b/src/uu/unlink/Cargo.toml index 85d668751..8f9adc52d 100644 --- a/src/uu/unlink/Cargo.toml +++ b/src/uu/unlink/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/uptime/Cargo.toml b/src/uu/uptime/Cargo.toml index 026039026..3d078fabb 100644 --- a/src/uu/uptime/Cargo.toml +++ b/src/uu/uptime/Cargo.toml @@ -11,6 +11,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/users/Cargo.toml b/src/uu/users/Cargo.toml index fa311e74a..3a68307e3 100644 --- a/src/uu/users/Cargo.toml +++ b/src/uu/users/Cargo.toml @@ -11,6 +11,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/vdir/Cargo.toml b/src/uu/vdir/Cargo.toml index 63097cdec..a9b6c5191 100644 --- a/src/uu/vdir/Cargo.toml +++ b/src/uu/vdir/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/wc/Cargo.toml b/src/uu/wc/Cargo.toml index ae9bb6e89..b4c2005af 100644 --- a/src/uu/wc/Cargo.toml +++ b/src/uu/wc/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/who/Cargo.toml b/src/uu/who/Cargo.toml index fdb5dd41d..c4cf5145b 100644 --- a/src/uu/who/Cargo.toml +++ b/src/uu/who/Cargo.toml @@ -12,6 +12,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/whoami/Cargo.toml b/src/uu/whoami/Cargo.toml index 80e135827..fe88d6407 100644 --- a/src/uu/whoami/Cargo.toml +++ b/src/uu/whoami/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uu/yes/Cargo.toml b/src/uu/yes/Cargo.toml index 33623c7c9..b8703bb55 100644 --- a/src/uu/yes/Cargo.toml +++ b/src/uu/yes/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true keywords.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true readme.workspace = true [lints] diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index d18d0630e..a914f9117 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -7,6 +7,7 @@ repository = "https://github.com/uutils/coreutils/tree/main/src/uucore" authors.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true homepage.workspace = true keywords.workspace = true license.workspace = true diff --git a/src/uucore_procs/Cargo.toml b/src/uucore_procs/Cargo.toml index 956813592..a8c76441c 100644 --- a/src/uucore_procs/Cargo.toml +++ b/src/uucore_procs/Cargo.toml @@ -6,6 +6,7 @@ keywords = ["cross-platform", "proc-macros", "uucore", "uutils"] # categories = ["os"] authors.workspace = true edition.workspace = true +rust-version.workspace = true homepage.workspace = true license.workspace = true version.workspace = true diff --git a/tests/uutests/Cargo.toml b/tests/uutests/Cargo.toml index 457930b4e..56ccab13b 100644 --- a/tests/uutests/Cargo.toml +++ b/tests/uutests/Cargo.toml @@ -7,6 +7,7 @@ repository = "https://github.com/uutils/coreutils/tree/main/tests/uutests" authors.workspace = true categories.workspace = true edition.workspace = true +rust-version.workspace = true homepage.workspace = true keywords.workspace = true license.workspace = true From 5648194754360bff5554287eda7cfd80ac91acd6 Mon Sep 17 00:00:00 2001 From: cerdelen <95369756+cerdelen@users.noreply.github.com> Date: Sat, 7 Feb 2026 15:50:42 +0100 Subject: [PATCH 14/46] Du size_format flag override (#10743) * du: Extend size_format flag override tests * du: Extend size_override implementation for '-h' '--si' '-h' '--block-size' * du: Extend size_format flag override tests * du: Extend size_format override tests for correct error return with invalid block-size value regardless of override * du: Refactor size-format parsing code --------- Co-authored-by: Sylvestre Ledru --- src/uu/du/src/du.rs | 57 +++++++++++++++++------------- tests/by-util/test_du.rs | 76 +++++++++++++++++++++++++++++++++------- 2 files changed, 97 insertions(+), 36 deletions(-) diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index a1b3596cc..46c51440d 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -963,38 +963,61 @@ fn read_files_from(file_name: &OsStr) -> Result, std::io::Error> { Ok(paths) } -fn get_block_size_arg_index_if_present(matches: &ArgMatches, flag: &str) -> Option { - if matches.get_flag(flag) { - // Indices of returns index even if flag is not present, thats why we need to if guard it +fn get_size_format_flag_arg_index_if_present(matches: &ArgMatches, arg: &str) -> Option { + if let Some(clap::parser::ValueSource::CommandLine) = matches.value_source(arg) { matches - .indices_of(flag) + .indices_of(arg) .and_then(|mut indices| indices.next_back()) } else { None } } -fn handle_block_size_arg_override(matches: &ArgMatches) -> Option { +fn parse_block_size_arg_or_default_fallback(matches: &ArgMatches) -> UResult { + let block_size_str = matches.get_one::(options::BLOCK_SIZE); + let block_size = read_block_size(block_size_str.map(AsRef::as_ref))?; + if block_size == 0 { + return Err(std::io::Error::other(translate!("du-error-invalid-block-size-argument", "option" => options::BLOCK_SIZE, "value" => block_size_str.map_or("???BUG", |v| v).quote())) + .into()); + } + Ok(SizeFormat::BlockSize(block_size)) +} + +fn parse_size_format(matches: &ArgMatches) -> UResult { + let block_size_value_or_default_fallback = parse_block_size_arg_or_default_fallback(matches)?; let candidates = [ ( SizeFormat::BlockSize(1), - get_block_size_arg_index_if_present(matches, options::BYTES), + get_size_format_flag_arg_index_if_present(matches, options::BYTES), ), ( SizeFormat::BlockSize(1024), - get_block_size_arg_index_if_present(matches, options::BLOCK_SIZE_1K), + get_size_format_flag_arg_index_if_present(matches, options::BLOCK_SIZE_1K), ), ( SizeFormat::BlockSize(1024 * 1024), - get_block_size_arg_index_if_present(matches, options::BLOCK_SIZE_1M), + get_size_format_flag_arg_index_if_present(matches, options::BLOCK_SIZE_1M), + ), + ( + SizeFormat::HumanBinary, + get_size_format_flag_arg_index_if_present(matches, options::HUMAN_READABLE), + ), + ( + SizeFormat::HumanDecimal, + get_size_format_flag_arg_index_if_present(matches, options::SI), + ), + ( + block_size_value_or_default_fallback.clone(), + get_size_format_flag_arg_index_if_present(matches, options::BLOCK_SIZE), ), ]; - candidates + Ok(candidates .into_iter() .filter(|(_, idx)| idx.is_some()) .max_by_key(|&(_, idx)| idx.unwrap_or(0)) .map(|(size_format, _)| size_format) + .unwrap_or(block_size_value_or_default_fallback)) } #[uucore::main] @@ -1048,21 +1071,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .map_or(MetadataTimeField::Modification, |s| s.as_str().into()) }); - let size_format = if matches.get_flag(options::HUMAN_READABLE) { - SizeFormat::HumanBinary - } else if matches.get_flag(options::SI) { - SizeFormat::HumanDecimal - } else if let Some(size_format) = handle_block_size_arg_override(&matches) { - size_format - } else { - let block_size_str = matches.get_one::(options::BLOCK_SIZE); - let block_size = read_block_size(block_size_str.map(AsRef::as_ref))?; - if block_size == 0 { - return Err(std::io::Error::other(translate!("du-error-invalid-block-size-argument", "option" => options::BLOCK_SIZE, "value" => block_size_str.map_or("???BUG", |v| v).quote())) - .into()); - } - SizeFormat::BlockSize(block_size) - }; + let size_format = parse_size_format(&matches)?; let traversal_options = TraversalOptions { all: matches.get_flag(options::ALL), diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index e73655c44..2b3f54302 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -2064,24 +2064,45 @@ fn test_block_size_args_override() { let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; let dir = "override_args_dir"; + let nested_dir = "override_args_dir/nested_dir"; + let nested_dir_2 = "override_args_dir/nested_dir_2"; - at.mkdir(dir); - let fpath = at.plus(format!("{dir}/file")); + at.mkdir_all(nested_dir); + at.mkdir_all(nested_dir_2); + let fpath = at.plus(format!("{nested_dir}/file")); std::fs::File::create(fpath) .expect("cannot create test file") .set_len(100_000_000) .expect("cannot set file size"); - let fpath2 = at.plus(format!("{dir}/file_2")); + let fpath2 = at.plus(format!("{nested_dir}/file_2")); std::fs::File::create(fpath2) .expect("cannot create test file") .set_len(100_000_000) .expect("cannot set file size"); + let fpath = at.plus(format!("{nested_dir_2}/file_3")); + std::fs::File::create(fpath) + .expect("cannot create test file") + .set_len(100_000) + .expect("cannot set file size"); + + let fpath2 = at.plus(format!("{nested_dir_2}/file_4")); + std::fs::File::create(fpath2) + .expect("cannot create test file") + .set_len(100) + .expect("cannot set file size"); + let test_cases = [ - (["-sk", "-m"], "-sm"), - (["-sk", "-b"], "-sb"), - (["-sm", "-k"], "-sk"), + (["-sk", "-m"], vec!["-sm"]), + (["-sk", "-b"], vec!["-sb"]), + (["-sm", "-k"], vec!["-sk"]), + (["-sk", "--si"], vec!["-s", "--si"]), + (["-sk", "-h"], vec!["-s", "-h"]), + (["-sm", "--block-size=128"], vec!["-s", "--block-size=128"]), + (["--block-size=128", "-b"], vec!["-b"]), + (["--si", "-b"], vec!["-b"]), + (["-h", "-b"], vec!["-b"]), ]; for (idx, (overwriting_args, expected)) in test_cases.into_iter().enumerate() { @@ -2095,7 +2116,7 @@ fn test_block_size_args_override() { let single_args = ts .ucmd() .arg(dir) - .arg(expected) + .args(&expected) .succeeds() .stdout_move_str(); @@ -2111,23 +2132,30 @@ fn test_block_override_b_still_has_apparent_size() { let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; let dir = "override_args_dir"; + let nested_dir = "override_args_dir/nested_dir"; - at.mkdir(dir); - let fpath = at.plus(format!("{dir}/file")); + at.mkdir_all(nested_dir); + let fpath = at.plus(format!("{nested_dir}/file")); std::fs::File::create(fpath) .expect("cannot create test file") .set_len(100_000_000) .expect("cannot set file size"); - let fpath2 = at.plus(format!("{dir}/file_2")); + let fpath2 = at.plus(format!("{nested_dir}/file_2")); std::fs::File::create(fpath2) .expect("cannot create test file") .set_len(100_000_000) .expect("cannot set file size"); let test_cases = [ - (["-sb", "-m"], ["-sm", "--apparent-size"]), - (["-sb", "-k"], ["-sk", "--apparent-size"]), + (["-b", "-m"], ["-m", "--apparent-size"]), + (["-b", "-k"], ["-k", "--apparent-size"]), + (["-b", "--si"], ["--si", "--apparent-size"]), + (["-b", "-h"], ["-h", "--apparent-size"]), + ( + ["-b", "--block-size=128"], + ["--block-size=128", "--apparent-size"], + ), ]; for (idx, (overwriting_args, expected)) in test_cases.into_iter().enumerate() { @@ -2151,3 +2179,27 @@ fn test_block_override_b_still_has_apparent_size() { ); } } + +#[test] +fn test_overriding_block_size_arg_with_invalid_value_still_errors() { + new_ucmd!() + .args(&["--block-size=abc", "-m"]) + .fails_with_code(1) + .stderr_contains("invalid --block-size argument 'abc'"); + new_ucmd!() + .args(&["--block-size=abc", "-k"]) + .fails_with_code(1) + .stderr_contains("invalid --block-size argument 'abc'"); + new_ucmd!() + .args(&["--block-size=abc", "-b"]) + .fails_with_code(1) + .stderr_contains("invalid --block-size argument 'abc'"); + new_ucmd!() + .args(&["--block-size=abc", "-h"]) + .fails_with_code(1) + .stderr_contains("invalid --block-size argument 'abc'"); + new_ucmd!() + .args(&["--block-size=abc", "--si"]) + .fails_with_code(1) + .stderr_contains("invalid --block-size argument 'abc'"); +} From fafa77588baef79454978b5444afa221ea47828c Mon Sep 17 00:00:00 2001 From: Reuben Wong Date: Sat, 7 Feb 2026 22:52:58 +0800 Subject: [PATCH 15/46] sync: open file with nonblock (#10765) * open file with nonblock and add test * fix spellcheck * remove libc dependency; improve errors --------- Co-authored-by: Sylvestre Ledru --- src/uu/sync/locales/en-US.ftl | 1 + src/uu/sync/locales/fr-FR.ftl | 1 + src/uu/sync/src/sync.rs | 20 ++++++++++++++++---- tests/by-util/test_sync.rs | 21 +++++++++++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/uu/sync/locales/en-US.ftl b/src/uu/sync/locales/en-US.ftl index d0b520bcb..b473c03d9 100644 --- a/src/uu/sync/locales/en-US.ftl +++ b/src/uu/sync/locales/en-US.ftl @@ -9,6 +9,7 @@ sync-help-data = sync only file data, no unneeded metadata (Linux only) sync-error-data-needs-argument = --data needs at least one argument sync-error-opening-file = error opening { $file } sync-error-no-such-file = error opening { $file }: No such file or directory +sync-error-syncing-file = error syncing { $file } # Warning messages sync-warning-fcntl-failed = warning: failed to reset O_NONBLOCK flag for { $file }: { $error } diff --git a/src/uu/sync/locales/fr-FR.ftl b/src/uu/sync/locales/fr-FR.ftl index f0d00db03..e2db79b90 100644 --- a/src/uu/sync/locales/fr-FR.ftl +++ b/src/uu/sync/locales/fr-FR.ftl @@ -9,6 +9,7 @@ sync-help-data = synchroniser seulement les données des fichiers, pas les méta sync-error-data-needs-argument = --data nécessite au moins un argument sync-error-opening-file = erreur lors de l'ouverture de { $file } sync-error-no-such-file = erreur lors de l'ouverture de { $file } : Aucun fichier ou répertoire de ce type +sync-error-syncing-file = erreur lors de la synchronisation de { $file } # Messages d'avertissement sync-warning-fcntl-failed = avertissement : échec de la réinitialisation du drapeau O_NONBLOCK pour { $file } : { $error } diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index 0e5b864b9..87873ceda 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -35,7 +35,11 @@ mod platform { #[cfg(any(target_os = "linux", target_os = "android"))] use nix::unistd::{fdatasync, syncfs}; #[cfg(any(target_os = "linux", target_os = "android"))] - use std::fs::File; + use std::fs::{File, OpenOptions}; + #[cfg(any(target_os = "linux", target_os = "android"))] + use std::os::unix::fs::OpenOptionsExt; + #[cfg(any(target_os = "linux", target_os = "android"))] + use uucore::display::Quotable; #[cfg(any(target_os = "linux", target_os = "android"))] use uucore::error::FromIo; #[cfg(any(target_os = "linux", target_os = "android"))] @@ -57,7 +61,11 @@ mod platform { /// Logs a warning if fcntl fails but doesn't abort the operation. #[cfg(any(target_os = "linux", target_os = "android"))] fn open_and_reset_nonblock(path: &str) -> UResult { - let f = File::open(path).map_err_context(|| path.to_string())?; + let f = OpenOptions::new() + .read(true) + .custom_flags(OFlag::O_NONBLOCK.bits()) + .open(path) + .map_err_context(|| path.to_string())?; // Reset O_NONBLOCK flag if it was set (matches GNU behavior) // This is non-critical, so we log errors but don't fail if let Err(e) = fcntl(&f, FcntlArg::F_SETFL(OFlag::empty())) { @@ -73,7 +81,9 @@ mod platform { pub fn do_syncfs(files: Vec) -> UResult<()> { for path in files { let f = open_and_reset_nonblock(&path)?; - syncfs(f)?; + syncfs(f).map_err_context( + || translate!("sync-error-syncing-file", "file" => path.quote()), + )?; } Ok(()) } @@ -82,7 +92,9 @@ mod platform { pub fn do_fdatasync(files: Vec) -> UResult<()> { for path in files { let f = open_and_reset_nonblock(&path)?; - fdatasync(f)?; + fdatasync(f).map_err_context( + || translate!("sync-error-syncing-file", "file" => path.quote()), + )?; } Ok(()) } diff --git a/tests/by-util/test_sync.rs b/tests/by-util/test_sync.rs index 9c3df3a1e..fe979cc0d 100644 --- a/tests/by-util/test_sync.rs +++ b/tests/by-util/test_sync.rs @@ -167,3 +167,24 @@ fn test_sync_multiple_files() { // Sync both files new_ucmd!().arg("--data").arg(&file1).arg(&file2).succeeds(); } + +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn test_sync_data_fifo_fails_immediately() { + use std::time::Duration; + use uutests::util::TestScenario; + use uutests::util_name; + + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + at.mkfifo("test-fifo"); + + ts.ucmd() + .arg("--data") + .arg(at.plus_as_string("test-fifo")) + .timeout(Duration::from_secs(2)) + .fails() + .stderr_contains("error syncing") + .stderr_contains("test-fifo") + .stderr_contains("Invalid input"); +} From 323dfb78202a1db9ab48dc42fcd2ca3ffdce97cc Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 7 Feb 2026 16:44:32 +0100 Subject: [PATCH 16/46] ci: fix incorrect selection of version (#10789) --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 9793d9dc3..53f830a79 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -42,7 +42,7 @@ jobs: - name: Get current version from Cargo.toml id: version run: | - VERSION=$(awk '/\[workspace\.package\]/{flag=1; next} flag && /version = /{gsub(/.*= "/, ""); gsub(/".*/, ""); print; exit}' Cargo.toml) + VERSION=$(awk '/\[workspace\.package\]/{flag=1; next} flag && /^version = /{gsub(/.*= "/, ""); gsub(/".*/, ""); print; exit}' Cargo.toml) echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Detected version: $VERSION" From 548afc0c410e0e78a7895fdd43b5ca2297745447 Mon Sep 17 00:00:00 2001 From: Max Ambaum Date: Sat, 7 Feb 2026 16:03:05 +0000 Subject: [PATCH 17/46] Fixed permissions in install for unix (#10564) Co-authored-by: Sylvestre Ledru --- src/uu/install/src/install.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index a3cd77931..84eefdcc8 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -813,6 +813,7 @@ fn perform_backup(to: &Path, b: &Behavior) -> UResult> { /// Returns an empty Result or an error in case of failure. /// fn copy_file(from: &Path, to: &Path) -> UResult<()> { + use std::os::unix::fs::OpenOptionsExt; if let Ok(to_abs) = to.canonicalize() { if from.canonicalize()? == to_abs { return Err(InstallError::SameFile(from.to_path_buf(), to.to_path_buf()).into()); @@ -841,7 +842,11 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> { let mut handle = File::open(from)?; // create_new provides TOCTOU protection - let mut dest = OpenOptions::new().write(true).create_new(true).open(to)?; + let mut dest = OpenOptions::new() + .write(true) + .create_new(true) + .mode(0o600) + .open(to)?; copy_stream(&mut handle, &mut dest).map_err(|err| { InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err.to_string()) From bcb890bcd6a24092653b59e6832b86d5c4448e5b Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:16:32 +0900 Subject: [PATCH 18/46] shuf: drop inline (#10781) --- src/uu/shuf/src/shuf.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index f0caf445d..c7e5689ab 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -378,7 +378,6 @@ impl Writable for &OsStr { } impl Writable for u64 { - #[inline] fn write_all_to(&self, output: &mut impl OsWrite) -> Result<(), io::Error> { // The itoa crate is surprisingly much more efficient than a formatted write. // It speeds up `shuf -r -n1000000 -i1-1024` by 1.8×. @@ -395,7 +394,6 @@ fn handle_write_error(e: io::Error) -> Box { e.map_err_context(move || ctx) } -#[inline(never)] fn shuf_exec( input: &mut impl Shufable, opts: &Options, From eccd15797d649679fbd780788c412f7862d25c44 Mon Sep 17 00:00:00 2001 From: Rostyslav Toch Date: Sat, 7 Feb 2026 16:17:18 +0000 Subject: [PATCH 19/46] perf(split): optimize FixedWidthNumber Display implementation (#10723) Co-authored-by: Sylvestre Ledru --- src/uu/split/src/number.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/uu/split/src/number.rs b/src/uu/split/src/number.rs index fd0256a15..58c08d12b 100644 --- a/src/uu/split/src/number.rs +++ b/src/uu/split/src/number.rs @@ -15,7 +15,7 @@ //! [radix]: https://en.wikipedia.org/wiki/Radix //! [positional notation]: https://en.wikipedia.org/wiki/Positional_notation use std::error::Error; -use std::fmt::{self, Display, Formatter}; +use std::fmt::{self, Display, Formatter, Write}; use uucore::translate; /// An overflow due to incrementing a number beyond its representable limit. @@ -244,12 +244,10 @@ impl FixedWidthNumber { impl Display for FixedWidthNumber { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let digits: String = self - .digits - .iter() - .map(|d| map_digit(self.radix, *d)) - .collect(); - write!(f, "{digits}") + for d in &self.digits { + f.write_char(map_digit(self.radix, *d))?; + } + Ok(()) } } From 76df3f5d011744a8184936e1c8f2d39624033335 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 7 Feb 2026 17:56:22 +0100 Subject: [PATCH 20/46] cp: improve code clarity and remove redundant filesystem checks (#10790) --- src/uu/cp/src/copydir.rs | 33 ++++++++++++++++++++++++--------- src/uu/cp/src/cp.rs | 6 +++--- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/uu/cp/src/copydir.rs b/src/uu/cp/src/copydir.rs index 614bd597d..7f63b5e7e 100644 --- a/src/uu/cp/src/copydir.rs +++ b/src/uu/cp/src/copydir.rs @@ -31,6 +31,16 @@ use crate::{ copy_file, }; +/// Represents a directory that needs permission fixup after copying its contents. +struct DirNeedingPermissions { + /// Absolute path to the source directory + source: PathBuf, + /// Path to the destination directory + dest: PathBuf, + /// Whether this directory was freshly created by the copy operation + was_created: bool, +} + /// Ensure a Windows path starts with a `\\?`. #[cfg(target_os = "windows")] fn adjust_canonicalization(p: &Path) -> Cow<'_, Path> { @@ -237,7 +247,12 @@ impl Entry { #[allow(clippy::too_many_arguments)] /// Copy a single entry during a directory traversal. -/// Returns a bool value indicating whether this function created a directory or not +/// +/// # Returns +/// +/// Returns `Ok(true)` if this function created a new directory, `Ok(false)` otherwise. +/// This information is used to determine whether default directory permissions should +/// be preserved during attribute copying. fn copy_direntry( progress_bar: Option<&ProgressBar>, entry: &Entry, @@ -422,7 +437,7 @@ pub(crate) fn copy_directory( let mut last_iter: Option = None; // Keep track of all directories we've created that need permission fixes - let mut dirs_needing_permissions: Vec<(PathBuf, PathBuf, bool)> = Vec::new(); + let mut dirs_needing_permissions: Vec = Vec::new(); // Traverse the contents of the directory, copying each one. for direntry_result in WalkDir::new(root) @@ -481,11 +496,11 @@ pub(crate) fn copy_directory( continue; } // Add this directory to our list for permission fixing later - dirs_needing_permissions.push(( - entry.source_absolute.clone(), - entry.local_to_target.clone(), - created, - )); + dirs_needing_permissions.push(DirNeedingPermissions { + source: entry.source_absolute.clone(), + dest: entry.local_to_target.clone(), + was_created: created, + }); // If true, last_iter is not a parent of this iter. // The means we just exited a directory. @@ -534,8 +549,8 @@ pub(crate) fn copy_directory( // Fix permissions for all directories we created // This ensures that even sibling directories get their permissions fixed - for (source_path, dest_path, created) in dirs_needing_permissions { - copy_attributes(&source_path, &dest_path, &options.attributes, created)?; + for dir in dirs_needing_permissions { + copy_attributes(&dir.source, &dir.dest, &options.attributes, dir.was_created)?; } // Also fix permissions for parent directories, diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index c57d962d0..db38d5c3e 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1717,16 +1717,16 @@ pub(crate) fn copy_attributes( source: &Path, dest: &Path, attributes: &Attributes, - created: bool, + dest_is_freshly_created_dir: bool, ) -> CopyResult<()> { let context = &*format!("{} -> {}", source.quote(), dest.quote()); let source_metadata = fs::symlink_metadata(source).map_err(|e| CpError::IoErrContext(e, context.to_owned()))?; - let is_explicit = matches!(attributes.mode, Preserve::No { explicit: true }); + let mode_explicitly_disabled = matches!(attributes.mode, Preserve::No { explicit: true }); // preserve is true by default if the destination is created by us and it's a directory - let mode = if !is_explicit && dest.is_dir() && created { + let mode = if !mode_explicitly_disabled && dest_is_freshly_created_dir { Preserve::Yes { required: false } } else { attributes.mode From dc982b144457a93beb71eff739eb96948a331949 Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Sun, 8 Feb 2026 02:11:26 +0900 Subject: [PATCH 21/46] GnuTests: 9.10, remove a bunch of backported tests (#10721) --- util/build-gnu.sh | 2 +- util/fetch-gnu.sh | 31 ++++--------------- util/gnu-patches/series | 1 - .../tests_tail_overlay_headers.patch | 16 ---------- 4 files changed, 7 insertions(+), 43 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 70886e5e9..7cda49fbe 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -124,7 +124,7 @@ else : > man/local.mk # Use CFLAGS for best build time since we discard GNU coreutils CFLAGS="${CFLAGS} -pipe -O0 -s" ./configure -C --quiet --disable-gcc-warnings --disable-nls --disable-dependency-tracking --disable-bold-man-page-references \ - --enable-single-binary=symlinks --enable-install-program="arch,kill,uptime,hostname" \ + --enable-single-binary=hardlinks --enable-install-program="arch,kill,uptime,hostname" \ "$([ "${SELINUX_ENABLED}" = 1 ] && echo --with-selinux || echo --without-selinux)" #Add timeout to to protect against hangs sed -i 's|^"\$@|'"${SYSTEM_TIMEOUT}"' 600 "\$@|' build-aux/test-driver diff --git a/util/fetch-gnu.sh b/util/fetch-gnu.sh index a242b6606..afd9ebfa8 100755 --- a/util/fetch-gnu.sh +++ b/util/fetch-gnu.sh @@ -1,29 +1,10 @@ #!/bin/bash -e -ver="9.9" +ver="9.10" repo=https://github.com/coreutils/coreutils curl -L "${repo}/releases/download/v${ver}/coreutils-${ver}.tar.xz" | tar --strip-components=1 -xJf - -# TODO stop backporting tests from master at GNU coreutils > 9.9 -curl -L ${repo}/raw/refs/heads/master/tests/timeout/timeout.sh > tests/timeout/timeout.sh -curl -L ${repo}/raw/refs/heads/master/tests/timeout/timeout-group.sh > tests/timeout/timeout-group.sh -curl -L ${repo}/raw/refs/heads/master/tests/mv/hardlink-case.sh > tests/mv/hardlink-case.sh -curl -L ${repo}/raw/refs/heads/master/tests/mkdir/writable-under-readonly.sh > tests/mkdir/writable-under-readonly.sh -curl -L ${repo}/raw/refs/heads/master/tests/cp/cp-mv-enotsup-xattr.sh > tests/cp/cp-mv-enotsup-xattr.sh #spell-checker:disable-line -curl -L ${repo}/raw/refs/heads/master/tests/cp/nfs-removal-race.sh > tests/cp/nfs-removal-race.sh -curl -L ${repo}/raw/refs/heads/master/tests/csplit/csplit-io-err.sh > tests/csplit/csplit-io-err.sh -# Replace tests not compatible with our binaries -sed -i -e 's/no-mtab-status.sh/no-mtab-status-masked-proc.sh/' -e 's/nproc-quota.sh/nproc-quota-systemd.sh/' tests/local.mk -curl -L ${repo}/raw/refs/heads/master/tests/df/no-mtab-status-masked-proc.sh > tests/df/no-mtab-status-masked-proc.sh -curl -L ${repo}/raw/refs/heads/master/tests/nproc/nproc-quota-systemd.sh > tests/nproc/nproc-quota-systemd.sh -curl -L ${repo}/raw/refs/heads/master/tests/stty/bad-speed.sh > tests/stty/bad-speed.sh -# symlink to /bin/false should fail with --help. Freeze commit to avoid regression. -curl -L https://raw.githubusercontent.com/coreutils/coreutils/d5164f3d216917005003877faeb1abe7cae5d765/tests/misc/coreutils.sh > tests/misc/coreutils.sh -# Better support for single binary -curl -L ${repo}/raw/refs/heads/master/tests/env/env.sh > tests/env/env.sh -# Avoid incorrect PASS -curl -L ${repo}/raw/refs/heads/master/tests/runcon/runcon-compute.sh > tests/runcon/runcon-compute.sh -curl -L ${repo}/raw/refs/heads/master/tests/tac/tac-continue.sh > tests/tac/tac-continue.sh -curl -L ${repo}/raw/refs/heads/master/tests/tail/inotify-dir-recreate.sh > tests/tail/inotify-dir-recreate.sh -# Add tac-continue.sh to root tests (it requires root to mount tmpfs) -# Use sed -i.bak for macOS -sed -i.bak 's|tests/split/l-chunk-root.sh.*|tests/split/l-chunk-root.sh\t\t\t\\\n tests/tac/tac-continue.sh\t\t\t\\|' tests/local.mk +# TODO stop backporting tests from master at GNU coreutils > $ver +# backport = () +# for f in ${backport[@]} +# do curl -L ${repo}/raw/refs/heads/master/tests/$f > tests/$f +# done diff --git a/util/gnu-patches/series b/util/gnu-patches/series index 2d9b30b2c..f1a24d0f6 100644 --- a/util/gnu-patches/series +++ b/util/gnu-patches/series @@ -10,4 +10,3 @@ tests_sort_merge.pl.patch tests_du_move_dir_while_traversing.patch test_mkdir_restorecon.patch error_msg_uniq.diff -tests_tail_overlay_headers.patch diff --git a/util/gnu-patches/tests_tail_overlay_headers.patch b/util/gnu-patches/tests_tail_overlay_headers.patch index 205401294..c01b718ca 100644 --- a/util/gnu-patches/tests_tail_overlay_headers.patch +++ b/util/gnu-patches/tests_tail_overlay_headers.patch @@ -31,19 +31,3 @@ -retry_delay_ wait4lines_ .1 6 13 || fail=1 +# Wait for 8 more lines (should total 13) +retry_delay_ wait4lines_ .1 6 13 || { echo "Failed waiting for 13 total lines"; fail=1; } - - kill $sleep && wait || framework_failure_ - --test "$(countlines_)" = 13 || fail=1 -+final_count=$(countlines_) -+echo "=== Final line count: $final_count (expected 13) ===" -+ -+if test "$final_count" != 13; then -+ echo "=== FAILURE: Expected 13 lines, got $final_count ===" -+ echo "=== Full output content: ===" -+ cat -A out -+ echo "=== End output content ===" -+ fail=1 -+fi - - Exit $fail From cac4a63ac439f2b183803a52e6e650ad8fefef5c Mon Sep 17 00:00:00 2001 From: Christopher Dryden Date: Sat, 7 Feb 2026 17:44:22 +0000 Subject: [PATCH 22/46] tac: fix stdin piping on Windows by skipping empty mmap results --- src/uu/tac/src/tac.rs | 6 +++++- tests/by-util/test_tac.rs | 27 --------------------------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index ec8ae4503..164cd58aa 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -411,7 +411,11 @@ fn tac(filenames: &[OsString], before: bool, regex: bool, separator: &str) -> UR fn try_mmap_stdin() -> Option { // SAFETY: If the file is truncated while we map it, SIGBUS will be raised // and our process will be terminated, thus preventing access of invalid memory. - unsafe { Mmap::map(&stdin()).ok() } + let mmap = unsafe { Mmap::map(&stdin()).ok()? }; + // On Windows, mmap on a pipe handle can "succeed" but return 0 bytes + // (the file size of a pipe is reported as 0). When that happens, return + // None so we fall through to buffer_stdin() which reads the pipe properly. + if mmap.is_empty() { None } else { Some(mmap) } } enum StdinData { diff --git a/tests/by-util/test_tac.rs b/tests/by-util/test_tac.rs index 1fc42d1c8..dee019677 100644 --- a/tests/by-util/test_tac.rs +++ b/tests/by-util/test_tac.rs @@ -29,8 +29,6 @@ fn test_invalid_arg() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_stdin_default() { new_ucmd!() .pipe_in("100\n200\n300\n400\n500") @@ -39,8 +37,6 @@ fn test_stdin_default() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_stdin_non_newline_separator() { new_ucmd!() .args(&["-s", ":"]) @@ -50,8 +46,6 @@ fn test_stdin_non_newline_separator() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_stdin_non_newline_separator_before() { new_ucmd!() .args(&["-b", "-s", ":"]) @@ -104,14 +98,11 @@ fn test_invalid_input() { } #[test] -#[cfg(not(windows))] // FIXME: https://github.com/uutils/coreutils/issues/4204 fn test_no_line_separators() { new_ucmd!().pipe_in("a").succeeds().stdout_is("a"); } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_before_trailing_separator_no_leading_separator() { new_ucmd!() .arg("-b") @@ -121,8 +112,6 @@ fn test_before_trailing_separator_no_leading_separator() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_before_trailing_separator_and_leading_separator() { new_ucmd!() .arg("-b") @@ -132,8 +121,6 @@ fn test_before_trailing_separator_and_leading_separator() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_before_leading_separator_no_trailing_separator() { new_ucmd!() .arg("-b") @@ -143,8 +130,6 @@ fn test_before_leading_separator_no_trailing_separator() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_before_no_separator() { new_ucmd!() .arg("-b") @@ -154,15 +139,11 @@ fn test_before_no_separator() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_before_empty_file() { new_ucmd!().arg("-b").pipe_in("").succeeds().stdout_is(""); } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_multi_char_separator() { new_ucmd!() .args(&["-s", "xx"]) @@ -204,8 +185,6 @@ fn test_multi_char_separator_overlap() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_multi_char_separator_overlap_before() { // With the "-b" option, the line separator is assumed to be at the // beginning of the line. In this case, That is, "axxx" is @@ -248,8 +227,6 @@ fn test_multi_char_separator_overlap_before() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_null_separator() { new_ucmd!() .args(&["-s", ""]) @@ -259,8 +236,6 @@ fn test_null_separator() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_regex() { new_ucmd!() .args(&["-r", "-s", "[xyz]+"]) @@ -289,8 +264,6 @@ fn test_regex() { } #[test] -// FIXME: See https://github.com/uutils/coreutils/issues/4204 -#[cfg(not(windows))] fn test_regex_before() { new_ucmd!() .args(&["-b", "-r", "-s", "[xyz]+"]) From 280abcd61906e99f924802ce73b7353aa99e72c4 Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Fri, 6 Feb 2026 15:28:49 +0100 Subject: [PATCH 23/46] uucore: Name Blake2b struct fields, move `new()` function out of Digest trait --- src/uucore/src/lib/features/checksum/mod.rs | 5 +- src/uucore/src/lib/features/sum.rs | 80 ++++++++++++--------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/src/uucore/src/lib/features/checksum/mod.rs b/src/uucore/src/lib/features/checksum/mod.rs index a2fba209c..5cbcb0ae3 100644 --- a/src/uucore/src/lib/features/checksum/mod.rs +++ b/src/uucore/src/lib/features/checksum/mod.rs @@ -338,8 +338,9 @@ impl SizedAlgoKind { Self::Sha3(Len256) => Box::new(Sha3_256::new()), Self::Sha3(Len384) => Box::new(Sha3_384::new()), Self::Sha3(Len512) => Box::new(Sha3_512::new()), - Self::Blake2b(Some(byte_len)) => Box::new(Blake2b::with_output_bytes(*byte_len)), - Self::Blake2b(None) => Box::new(Blake2b::new()), + Self::Blake2b(len_opt) => Box::new(Blake2b::with_output_bytes( + len_opt.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE), + )), Self::Shake128(_) => Box::new(Shake128::new()), Self::Shake256(_) => Box::new(Shake256::new()), } diff --git a/src/uucore/src/lib/features/sum.rs b/src/uucore/src/lib/features/sum.rs index 5272ab3c1..8bce7acb6 100644 --- a/src/uucore/src/lib/features/sum.rs +++ b/src/uucore/src/lib/features/sum.rs @@ -59,9 +59,6 @@ impl DigestOutput { } pub trait Digest { - fn new() -> Self - where - Self: Sized; fn hash_update(&mut self, input: &[u8]); fn hash_finalize(&mut self, out: &mut [u8]); fn reset(&mut self); @@ -79,31 +76,34 @@ pub trait Digest { /// first element of the tuple is the blake2b state /// second is the number of output bits -pub struct Blake2b(blake2b_simd::State, usize); +pub struct Blake2b { + digest: blake2b_simd::State, + bit_size: usize, +} impl Blake2b { + pub const DEFAULT_BYTE_SIZE: usize = 64; + /// Return a new Blake2b instance with a custom output bytes length pub fn with_output_bytes(output_bytes: usize) -> Self { let mut params = blake2b_simd::Params::new(); params.hash_length(output_bytes); let state = params.to_state(); - Self(state, output_bytes * 8) + Self { + digest: state, + bit_size: output_bytes * 8, + } } } impl Digest for Blake2b { - fn new() -> Self { - // by default, Blake2b output is 512 bits long (= 64B) - Self::with_output_bytes(64) - } - fn hash_update(&mut self, input: &[u8]) { - self.0.update(input); + self.digest.update(input); } fn hash_finalize(&mut self, out: &mut [u8]) { - let hash_result = &self.0.finalize(); + let hash_result = &self.digest.finalize(); out.copy_from_slice(hash_result.as_bytes()); } @@ -112,16 +112,19 @@ impl Digest for Blake2b { } fn output_bits(&self) -> usize { - self.1 + self.bit_size } } pub struct Blake3(blake3::Hasher); -impl Digest for Blake3 { - fn new() -> Self { + +impl Blake3 { + pub fn new() -> Self { Self(blake3::Hasher::new()) } +} +impl Digest for Blake3 { fn hash_update(&mut self, input: &[u8]) { self.0.update(input); } @@ -141,11 +144,14 @@ impl Digest for Blake3 { } pub struct Sm3(sm3::Sm3); -impl Digest for Sm3 { - fn new() -> Self { + +impl Sm3 { + pub fn new() -> Self { Self(::new()) } +} +impl Digest for Sm3 { fn hash_update(&mut self, input: &[u8]) { ::update(&mut self.0, input); } @@ -182,16 +188,16 @@ impl Crc { 0, // Check value (not used) ) } -} -impl Digest for Crc { - fn new() -> Self { + pub fn new() -> Self { Self { digest: crc_fast::Digest::new_with_params(Self::get_posix_cksum_params()), size: 0, } } +} +impl Digest for Crc { fn hash_update(&mut self, input: &[u8]) { self.digest.update(input); self.size += input.len(); @@ -230,13 +236,15 @@ pub struct CRC32B { digest: crc_fast::Digest, } -impl Digest for CRC32B { - fn new() -> Self { +impl CRC32B { + pub fn new() -> Self { Self { digest: crc_fast::Digest::new(crc_fast::CrcAlgorithm::Crc32IsoHdlc), } } +} +impl Digest for CRC32B { fn hash_update(&mut self, input: &[u8]) { self.digest.update(input); } @@ -267,11 +275,14 @@ impl Digest for CRC32B { pub struct Bsd { state: u16, } -impl Digest for Bsd { - fn new() -> Self { + +impl Bsd { + pub fn new() -> Self { Self { state: 0 } } +} +impl Digest for Bsd { fn hash_update(&mut self, input: &[u8]) { for &byte in input { self.state = (self.state >> 1) + ((self.state & 1) << 15); @@ -301,11 +312,14 @@ impl Digest for Bsd { pub struct SysV { state: u32, } -impl Digest for SysV { - fn new() -> Self { + +impl SysV { + pub fn new() -> Self { Self { state: 0 } } +} +impl Digest for SysV { fn hash_update(&mut self, input: &[u8]) { for &byte in input { self.state = self.state.wrapping_add(u32::from(byte)); @@ -336,11 +350,12 @@ impl Digest for SysV { // Implements the Digest trait for sha2 / sha3 algorithms with fixed output macro_rules! impl_digest_common { ($algo_type: ty, $size: literal) => { - impl Digest for $algo_type { - fn new() -> Self { + impl $algo_type { + pub fn new() -> Self { Self(Default::default()) } - + } + impl Digest for $algo_type { fn hash_update(&mut self, input: &[u8]) { digest::Digest::update(&mut self.0, input); } @@ -363,11 +378,12 @@ macro_rules! impl_digest_common { // Implements the Digest trait for sha2 / sha3 algorithms with variable output macro_rules! impl_digest_shake { ($algo_type: ty, $output_bits: literal) => { - impl Digest for $algo_type { - fn new() -> Self { + impl $algo_type { + pub fn new() -> Self { Self(Default::default()) } - + } + impl Digest for $algo_type { fn hash_update(&mut self, input: &[u8]) { digest::Update::update(&mut self.0, input); } From 690b149b4b7103a04b5f554df7515f9a61f7d67c Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Fri, 6 Feb 2026 15:55:04 +0100 Subject: [PATCH 24/46] uucore: Make Shake* algorithms optionally accept a length --- src/uu/cksum/benches/cksum_bench.rs | 4 +- src/uu/cksum/src/cksum.rs | 10 +++++ src/uucore/src/lib/features/checksum/mod.rs | 47 ++++++++++++--------- src/uucore/src/lib/features/sum.rs | 35 +++++++++++---- 4 files changed, 64 insertions(+), 32 deletions(-) diff --git a/src/uu/cksum/benches/cksum_bench.rs b/src/uu/cksum/benches/cksum_bench.rs index c316ec274..81f70bbb3 100644 --- a/src/uu/cksum/benches/cksum_bench.rs +++ b/src/uu/cksum/benches/cksum_bench.rs @@ -57,7 +57,7 @@ macro_rules! bench_shake_algorithm { let data = text_data::generate_by_size(100, 80); bencher.bench(|| { - let mut shake = Shake128::new(); + let mut shake = Shake128::with_output_bits(256); shake.hash_update(&data); // SHAKE algorithms can output any length, use 256 bits (32 bytes) for meaningful comparison @@ -76,7 +76,7 @@ macro_rules! bench_shake_algorithm { let data = text_data::generate_by_size(100, 80); bencher.bench(|| { - let mut shake = Shake256::new(); + let mut shake = Shake256::with_output_bits(512); shake.hash_update(&data); // SHAKE algorithms can output any length, use 256 bits (32 bytes) for meaningful comparison diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 5c78af2c6..51dd83d3a 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -100,6 +100,16 @@ fn maybe_sanitize_length( sanitize_sha2_sha3_length_str(algo, s_len).map(Some) } + // SHAKE128 and SHAKE256 algorithms optionally take a bit length. No + // validation is performed on this length, any value is valid. If the + // given length is not a multiple of 8, the last byte of the output + // will have its extra bits set to zero. + (Some(AlgoKind::Shake128 | AlgoKind::Shake256), Some(len)) => match len.parse::() { + Ok(0) => Ok(None), + Ok(l) => Ok(Some(l)), + Err(_) => Err(ChecksumError::InvalidLength(len.into()).into()), + }, + // For BLAKE2b, if a length is provided, validate it. (Some(AlgoKind::Blake2b), Some(len)) => calculate_blake2b_length_str(len), diff --git a/src/uucore/src/lib/features/checksum/mod.rs b/src/uucore/src/lib/features/checksum/mod.rs index 5cbcb0ae3..09e6d6344 100644 --- a/src/uucore/src/lib/features/checksum/mod.rs +++ b/src/uucore/src/lib/features/checksum/mod.rs @@ -114,6 +114,9 @@ impl AlgoKind { ALGORITHM_OPTIONS_SHA256 => Sha256, ALGORITHM_OPTIONS_SHA384 => Sha384, ALGORITHM_OPTIONS_SHA512 => Sha512, + + ALGORITHM_OPTIONS_SHAKE128 => Shake128, + ALGORITHM_OPTIONS_SHAKE256 => Shake256, _ => return Err(ChecksumError::UnknownAlgorithm(algo.as_ref().to_string()).into()), }) } @@ -247,8 +250,8 @@ pub enum SizedAlgoKind { Sha3(ShaLength), // Note: we store Blake2b's length as BYTES. Blake2b(Option), - Shake128(usize), - Shake256(usize), + Shake128(Option), + Shake256(Option), } impl SizedAlgoKind { @@ -280,8 +283,8 @@ impl SizedAlgoKind { (ak::Sha1, _) => Ok(Self::Sha1), (ak::Blake3, _) => Ok(Self::Blake3), - (ak::Shake128, Some(l)) => Ok(Self::Shake128(l)), - (ak::Shake256, Some(l)) => Ok(Self::Shake256(l)), + (ak::Shake128, l) => Ok(Self::Shake128(l)), + (ak::Shake256, l) => Ok(Self::Shake256(l)), (ak::Sha2, Some(l)) => Ok(Self::Sha2(ShaLength::try_from(l)?)), (ak::Sha3, Some(l)) => Ok(Self::Sha3(ShaLength::try_from(l)?)), (algo @ (ak::Sha2 | ak::Sha3), None) => { @@ -298,7 +301,6 @@ impl SizedAlgoKind { (ak::Sha256, None) => Ok(Self::Sha2(ShaLength::Len256)), (ak::Sha384, None) => Ok(Self::Sha2(ShaLength::Len384)), (ak::Sha512, None) => Ok(Self::Sha2(ShaLength::Len512)), - (_, None) => Err(ChecksumError::LengthRequired(kind.to_uppercase().into()).into()), } } @@ -341,27 +343,30 @@ impl SizedAlgoKind { Self::Blake2b(len_opt) => Box::new(Blake2b::with_output_bytes( len_opt.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE), )), - Self::Shake128(_) => Box::new(Shake128::new()), - Self::Shake256(_) => Box::new(Shake256::new()), + Self::Shake128(len_opt) => Box::new(Shake128::with_output_bits( + len_opt.unwrap_or(Shake128::DEFAULT_BIT_SIZE), + )), + Self::Shake256(len_opt) => Box::new(Shake256::with_output_bits( + len_opt.unwrap_or(Shake256::DEFAULT_BIT_SIZE), + )), } } pub fn bitlen(&self) -> usize { - use SizedAlgoKind::*; match self { - Sysv => 512, - Bsd => 1024, - Crc => 256, - Crc32b => 32, - Md5 => 128, - Sm3 => 512, - Sha1 => 160, - Blake3 => 256, - Sha2(len) => len.as_usize(), - Sha3(len) => len.as_usize(), - Blake2b(len) => len.unwrap_or(512), - Shake128(len) => *len, - Shake256(len) => *len, + Self::Sysv => 512, + Self::Bsd => 1024, + Self::Crc => 256, + Self::Crc32b => 32, + Self::Md5 => 128, + Self::Sm3 => 512, + Self::Sha1 => 160, + Self::Blake3 => 256, + Self::Sha2(len) => len.as_usize(), + Self::Sha3(len) => len.as_usize(), + Self::Blake2b(len) => len.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE * 8), + Self::Shake128(len) => len.unwrap_or(Shake128::DEFAULT_BIT_SIZE), + Self::Shake256(len) => len.unwrap_or(Shake256::DEFAULT_BIT_SIZE), } } pub fn is_legacy(&self) -> bool { diff --git a/src/uucore/src/lib/features/sum.rs b/src/uucore/src/lib/features/sum.rs index 8bce7acb6..cc4244362 100644 --- a/src/uucore/src/lib/features/sum.rs +++ b/src/uucore/src/lib/features/sum.rs @@ -377,27 +377,38 @@ macro_rules! impl_digest_common { // Implements the Digest trait for sha2 / sha3 algorithms with variable output macro_rules! impl_digest_shake { - ($algo_type: ty, $output_bits: literal) => { + ($algo_type: ty, $default_output_bits: literal) => { impl $algo_type { - pub fn new() -> Self { - Self(Default::default()) + pub const DEFAULT_BIT_SIZE: usize = $default_output_bits; + + pub fn with_output_bits(bits: usize) -> Self { + Self { + digest: Default::default(), + bit_size: bits, + } } } impl Digest for $algo_type { fn hash_update(&mut self, input: &[u8]) { - digest::Update::update(&mut self.0, input); + digest::Update::update(&mut self.digest, input); } fn hash_finalize(&mut self, out: &mut [u8]) { - digest::ExtendableOutputReset::finalize_xof_reset_into(&mut self.0, out); + digest::ExtendableOutputReset::finalize_xof_reset_into(&mut self.digest, out); + + // Remove the last bits if the requested length is not a multiple of 8. + let extra = self.output_bits() % 8; + if extra != 0 { + out[out.len() - 1] &= (1 << extra) - 1; + } } fn reset(&mut self) { - *self = Self::new(); + *self = Self::with_output_bits(self.bit_size); } fn output_bits(&self) -> usize { - $output_bits + self.bit_size } fn result(&mut self) -> DigestOutput { @@ -431,8 +442,14 @@ impl_digest_common!(Sha3_256, 256); impl_digest_common!(Sha3_384, 384); impl_digest_common!(Sha3_512, 512); -pub struct Shake128(sha3::Shake128); -pub struct Shake256(sha3::Shake256); +pub struct Shake128 { + digest: sha3::Shake128, + bit_size: usize, +} +pub struct Shake256 { + digest: sha3::Shake256, + bit_size: usize, +} impl_digest_shake!(Shake128, 256); impl_digest_shake!(Shake256, 512); From 4bb60ac754ff26fd90adb2936c737bea0bcb56eb Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Fri, 6 Feb 2026 18:26:11 +0100 Subject: [PATCH 25/46] uucore: Fix `new_without_default` clippy lint --- src/uucore/src/lib/features/checksum/mod.rs | 50 ++++++------- src/uucore/src/lib/features/sum.rs | 83 ++++++++++----------- 2 files changed, 63 insertions(+), 70 deletions(-) diff --git a/src/uucore/src/lib/features/checksum/mod.rs b/src/uucore/src/lib/features/checksum/mod.rs index 09e6d6344..13b0680df 100644 --- a/src/uucore/src/lib/features/checksum/mod.rs +++ b/src/uucore/src/lib/features/checksum/mod.rs @@ -324,31 +324,31 @@ impl SizedAlgoKind { pub fn create_digest(&self) -> Box { use ShaLength::*; match self { - Self::Sysv => Box::new(SysV::new()), - Self::Bsd => Box::new(Bsd::new()), - Self::Crc => Box::new(Crc::new()), - Self::Crc32b => Box::new(CRC32B::new()), - Self::Md5 => Box::new(Md5::new()), - Self::Sm3 => Box::new(Sm3::new()), - Self::Sha1 => Box::new(Sha1::new()), - Self::Blake3 => Box::new(Blake3::new()), - Self::Sha2(Len224) => Box::new(Sha224::new()), - Self::Sha2(Len256) => Box::new(Sha256::new()), - Self::Sha2(Len384) => Box::new(Sha384::new()), - Self::Sha2(Len512) => Box::new(Sha512::new()), - Self::Sha3(Len224) => Box::new(Sha3_224::new()), - Self::Sha3(Len256) => Box::new(Sha3_256::new()), - Self::Sha3(Len384) => Box::new(Sha3_384::new()), - Self::Sha3(Len512) => Box::new(Sha3_512::new()), - Self::Blake2b(len_opt) => Box::new(Blake2b::with_output_bytes( - len_opt.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE), - )), - Self::Shake128(len_opt) => Box::new(Shake128::with_output_bits( - len_opt.unwrap_or(Shake128::DEFAULT_BIT_SIZE), - )), - Self::Shake256(len_opt) => Box::new(Shake256::with_output_bits( - len_opt.unwrap_or(Shake256::DEFAULT_BIT_SIZE), - )), + Self::Sysv => Box::new(SysV::default()), + Self::Bsd => Box::new(Bsd::default()), + Self::Crc => Box::new(Crc::default()), + Self::Crc32b => Box::new(CRC32B::default()), + Self::Md5 => Box::new(Md5::default()), + Self::Sm3 => Box::new(Sm3::default()), + Self::Sha1 => Box::new(Sha1::default()), + Self::Blake3 => Box::new(Blake3::default()), + Self::Sha2(Len224) => Box::new(Sha224::default()), + Self::Sha2(Len256) => Box::new(Sha256::default()), + Self::Sha2(Len384) => Box::new(Sha384::default()), + Self::Sha2(Len512) => Box::new(Sha512::default()), + Self::Sha3(Len224) => Box::new(Sha3_224::default()), + Self::Sha3(Len256) => Box::new(Sha3_256::default()), + Self::Sha3(Len384) => Box::new(Sha3_384::default()), + Self::Sha3(Len512) => Box::new(Sha3_512::default()), + Self::Blake2b(len_opt) => { + Box::new(len_opt.map(Blake2b::with_output_bytes).unwrap_or_default()) + } + Self::Shake128(len_opt) => { + Box::new(len_opt.map(Shake128::with_output_bits).unwrap_or_default()) + } + Self::Shake256(len_opt) => { + Box::new(len_opt.map(Shake256::with_output_bits).unwrap_or_default()) + } } } diff --git a/src/uucore/src/lib/features/sum.rs b/src/uucore/src/lib/features/sum.rs index cc4244362..279643a96 100644 --- a/src/uucore/src/lib/features/sum.rs +++ b/src/uucore/src/lib/features/sum.rs @@ -97,6 +97,12 @@ impl Blake2b { } } +impl Default for Blake2b { + fn default() -> Self { + Self::with_output_bytes(Self::DEFAULT_BYTE_SIZE) + } +} + impl Digest for Blake2b { fn hash_update(&mut self, input: &[u8]) { self.digest.update(input); @@ -116,14 +122,9 @@ impl Digest for Blake2b { } } +#[derive(Default)] pub struct Blake3(blake3::Hasher); -impl Blake3 { - pub fn new() -> Self { - Self(blake3::Hasher::new()) - } -} - impl Digest for Blake3 { fn hash_update(&mut self, input: &[u8]) { self.0.update(input); @@ -135,7 +136,7 @@ impl Digest for Blake3 { } fn reset(&mut self) { - *self = Self::new(); + *self = Self::default(); } fn output_bits(&self) -> usize { @@ -143,14 +144,9 @@ impl Digest for Blake3 { } } +#[derive(Default)] pub struct Sm3(sm3::Sm3); -impl Sm3 { - pub fn new() -> Self { - Self(::new()) - } -} - impl Digest for Sm3 { fn hash_update(&mut self, input: &[u8]) { ::update(&mut self.0, input); @@ -161,7 +157,7 @@ impl Digest for Sm3 { } fn reset(&mut self) { - *self = Self::new(); + *self = Self::default(); } fn output_bits(&self) -> usize { @@ -188,8 +184,10 @@ impl Crc { 0, // Check value (not used) ) } +} - pub fn new() -> Self { +impl Default for Crc { + fn default() -> Self { Self { digest: crc_fast::Digest::new_with_params(Self::get_posix_cksum_params()), size: 0, @@ -236,8 +234,8 @@ pub struct CRC32B { digest: crc_fast::Digest, } -impl CRC32B { - pub fn new() -> Self { +impl Default for CRC32B { + fn default() -> Self { Self { digest: crc_fast::Digest::new(crc_fast::CrcAlgorithm::Crc32IsoHdlc), } @@ -272,16 +270,11 @@ impl Digest for CRC32B { } } +#[derive(Default)] pub struct Bsd { state: u16, } -impl Bsd { - pub fn new() -> Self { - Self { state: 0 } - } -} - impl Digest for Bsd { fn hash_update(&mut self, input: &[u8]) { for &byte in input { @@ -301,7 +294,7 @@ impl Digest for Bsd { } fn reset(&mut self) { - *self = Self::new(); + *self = Self::default(); } fn output_bits(&self) -> usize { @@ -309,16 +302,11 @@ impl Digest for Bsd { } } +#[derive(Default)] pub struct SysV { state: u32, } -impl SysV { - pub fn new() -> Self { - Self { state: 0 } - } -} - impl Digest for SysV { fn hash_update(&mut self, input: &[u8]) { for &byte in input { @@ -339,7 +327,7 @@ impl Digest for SysV { } fn reset(&mut self) { - *self = Self::new(); + *self = Self::default(); } fn output_bits(&self) -> usize { @@ -350,8 +338,8 @@ impl Digest for SysV { // Implements the Digest trait for sha2 / sha3 algorithms with fixed output macro_rules! impl_digest_common { ($algo_type: ty, $size: literal) => { - impl $algo_type { - pub fn new() -> Self { + impl Default for $algo_type { + fn default() -> Self { Self(Default::default()) } } @@ -365,7 +353,7 @@ macro_rules! impl_digest_common { } fn reset(&mut self) { - *self = Self::new(); + *self = Self::default(); } fn output_bits(&self) -> usize { @@ -388,6 +376,11 @@ macro_rules! impl_digest_shake { } } } + impl Default for $algo_type { + fn default() -> Self { + Self::with_output_bits(Self::DEFAULT_BIT_SIZE) + } + } impl Digest for $algo_type { fn hash_update(&mut self, input: &[u8]) { digest::Update::update(&mut self.digest, input); @@ -600,8 +593,8 @@ mod tests { #[test] fn test_crc_basic_functionality() { // Test that our CRC implementation works with basic functionality - let mut crc1 = Crc::new(); - let mut crc2 = Crc::new(); + let mut crc1 = Crc::default(); + let mut crc2 = Crc::default(); // Same input should give same output crc1.hash_update(b"test"); @@ -617,7 +610,7 @@ mod tests { #[test] fn test_crc_digest_basic() { - let mut crc = Crc::new(); + let mut crc = Crc::default(); // Test empty input let mut output = [0u8; 8]; @@ -625,7 +618,7 @@ mod tests { let empty_result = u64::from_ne_bytes(output); // Reset and test with "test" string - let mut crc = Crc::new(); + let mut crc = Crc::default(); crc.hash_update(b"test"); crc.hash_finalize(&mut output); let test_result = u64::from_ne_bytes(output); @@ -639,8 +632,8 @@ mod tests { #[test] fn test_crc_digest_incremental() { - let mut crc1 = Crc::new(); - let mut crc2 = Crc::new(); + let mut crc1 = Crc::default(); + let mut crc2 = Crc::default(); // Test that processing in chunks gives same result as all at once let data = b"Hello, World! This is a test string for CRC computation."; @@ -665,13 +658,13 @@ mod tests { // Test that our optimized slice-by-8 gives same results as byte-by-byte let test_data = b"This is a longer test string to verify slice-by-8 optimization works correctly with various data sizes including remainders."; - let mut crc_optimized = Crc::new(); + let mut crc_optimized = Crc::default(); crc_optimized.hash_update(test_data); let mut output_opt = [0u8; 8]; crc_optimized.hash_finalize(&mut output_opt); // Create a reference implementation using hash_update - let mut crc_reference = Crc::new(); + let mut crc_reference = Crc::default(); for &byte in test_data { crc_reference.hash_update(&[byte]); } @@ -692,7 +685,7 @@ mod tests { ]; for (input, expected) in test_cases { - let mut crc = Crc::new(); + let mut crc = Crc::default(); crc.hash_update(input.as_bytes()); let mut output = [0u8; 8]; crc.hash_finalize(&mut output); @@ -704,14 +697,14 @@ mod tests { #[test] fn test_crc_hash_update_edge_cases() { - let mut crc = Crc::new(); + let mut crc = Crc::default(); // Test with data that's not a multiple of 8 bytes let data7 = b"1234567"; // 7 bytes crc.hash_update(data7); let data9 = b"123456789"; // 9 bytes - let mut crc2 = Crc::new(); + let mut crc2 = Crc::default(); crc2.hash_update(data9); // Should not panic and should produce valid results From 756a7777403a4eabfb7f767ecb3129e84de33df5 Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Sat, 7 Feb 2026 14:00:13 +0100 Subject: [PATCH 26/46] test(cksum): Add tests for SHAKE128|256 --- tests/by-util/test_cksum.rs | 128 ++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index daa69047f..fac6ab679 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -4,6 +4,8 @@ // file that was distributed with this source code. // spell-checker:ignore (words) asdf algo algos asha mgmt xffname hexa GFYEQ HYQK Yqxb dont checkfile +use rstest::rstest; + use uutests::at_and_ucmd; use uutests::new_ucmd; use uutests::util::TestScenario; @@ -3098,3 +3100,129 @@ fn test_check_checkfile_with_io_error() { .stderr_contains("/proc/self/mem: read error") .no_stdout(); } + +#[rstest] +#[case::default_length( + &[], + "ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdf" +)] +#[case::pass_default_length( + &["-l", "256"], + "ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdf" +)] +#[case::smaller_length( + &["-l", "128"], + "ac8549b2861a151896ab721bd29d7a20" +)] +#[case::bigger_length( + &["-l", "264"], + "ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdfc2" +)] +#[case::length_0( + &["-l", "0"], + "ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdf" +)] +#[case::length_1( + &["-l", "1"], + "00" +)] +#[case::length_2( + &["-l", "2"], + "00" +)] +#[case::length_3( + &["-l", "3"], + "04" +)] +#[case::length_4( + &["-l", "4"], + "0c" +)] +#[case::length_5( + &["-l", "5"], + "0c" +)] +#[case::length_6( + &["-l", "6"], + "2c" +)] +#[case::length_7( + &["-l", "7"], + "2c" +)] +#[case::length_8( + &["-l", "8"], + "ac" +)] +fn test_shake128(#[case] args: &[&str], #[case] expected: &str) { + new_ucmd!() + .arg("-a") + .arg("shake128") + .args(args) + .pipe_in("xxx") + .succeeds() + .stdout_only(format!("SHAKE128 (-) = {expected}\n")); +} + +#[rstest] +#[case::default_length( + &[], + "2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba" +)] +#[case::pass_default_length( + &["-l", "512"], + "2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba" +)] +#[case::smaller_length( + &["-l", "128"], + "2fa631503c3ea5fe85131dbfa2480518" +)] +#[case::bigger_length( + &["-l", "1024"], + "2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba18783d25f8b36d92b8607f016352b5c405945a7859a8339201728f680647324d1b8ea93a01d2ef965dadf4a1bee3ff044ed2b4bd95e4311f5e3f2cd5bae0b7c6" +)] +#[case::length_0( + &["-l", "0"], + "2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba" +)] +#[case::length_1( + &["-l", "1"], + "01" +)] +#[case::length_2( + &["-l", "2"], + "03" +)] +#[case::length_3( + &["-l", "3"], + "07" +)] +#[case::length_4( + &["-l", "4"], + "0f" +)] +#[case::length_5( + &["-l", "5"], + "0f" +)] +#[case::length_6( + &["-l", "6"], + "2f" +)] +#[case::length_7( + &["-l", "7"], + "2f" +)] +#[case::length_8( + &["-l", "8"], + "2f" +)] +fn test_shake256(#[case] args: &[&str], #[case] expected: &str) { + new_ucmd!() + .arg("-a") + .arg("shake256") + .args(args) + .pipe_in("xxx") + .succeeds() + .stdout_only(format!("SHAKE256 (-) = {expected}\n")); +} From 52a5b48bcf1f4b4b6639c7c1ce30b8d058861513 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 8 Feb 2026 10:08:35 +0000 Subject: [PATCH 27/46] sort: making ClosedCompressedTmpFile::reopen() not panic. (#10807) Even tough for the panic to happen, we would need specific contexts, we mirror what is done for ClosedPlainTmpFile to make it smoother. --- src/uu/sort/src/merge.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/uu/sort/src/merge.rs b/src/uu/sort/src/merge.rs index 39465827e..d2bb3056b 100644 --- a/src/uu/sort/src/merge.rs +++ b/src/uu/sort/src/merge.rs @@ -548,7 +548,9 @@ impl ClosedTmpFile for ClosedCompressedTmpFile { fn reopen(self) -> UResult { let mut command = Command::new(&self.compress_prog); - let file = File::open(&self.path).unwrap(); + // mirroring what is done for ClosedPlainTmpFile + let file = + File::open(&self.path).map_err(|error| SortError::OpenTmpFileFailed { error })?; command.stdin(file).stdout(Stdio::piped()).arg("-d"); let mut child = command .spawn() From 2a4573326f8bcafa64282fa1c3a9986d5c7a8500 Mon Sep 17 00:00:00 2001 From: Chris Dryden Date: Sun, 8 Feb 2026 05:13:17 -0500 Subject: [PATCH 28/46] Remove wincode and wincode-derive dependencies (#10802) --- Cargo.lock | 73 ------------------------------------ Cargo.toml | 7 +--- tests/by-util/test_uptime.rs | 39 ++++++++++++++----- 3 files changed, 31 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0436afde6..e517cd19e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -667,8 +667,6 @@ dependencies = [ "uucore", "uutests", "walkdir", - "wincode", - "wincode-derive", "zip", ] @@ -812,41 +810,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core", - "quote", - "syn", -] - [[package]] name = "data-encoding" version = "2.10.0" @@ -1605,12 +1568,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e10b0e5e87a2c84bd5fa407705732052edebe69291d347d0c3033785470edbf" -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "indexmap" version = "2.13.0" @@ -2200,12 +2157,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "pastey" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" - [[package]] name = "phf" version = "0.13.1" @@ -4648,30 +4599,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "wincode" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f27315388539620cef3386d77b05305dbfb7fccbd32eead533a7a3779313532" -dependencies = [ - "pastey", - "proc-macro2", - "quote", - "thiserror 2.0.18", -] - -[[package]] -name = "wincode-derive" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89833c7743abd1a831b7eb0fdfd7210bd8bedc297435e9426aad9b63d7c7f77" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-core" version = "0.62.2" diff --git a/Cargo.toml b/Cargo.toml index bf6a17121..dfa88f5f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ # coreutils (uutils) # * see the repository LICENSE, README, and CONTRIBUTING files for more information -# spell-checker:ignore (libs) bigdecimal datetime serde wincode gethostid kqueue libselinux mangen memmap uuhelp startswith constness expl unnested logind cfgs interner +# spell-checker:ignore (libs) bigdecimal datetime serde gethostid kqueue libselinux mangen memmap uuhelp startswith constness expl unnested logind cfgs interner [package] name = "coreutils" @@ -587,11 +587,6 @@ nix = { workspace = true, features = [ ] } rlimit = { workspace = true } -# Used in test_uptime::test_uptime_with_file_containing_valid_boot_time_utmpx_record -# to deserialize an utmpx struct into a binary file -[target.'cfg(all(target_family= "unix",not(target_os = "macos")))'.dev-dependencies] -wincode = "0.3.1" -wincode-derive = "0.3.1" [build-dependencies] phf_codegen.workspace = true diff --git a/tests/by-util/test_uptime.rs b/tests/by-util/test_uptime.rs index 42111834b..cf39eb8b2 100644 --- a/tests/by-util/test_uptime.rs +++ b/tests/by-util/test_uptime.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // -// spell-checker:ignore wincode serde utmp runlevel testusr testx boottime +// spell-checker:ignore utmp runlevel testusr testx boottime #![allow(clippy::cast_possible_wrap, clippy::unreadable_literal)] use uutests::{at_and_ucmd, new_ucmd}; @@ -97,8 +97,6 @@ fn test_uptime_with_non_existent_file() { fn test_uptime_with_file_containing_valid_boot_time_utmpx_record() { use std::fs::File; use std::{io::Write, path::PathBuf}; - use wincode::serialize; - use wincode_derive::SchemaWrite; // This test will pass for freebsd but we currently don't support changing the utmpx file for // freebsd. @@ -132,21 +130,18 @@ fn test_uptime_with_file_containing_valid_boot_time_utmpx_record() { const RUN_LVL: i32 = 1; const USER_PROCESS: i32 = 7; - #[derive(SchemaWrite)] #[repr(C)] pub struct TimeVal { pub tv_sec: i32, pub tv_usec: i32, } - #[derive(SchemaWrite)] #[repr(C)] pub struct ExitStatus { e_termination: i16, e_exit: i16, } - #[derive(SchemaWrite)] #[repr(C, align(4))] pub struct Utmp { pub ut_type: i32, @@ -222,9 +217,35 @@ fn test_uptime_with_file_containing_valid_boot_time_utmpx_record() { glibc_reserved: [0; 20], }; - let mut buf = serialize(&utmp).unwrap(); - buf.append(&mut serialize(&utmp1).unwrap()); - buf.append(&mut serialize(&utmp2).unwrap()); + fn serialize_i8_arr(buf: &mut Vec, arr: &[i8]) { + for b in arr { + buf.push(*b as u8); + } + } + + fn serialize(utmp: &Utmp) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&utmp.ut_type.to_ne_bytes()); + buf.extend_from_slice(&utmp.ut_pid.to_ne_bytes()); + serialize_i8_arr(&mut buf, &utmp.ut_line); + serialize_i8_arr(&mut buf, &utmp.ut_id); + serialize_i8_arr(&mut buf, &utmp.ut_user); + serialize_i8_arr(&mut buf, &utmp.ut_host); + buf.extend_from_slice(&utmp.ut_exit.e_termination.to_ne_bytes()); + buf.extend_from_slice(&utmp.ut_exit.e_exit.to_ne_bytes()); + buf.extend_from_slice(&utmp.ut_session.to_ne_bytes()); + buf.extend_from_slice(&utmp.ut_tv.tv_sec.to_ne_bytes()); + buf.extend_from_slice(&utmp.ut_tv.tv_usec.to_ne_bytes()); + for v in &utmp.ut_addr_v6 { + buf.extend_from_slice(&v.to_ne_bytes()); + } + serialize_i8_arr(&mut buf, &utmp.glibc_reserved); + buf + } + + let mut buf = serialize(&utmp); + buf.append(&mut serialize(&utmp1)); + buf.append(&mut serialize(&utmp2)); let mut f = File::create(path).unwrap(); f.write_all(&buf).unwrap(); } From 99fa1bb9c569990f39d1f485e7a10922a8543b76 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 8 Feb 2026 10:18:01 +0000 Subject: [PATCH 29/46] tr: fix possible usage in invalid utf8 set sequence. (#10791) --- src/uu/tr/locales/en-US.ftl | 1 + src/uu/tr/locales/fr-FR.ftl | 1 + src/uu/tr/src/operation.rs | 17 ++++++++++------- tests/by-util/test_tr.rs | 24 ++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/uu/tr/locales/en-US.ftl b/src/uu/tr/locales/en-US.ftl index 0f1fc38a9..087436847 100644 --- a/src/uu/tr/locales/en-US.ftl +++ b/src/uu/tr/locales/en-US.ftl @@ -23,6 +23,7 @@ tr-error-write-error = write error # Warning messages tr-warning-unescaped-backslash = warning: an unescaped backslash at end of string is not portable tr-warning-ambiguous-octal-escape = the ambiguous octal escape \{ $origin_octal } is being interpreted as the 2-byte sequence \0{ $actual_octal_tail }, { $outstand_char } +tr-warning-invalid-utf8 = invalid utf8 sequence # Sequence parsing error messages tr-error-missing-char-class-name = missing character class name '[::]' diff --git a/src/uu/tr/locales/fr-FR.ftl b/src/uu/tr/locales/fr-FR.ftl index e8af47a0d..075b4a447 100644 --- a/src/uu/tr/locales/fr-FR.ftl +++ b/src/uu/tr/locales/fr-FR.ftl @@ -24,6 +24,7 @@ tr-error-write-error = erreur d'écriture tr-warning-unescaped-backslash = avertissement : une barre oblique inverse non échappée à la fin de la chaîne n'est pas portable tr-warning-ambiguous-octal-escape = l'échappement octal ambigu \{ $origin_octal } est en cours d'interprétation comme la séquence de 2 octets \0{ $actual_octal_tail }, { $outstand_char } +tr-warning-invalid-utf8 = séquence UTF-8 non valide # Messages d'erreur d'analyse de séquence tr-error-missing-char-class-name = nom de classe de caractères manquant '[::]' diff --git a/src/uu/tr/src/operation.rs b/src/uu/tr/src/operation.rs index e2ba2bd83..a9d1f5287 100644 --- a/src/uu/tr/src/operation.rs +++ b/src/uu/tr/src/operation.rs @@ -379,13 +379,16 @@ impl Sequence { let str_to_parse = std::str::from_utf8(out).unwrap(); let result = u8::from_str_radix(str_to_parse, 8).ok(); if result.is_none() { - let origin_octal: &str = std::str::from_utf8(input).unwrap(); - let actual_octal_tail: &str = std::str::from_utf8(&input[0..2]).unwrap(); - let outstand_char: char = char::from_u32(input[2] as u32).unwrap(); - show_warning!( - "{}", - translate!("tr-warning-ambiguous-octal-escape", "origin_octal" => origin_octal, "actual_octal_tail" => actual_octal_tail, "outstand_char" => outstand_char) - ); + if let Ok(origin_octal) = std::str::from_utf8(input) { + let actual_octal_tail: &str = std::str::from_utf8(&input[0..2]).unwrap(); + let outstand_char: char = char::from_u32(input[2] as u32).unwrap(); + show_warning!( + "{}", + translate!("tr-warning-ambiguous-octal-escape", "origin_octal" => origin_octal, "actual_octal_tail" => actual_octal_tail, "outstand_char" => outstand_char) + ); + } else { + show_warning!("{}", translate!("tr-warning-invalid-utf8")); + } } result }, diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs index 491fd64ee..4a7c266b9 100644 --- a/tests/by-util/test_tr.rs +++ b/tests/by-util/test_tr.rs @@ -1544,6 +1544,30 @@ fn test_non_digit_repeat() { .stderr_only("tr: invalid repeat count 'c' in [c*n] construct\n"); } +#[test] +#[cfg(unix)] +fn test_octal_escape_ambiguous_followed_by_non_utf8() { + // This case does not trigger the panic + let set1 = OsStr::from_bytes(b"\\501a"); + new_ucmd!() + .arg("-d") + .arg(set1) + .pipe_in("(1a)") + .succeeds() + .stderr_contains("warning: the ambiguous octal escape") + .stdout_is(")"); + + // An user is not supposed to use this invalid utf8 set but + // we would need to make the command more error-proof still + let set1 = OsStr::from_bytes(b"\\501\xff"); + new_ucmd!() + .arg("-d") + .arg(set1) + .pipe_in([b'(', b'1', 0xff, b')']) + .succeeds() + .stderr_contains("warning: invalid utf8 sequence"); +} + #[cfg(target_os = "linux")] #[test] fn test_failed_write_is_reported() { From 4bbd71fffb323ef4b6a09bae5e278e7b8c5f4896 Mon Sep 17 00:00:00 2001 From: cerdelen <95369756+cerdelen@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:16:19 +0100 Subject: [PATCH 30/46] unexpand: use buffered read & improve performance by 34.66% (#10798) --- src/uu/unexpand/src/unexpand.rs | 173 ++++++++++-------- tests/by-util/test_unexpand.rs | 111 +++++++++++ ...ing_spaces_after_chunk_without_newline.txt | 5 + ...s_after_chunk_without_newline_expected.txt | 5 + tests/fixtures/unexpand/new_line_in_chunk.txt | 9 + .../new_line_in_chunk_all_normal_chars.txt | 2 + ...ine_in_chunk_all_normal_chars_expected.txt | 2 + .../unexpand/new_line_in_chunk_expected.txt | 9 + ..._into_leading_blanks_into_normal_chars.txt | 2 + ...ding_blanks_into_normal_chars_expected.txt | 2 + ..._few_trailing_spaces_into_normal_chars.txt | 2 + ...ling_spaces_into_normal_chars_expected.txt | 2 + ...chunk_normal_chars_into_leading_blanks.txt | 2 + ...mal_chars_into_leading_blanks_expected.txt | 2 + ..._into_leading_blanks_into_normal_chars.txt | 2 + ...ding_blanks_into_normal_chars_expected.txt | 2 + ...hunk_trailing_spaces_into_normal_chars.txt | 2 + ...ling_spaces_into_normal_chars_expected.txt | 2 + ...eading_spaces_in_chunk_without_newline.txt | 5 + ...aces_in_chunk_without_newline_expected.txt | 5 + ...ailing_spaces_in_chunk_without_newline.txt | 5 + ...aces_in_chunk_without_newline_expected.txt | 5 + 22 files changed, 283 insertions(+), 73 deletions(-) create mode 100644 tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline.txt create mode 100644 tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline_expected.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars_expected.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_expected.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars.txt create mode 100644 tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt create mode 100644 tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt create mode 100644 tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt create mode 100644 tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline.txt create mode 100644 tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline_expected.txt diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 14c2b8b0b..744783591 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -8,7 +8,7 @@ use clap::{Arg, ArgAction, Command}; use std::ffi::OsString; use std::fs::File; -use std::io::{BufRead, BufReader, BufWriter, Read, Stdout, Write, stdin, stdout}; +use std::io::{BufReader, BufWriter, Read, Stdout, Write, stdin, stdout}; use std::num::IntErrorKind; use std::path::Path; use std::str::from_utf8; @@ -347,7 +347,7 @@ fn next_tabstop(tab_config: &TabConfig, col: usize) -> Option { fn write_tabs( output: &mut BufWriter, tab_config: &TabConfig, - mut scol: usize, + scol: &mut usize, col: usize, prevtab: bool, init: bool, @@ -357,20 +357,20 @@ fn write_tabs( // We never turn a single space before a non-blank into // a tab, unless it's at the start of the line. let ai = init || amode; - if (ai && !prevtab && col > scol + 1) || (col > scol && (init || ai && prevtab)) { - while let Some(nts) = next_tabstop(tab_config, scol) { - if col < scol + nts { + if (ai && !prevtab && col > *scol + 1) || (col > *scol && (init || ai && prevtab)) { + while let Some(nts) = next_tabstop(tab_config, *scol) { + if col < *scol + nts { break; } output.write_all(b"\t")?; - scol += nts; + *scol += nts; } } - while col > scol { + while col > *scol { output.write_all(b" ")?; - scol += 1; + *scol += 1; } Ok(()) } @@ -424,81 +424,88 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi } #[allow(clippy::cognitive_complexity)] +#[allow(clippy::too_many_arguments)] fn unexpand_line( - buf: &mut Vec, + buf: &[u8], output: &mut BufWriter, options: &Options, lastcol: usize, tab_config: &TabConfig, + col: &mut usize, + scol: &mut usize, + leading: &mut bool, ) -> UResult<()> { - // Fast path: if we're not converting all spaces (-a flag not set) - // and the line doesn't start with spaces, just write it directly - if !options.aflag && !buf.is_empty() && buf[0] != b' ' && buf[0] != b'\t' { - output.write_all(buf)?; - buf.truncate(0); - return Ok(()); + // We can only fast forward if we don't need to calculate col/scol + if let Some(b'\n') = buf.last() { + // Fast path: if we're not converting all spaces (-a flag not set) + // and the line doesn't start with spaces, just write it directly + if !options.aflag && !buf.is_empty() && ((buf[0] != b' ' && buf[0] != b'\t') || !*leading) { + *col += buf.len(); + output.write_all(buf)?; + return Ok(()); + } } let mut byte = 0; // offset into the buffer - let mut col = 0; // the current column - let mut scol = 0; // the start col for the current span, i.e., the already-printed width - let mut init = true; // are we at the start of the line? let mut pctype = CharType::Other; - // Fast path for leading spaces in non-UTF8 mode: count consecutive spaces/tabs at start - if !options.uflag && !options.aflag { - // In default mode (not -a), we only convert leading spaces - // So we can batch process them and then copy the rest - while byte < buf.len() { - match buf[byte] { - b' ' => { - col += 1; - byte += 1; + // We can only fast forward if we don't need to calculate col/scol + if let Some(b'\n') = buf.last() { + // Fast path for leading spaces in non-UTF8 mode: count consecutive spaces/tabs at start + if !options.uflag && !options.aflag && *leading { + // In default mode (not -a), we only convert leading spaces + // So we can batch process them and then copy the rest + while byte < buf.len() { + match buf[byte] { + b' ' => { + *col += 1; + byte += 1; + } + b'\t' => { + *col += next_tabstop(tab_config, *col).unwrap_or(1); + byte += 1; + pctype = CharType::Tab; + } + _ => break, } - b'\t' => { - col += next_tabstop(tab_config, col).unwrap_or(1); - byte += 1; - pctype = CharType::Tab; - } - _ => break, } - } - // If we found spaces/tabs, write them as tabs - if byte > 0 { - write_tabs( - output, - tab_config, - 0, - col, - pctype == CharType::Tab, - true, - true, - )?; - } + // If we found spaces/tabs, write them as tabs + if byte > 0 { + write_tabs( + output, + tab_config, + scol, + *col, + pctype == CharType::Tab, + true, + true, + )?; + } - // Write the rest of the line directly (no more tab conversion needed) - if byte < buf.len() { - output.write_all(&buf[byte..])?; + // Write the rest of the line directly (no more tab conversion needed) + if byte < buf.len() { + *leading = false; + output.write_all(&buf[byte..])?; + } + return Ok(()); } - buf.truncate(0); - return Ok(()); } while byte < buf.len() { // when we have a finite number of columns, never convert past the last column - if lastcol > 0 && col >= lastcol { + if lastcol > 0 && *col >= lastcol { write_tabs( output, tab_config, scol, - col, + *col, pctype == CharType::Tab, - init, + *leading, true, )?; output.write_all(&buf[byte..])?; - scol = col; + *scol = *col; break; } @@ -506,19 +513,19 @@ fn unexpand_line( let (ctype, cwidth, nbytes) = next_char_info(options.uflag, buf, byte); // now figure out how many columns this char takes up, and maybe print it - let tabs_buffered = init || options.aflag; + let tabs_buffered = *leading || options.aflag; match ctype { CharType::Space | CharType::Tab => { // compute next col, but only write space or tab chars if not buffering - col += if ctype == CharType::Space { + *col += if ctype == CharType::Space { 1 } else { - next_tabstop(tab_config, col).unwrap_or(1) + next_tabstop(tab_config, *col).unwrap_or(1) }; if !tabs_buffered { output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // now printed up to this column + *scol = *col; // now printed up to this column } } CharType::Other | CharType::Backspace => { @@ -527,23 +534,23 @@ fn unexpand_line( output, tab_config, scol, - col, + *col, pctype == CharType::Tab, - init, + *leading, options.aflag, )?; - init = false; // no longer at the start of a line - col = if ctype == CharType::Other { + *leading = false; // no longer at the start of a line + *col = if ctype == CharType::Other { // use computed width - col + cwidth - } else if col > 0 { + *col + cwidth + } else if *col > 0 { // Backspace case, but only if col > 0 - col - 1 + *col - 1 } else { 0 }; output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // we've now printed up to this column + *scol = *col; // we've now printed up to this column } } @@ -556,12 +563,11 @@ fn unexpand_line( output, tab_config, scol, - col, + *col, pctype == CharType::Tab, - init, + *leading, true, )?; - buf.truncate(0); // clear out the buffer Ok(()) } @@ -573,12 +579,33 @@ fn unexpand_file( lastcol: usize, tab_config: &TabConfig, ) -> UResult<()> { - let mut buf = Vec::new(); + let mut buf = [0u8; 4096]; let mut input = open(file)?; + let mut col = 0; + let mut scol = 0; + let mut leading = true; loop { - match input.read_until(b'\n', &mut buf) { + match input.read(&mut buf) { Ok(0) => break, - Ok(_) => unexpand_line(&mut buf, output, options, lastcol, tab_config)?, + Ok(n) => { + for line in buf[..n].split_inclusive(|b| *b == b'\n') { + unexpand_line( + line, + output, + options, + lastcol, + tab_config, + &mut col, + &mut scol, + &mut leading, + )?; + if let Some(b'\n') = line.last() { + col = 0; + scol = 0; + leading = true; + } + } + } Err(e) => return Err(e.map_err_context(|| file.maybe_quote().to_string())), } } diff --git a/tests/by-util/test_unexpand.rs b/tests/by-util/test_unexpand.rs index d29fecfd3..489a826db 100644 --- a/tests/by-util/test_unexpand.rs +++ b/tests/by-util/test_unexpand.rs @@ -366,3 +366,114 @@ fn test_extended_tabstop_syntax() { .stdout_is(expected); } } + +#[test] +fn test_buffered_reads_new_line_no_tabs_in_first_chunk() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["new_line_in_chunk.txt"]) + .succeeds() + .stdout_is_fixture("new_line_in_chunk_expected.txt"); +} + +#[test] +fn test_leading_spaces_after_chunk_without_newline() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["leading_spaces_after_chunk_without_newline.txt"]) + .succeeds() + .stdout_is_fixture("leading_spaces_after_chunk_without_newline_expected.txt"); +} + +#[test] +fn test_trailing_spaces_and_leading_spaces_in_chunk_without_newline() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt"]) + .succeeds() + .stdout_is_fixture( + "trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt", + ); +} + +#[test] +fn test_trailing_spaces_in_chunk_without_newline() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["trailing_spaces_in_chunk_without_newline.txt"]) + .succeeds() + .stdout_is_fixture("trailing_spaces_in_chunk_without_newline_expected.txt"); +} + +#[test] +fn test_new_line_in_chunk_all_normal_chars() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["new_line_in_chunk_all_normal_chars.txt"]) + .succeeds() + .stdout_is_fixture("new_line_in_chunk_all_normal_chars_expected.txt"); +} + +#[test] +fn test_new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars() + { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt"]) + .succeeds() + .stdout_is_fixture("new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt"); +} + +#[test] +fn test_new_line_in_chunk_few_trailing_spaces_into_normal_chars() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt"]) + .succeeds() + .stdout_is_fixture("new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt"); +} + +#[test] +fn test_new_line_in_chunk_normal_chars_into_leading_blanks() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["new_line_in_chunk_normal_chars_into_leading_blanks.txt"]) + .succeeds() + .stdout_is_fixture("new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt"); +} + +#[test] +fn test_new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt"]) + .succeeds() + .stdout_is_fixture( + "new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt", + ); +} + +#[test] +fn test_new_line_in_chunk_trailing_spaces_into_normal_chars() { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + new_ucmd!() + .args(&["new_line_in_chunk_trailing_spaces_into_normal_chars.txt"]) + .succeeds() + .stdout_is_fixture("new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt"); +} + +// +// i need tests for +// 4050 chars, '\n', normal chars , normal chars +// 4050 chars, '\n', normal chars , leading blanks normal chars +// +// 4050 chars, '\n', all blanks until 4096, normal chars +// 4050 chars, '\n', all blanks until 4096, leading blanks normal chars +// +// 4050 chars, '\n', a few blanks until 4096, normal chars +// 4050 chars, '\n', a few blanks until 4096, leading blanks normal chars +// +// +// +// +// diff --git a/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline.txt b/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline.txt new file mode 100644 index 000000000..d2a1d418d --- /dev/null +++ b/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline.txt @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + test + +few + pldwq diff --git a/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline_expected.txt b/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline_expected.txt new file mode 100644 index 000000000..d2a1d418d --- /dev/null +++ b/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline_expected.txt @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + test + +few + pldwq diff --git a/tests/fixtures/unexpand/new_line_in_chunk.txt b/tests/fixtures/unexpand/new_line_in_chunk.txt new file mode 100644 index 000000000..f643b5bbf --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk.txt @@ -0,0 +1,9 @@ +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000 0 000 000 00 0 0 0 0 + Lorem Ipsum test file + + + + +hello diff --git a/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars.txt new file mode 100644 index 000000000..21ee70caa --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars_expected.txt new file mode 100644 index 000000000..21ee70caa --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars_expected.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_expected.txt new file mode 100644 index 000000000..0cb46416a --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_expected.txt @@ -0,0 +1,9 @@ +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000 0 000 000 00 0 0 0 0 + Lorem Ipsum test file + + + + +hello diff --git a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt new file mode 100644 index 000000000..499865b02 --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt new file mode 100644 index 000000000..e3c3e87ec --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 00000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt new file mode 100644 index 000000000..4f31c853c --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt new file mode 100644 index 000000000..cfa6e55f1 --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks.txt b/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks.txt new file mode 100644 index 000000000..a4f30dda1 --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt new file mode 100644 index 000000000..a4f30dda1 --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt new file mode 100644 index 000000000..0a2934ce7 --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt new file mode 100644 index 000000000..68b3d342e --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars.txt new file mode 100644 index 000000000..dcf0658e8 --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt new file mode 100644 index 000000000..d2123cadf --- /dev/null +++ b/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt @@ -0,0 +1,2 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt b/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt new file mode 100644 index 000000000..d2a1d418d --- /dev/null +++ b/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + test + +few + pldwq diff --git a/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt b/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt new file mode 100644 index 000000000..d2a1d418d --- /dev/null +++ b/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + test + +few + pldwq diff --git a/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline.txt b/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline.txt new file mode 100644 index 000000000..21d542430 --- /dev/null +++ b/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline.txt @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000 + test + +few + pldwq diff --git a/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline_expected.txt b/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline_expected.txt new file mode 100644 index 000000000..21d542430 --- /dev/null +++ b/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline_expected.txt @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000 + test + +few + pldwq From 4c1536b5d551d72934785e4896a53178b8272556 Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Mon, 9 Feb 2026 00:08:25 +0900 Subject: [PATCH 31/46] unexpand: codegen-units=1 - improve performance by 7.18% (#10817) Removed uu_unexpand profile configuration from Cargo.toml. --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dfa88f5f4..34d2b069e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -607,8 +607,6 @@ codegen-units = 1 # FIXME: https://github.com/uutils/coreutils/issues/10654 [profile.release.package.uu_expand] codegen-units = 16 -[profile.release.package.uu_unexpand] -codegen-units = 16 # A release-like profile that is as small as possible. [profile.release-small] From 1f4a976a923122ba9d26f7633848162ec31f8b9a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Feb 2026 16:14:09 +0100 Subject: [PATCH 32/46] Revert "unexpand: use buffered read & improve performance by 34.66% (#10798)" (#10819) This reverts commit 4bbd71fffb323ef4b6a09bae5e278e7b8c5f4896. --- src/uu/unexpand/src/unexpand.rs | 177 ++++++++---------- tests/by-util/test_unexpand.rs | 111 ----------- ...ing_spaces_after_chunk_without_newline.txt | 5 - ...s_after_chunk_without_newline_expected.txt | 5 - tests/fixtures/unexpand/new_line_in_chunk.txt | 9 - .../new_line_in_chunk_all_normal_chars.txt | 2 - ...ine_in_chunk_all_normal_chars_expected.txt | 2 - .../unexpand/new_line_in_chunk_expected.txt | 9 - ..._into_leading_blanks_into_normal_chars.txt | 2 - ...ding_blanks_into_normal_chars_expected.txt | 2 - ..._few_trailing_spaces_into_normal_chars.txt | 2 - ...ling_spaces_into_normal_chars_expected.txt | 2 - ...chunk_normal_chars_into_leading_blanks.txt | 2 - ...mal_chars_into_leading_blanks_expected.txt | 2 - ..._into_leading_blanks_into_normal_chars.txt | 2 - ...ding_blanks_into_normal_chars_expected.txt | 2 - ...hunk_trailing_spaces_into_normal_chars.txt | 2 - ...ling_spaces_into_normal_chars_expected.txt | 2 - ...eading_spaces_in_chunk_without_newline.txt | 5 - ...aces_in_chunk_without_newline_expected.txt | 5 - ...ailing_spaces_in_chunk_without_newline.txt | 5 - ...aces_in_chunk_without_newline_expected.txt | 5 - 22 files changed, 75 insertions(+), 285 deletions(-) delete mode 100644 tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline.txt delete mode 100644 tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline_expected.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars_expected.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_expected.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars.txt delete mode 100644 tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt delete mode 100644 tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt delete mode 100644 tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt delete mode 100644 tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline.txt delete mode 100644 tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline_expected.txt diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 744783591..14c2b8b0b 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -8,7 +8,7 @@ use clap::{Arg, ArgAction, Command}; use std::ffi::OsString; use std::fs::File; -use std::io::{BufReader, BufWriter, Read, Stdout, Write, stdin, stdout}; +use std::io::{BufRead, BufReader, BufWriter, Read, Stdout, Write, stdin, stdout}; use std::num::IntErrorKind; use std::path::Path; use std::str::from_utf8; @@ -347,7 +347,7 @@ fn next_tabstop(tab_config: &TabConfig, col: usize) -> Option { fn write_tabs( output: &mut BufWriter, tab_config: &TabConfig, - scol: &mut usize, + mut scol: usize, col: usize, prevtab: bool, init: bool, @@ -357,20 +357,20 @@ fn write_tabs( // We never turn a single space before a non-blank into // a tab, unless it's at the start of the line. let ai = init || amode; - if (ai && !prevtab && col > *scol + 1) || (col > *scol && (init || ai && prevtab)) { - while let Some(nts) = next_tabstop(tab_config, *scol) { - if col < *scol + nts { + if (ai && !prevtab && col > scol + 1) || (col > scol && (init || ai && prevtab)) { + while let Some(nts) = next_tabstop(tab_config, scol) { + if col < scol + nts { break; } output.write_all(b"\t")?; - *scol += nts; + scol += nts; } } - while col > *scol { + while col > scol { output.write_all(b" ")?; - *scol += 1; + scol += 1; } Ok(()) } @@ -424,88 +424,81 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi } #[allow(clippy::cognitive_complexity)] -#[allow(clippy::too_many_arguments)] fn unexpand_line( - buf: &[u8], + buf: &mut Vec, output: &mut BufWriter, options: &Options, lastcol: usize, tab_config: &TabConfig, - col: &mut usize, - scol: &mut usize, - leading: &mut bool, ) -> UResult<()> { - // We can only fast forward if we don't need to calculate col/scol - if let Some(b'\n') = buf.last() { - // Fast path: if we're not converting all spaces (-a flag not set) - // and the line doesn't start with spaces, just write it directly - if !options.aflag && !buf.is_empty() && ((buf[0] != b' ' && buf[0] != b'\t') || !*leading) { - *col += buf.len(); - output.write_all(buf)?; - return Ok(()); - } + // Fast path: if we're not converting all spaces (-a flag not set) + // and the line doesn't start with spaces, just write it directly + if !options.aflag && !buf.is_empty() && buf[0] != b' ' && buf[0] != b'\t' { + output.write_all(buf)?; + buf.truncate(0); + return Ok(()); } let mut byte = 0; // offset into the buffer + let mut col = 0; // the current column + let mut scol = 0; // the start col for the current span, i.e., the already-printed width + let mut init = true; // are we at the start of the line? let mut pctype = CharType::Other; - // We can only fast forward if we don't need to calculate col/scol - if let Some(b'\n') = buf.last() { - // Fast path for leading spaces in non-UTF8 mode: count consecutive spaces/tabs at start - if !options.uflag && !options.aflag && *leading { - // In default mode (not -a), we only convert leading spaces - // So we can batch process them and then copy the rest - while byte < buf.len() { - match buf[byte] { - b' ' => { - *col += 1; - byte += 1; - } - b'\t' => { - *col += next_tabstop(tab_config, *col).unwrap_or(1); - byte += 1; - pctype = CharType::Tab; - } - _ => break, + // Fast path for leading spaces in non-UTF8 mode: count consecutive spaces/tabs at start + if !options.uflag && !options.aflag { + // In default mode (not -a), we only convert leading spaces + // So we can batch process them and then copy the rest + while byte < buf.len() { + match buf[byte] { + b' ' => { + col += 1; + byte += 1; } + b'\t' => { + col += next_tabstop(tab_config, col).unwrap_or(1); + byte += 1; + pctype = CharType::Tab; + } + _ => break, } - - // If we found spaces/tabs, write them as tabs - if byte > 0 { - write_tabs( - output, - tab_config, - scol, - *col, - pctype == CharType::Tab, - true, - true, - )?; - } - - // Write the rest of the line directly (no more tab conversion needed) - if byte < buf.len() { - *leading = false; - output.write_all(&buf[byte..])?; - } - return Ok(()); } + + // If we found spaces/tabs, write them as tabs + if byte > 0 { + write_tabs( + output, + tab_config, + 0, + col, + pctype == CharType::Tab, + true, + true, + )?; + } + + // Write the rest of the line directly (no more tab conversion needed) + if byte < buf.len() { + output.write_all(&buf[byte..])?; + } + buf.truncate(0); + return Ok(()); } while byte < buf.len() { // when we have a finite number of columns, never convert past the last column - if lastcol > 0 && *col >= lastcol { + if lastcol > 0 && col >= lastcol { write_tabs( output, tab_config, scol, - *col, + col, pctype == CharType::Tab, - *leading, + init, true, )?; output.write_all(&buf[byte..])?; - *scol = *col; + scol = col; break; } @@ -513,19 +506,19 @@ fn unexpand_line( let (ctype, cwidth, nbytes) = next_char_info(options.uflag, buf, byte); // now figure out how many columns this char takes up, and maybe print it - let tabs_buffered = *leading || options.aflag; + let tabs_buffered = init || options.aflag; match ctype { CharType::Space | CharType::Tab => { // compute next col, but only write space or tab chars if not buffering - *col += if ctype == CharType::Space { + col += if ctype == CharType::Space { 1 } else { - next_tabstop(tab_config, *col).unwrap_or(1) + next_tabstop(tab_config, col).unwrap_or(1) }; if !tabs_buffered { output.write_all(&buf[byte..byte + nbytes])?; - *scol = *col; // now printed up to this column + scol = col; // now printed up to this column } } CharType::Other | CharType::Backspace => { @@ -534,23 +527,23 @@ fn unexpand_line( output, tab_config, scol, - *col, + col, pctype == CharType::Tab, - *leading, + init, options.aflag, )?; - *leading = false; // no longer at the start of a line - *col = if ctype == CharType::Other { + init = false; // no longer at the start of a line + col = if ctype == CharType::Other { // use computed width - *col + cwidth - } else if *col > 0 { + col + cwidth + } else if col > 0 { // Backspace case, but only if col > 0 - *col - 1 + col - 1 } else { 0 }; output.write_all(&buf[byte..byte + nbytes])?; - *scol = *col; // we've now printed up to this column + scol = col; // we've now printed up to this column } } @@ -563,11 +556,12 @@ fn unexpand_line( output, tab_config, scol, - *col, + col, pctype == CharType::Tab, - *leading, + init, true, )?; + buf.truncate(0); // clear out the buffer Ok(()) } @@ -579,33 +573,12 @@ fn unexpand_file( lastcol: usize, tab_config: &TabConfig, ) -> UResult<()> { - let mut buf = [0u8; 4096]; + let mut buf = Vec::new(); let mut input = open(file)?; - let mut col = 0; - let mut scol = 0; - let mut leading = true; loop { - match input.read(&mut buf) { + match input.read_until(b'\n', &mut buf) { Ok(0) => break, - Ok(n) => { - for line in buf[..n].split_inclusive(|b| *b == b'\n') { - unexpand_line( - line, - output, - options, - lastcol, - tab_config, - &mut col, - &mut scol, - &mut leading, - )?; - if let Some(b'\n') = line.last() { - col = 0; - scol = 0; - leading = true; - } - } - } + Ok(_) => unexpand_line(&mut buf, output, options, lastcol, tab_config)?, Err(e) => return Err(e.map_err_context(|| file.maybe_quote().to_string())), } } diff --git a/tests/by-util/test_unexpand.rs b/tests/by-util/test_unexpand.rs index 489a826db..d29fecfd3 100644 --- a/tests/by-util/test_unexpand.rs +++ b/tests/by-util/test_unexpand.rs @@ -366,114 +366,3 @@ fn test_extended_tabstop_syntax() { .stdout_is(expected); } } - -#[test] -fn test_buffered_reads_new_line_no_tabs_in_first_chunk() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["new_line_in_chunk.txt"]) - .succeeds() - .stdout_is_fixture("new_line_in_chunk_expected.txt"); -} - -#[test] -fn test_leading_spaces_after_chunk_without_newline() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["leading_spaces_after_chunk_without_newline.txt"]) - .succeeds() - .stdout_is_fixture("leading_spaces_after_chunk_without_newline_expected.txt"); -} - -#[test] -fn test_trailing_spaces_and_leading_spaces_in_chunk_without_newline() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt"]) - .succeeds() - .stdout_is_fixture( - "trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt", - ); -} - -#[test] -fn test_trailing_spaces_in_chunk_without_newline() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["trailing_spaces_in_chunk_without_newline.txt"]) - .succeeds() - .stdout_is_fixture("trailing_spaces_in_chunk_without_newline_expected.txt"); -} - -#[test] -fn test_new_line_in_chunk_all_normal_chars() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["new_line_in_chunk_all_normal_chars.txt"]) - .succeeds() - .stdout_is_fixture("new_line_in_chunk_all_normal_chars_expected.txt"); -} - -#[test] -fn test_new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars() - { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt"]) - .succeeds() - .stdout_is_fixture("new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt"); -} - -#[test] -fn test_new_line_in_chunk_few_trailing_spaces_into_normal_chars() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt"]) - .succeeds() - .stdout_is_fixture("new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt"); -} - -#[test] -fn test_new_line_in_chunk_normal_chars_into_leading_blanks() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["new_line_in_chunk_normal_chars_into_leading_blanks.txt"]) - .succeeds() - .stdout_is_fixture("new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt"); -} - -#[test] -fn test_new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt"]) - .succeeds() - .stdout_is_fixture( - "new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt", - ); -} - -#[test] -fn test_new_line_in_chunk_trailing_spaces_into_normal_chars() { - // fixture has newlines in first chunk and has leading spaces after newline in chunk - new_ucmd!() - .args(&["new_line_in_chunk_trailing_spaces_into_normal_chars.txt"]) - .succeeds() - .stdout_is_fixture("new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt"); -} - -// -// i need tests for -// 4050 chars, '\n', normal chars , normal chars -// 4050 chars, '\n', normal chars , leading blanks normal chars -// -// 4050 chars, '\n', all blanks until 4096, normal chars -// 4050 chars, '\n', all blanks until 4096, leading blanks normal chars -// -// 4050 chars, '\n', a few blanks until 4096, normal chars -// 4050 chars, '\n', a few blanks until 4096, leading blanks normal chars -// -// -// -// -// diff --git a/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline.txt b/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline.txt deleted file mode 100644 index d2a1d418d..000000000 --- a/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline.txt +++ /dev/null @@ -1,5 +0,0 @@ -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - test - -few - pldwq diff --git a/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline_expected.txt b/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline_expected.txt deleted file mode 100644 index d2a1d418d..000000000 --- a/tests/fixtures/unexpand/leading_spaces_after_chunk_without_newline_expected.txt +++ /dev/null @@ -1,5 +0,0 @@ -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - test - -few - pldwq diff --git a/tests/fixtures/unexpand/new_line_in_chunk.txt b/tests/fixtures/unexpand/new_line_in_chunk.txt deleted file mode 100644 index f643b5bbf..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk.txt +++ /dev/null @@ -1,9 +0,0 @@ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000 0 000 000 00 0 0 0 0 - Lorem Ipsum test file - - - - -hello diff --git a/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars.txt deleted file mode 100644 index 21ee70caa..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars_expected.txt deleted file mode 100644 index 21ee70caa..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_all_normal_chars_expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_expected.txt deleted file mode 100644 index 0cb46416a..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_expected.txt +++ /dev/null @@ -1,9 +0,0 @@ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000 0 000 000 00 0 0 0 0 - Lorem Ipsum test file - - - - -hello diff --git a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt deleted file mode 100644 index 499865b02..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 00000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt deleted file mode 100644 index e3c3e87ec..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_blanks_into_normal_chars_into_leading_blanks_into_normal_chars_expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 00000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt deleted file mode 100644 index 4f31c853c..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt deleted file mode 100644 index cfa6e55f1..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_few_trailing_spaces_into_normal_chars_expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks.txt b/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks.txt deleted file mode 100644 index a4f30dda1..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt deleted file mode 100644 index a4f30dda1..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_normal_chars_into_leading_blanks_expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -0000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt deleted file mode 100644 index 0a2934ce7..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt deleted file mode 100644 index 68b3d342e..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_trailing_blanks_into_leading_blanks_into_normal_chars_expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars.txt b/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars.txt deleted file mode 100644 index dcf0658e8..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt b/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt deleted file mode 100644 index d2123cadf..000000000 --- a/tests/fixtures/unexpand/new_line_in_chunk_trailing_spaces_into_normal_chars_expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt b/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt deleted file mode 100644 index d2a1d418d..000000000 --- a/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline.txt +++ /dev/null @@ -1,5 +0,0 @@ -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - test - -few - pldwq diff --git a/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt b/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt deleted file mode 100644 index d2a1d418d..000000000 --- a/tests/fixtures/unexpand/trailing_spaces_and_leading_spaces_in_chunk_without_newline_expected.txt +++ /dev/null @@ -1,5 +0,0 @@ -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - test - -few - pldwq diff --git a/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline.txt b/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline.txt deleted file mode 100644 index 21d542430..000000000 --- a/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline.txt +++ /dev/null @@ -1,5 +0,0 @@ -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000 - test - -few - pldwq diff --git a/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline_expected.txt b/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline_expected.txt deleted file mode 100644 index 21d542430..000000000 --- a/tests/fixtures/unexpand/trailing_spaces_in_chunk_without_newline_expected.txt +++ /dev/null @@ -1,5 +0,0 @@ -0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000 - test - -few - pldwq From a9136fb863b932033ef98310d01e2153cf4c3382 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Feb 2026 20:10:09 +0100 Subject: [PATCH 33/46] printf: fix %q shell quoting with control chars and quotes (#10816) --- .../features/quoting_style/shell_quoter.rs | 90 +++++++++++++++++-- tests/by-util/test_printf.rs | 13 +++ 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/src/uucore/src/lib/features/quoting_style/shell_quoter.rs b/src/uucore/src/lib/features/quoting_style/shell_quoter.rs index 58d3b3e8d..bb531b50d 100644 --- a/src/uucore/src/lib/features/quoting_style/shell_quoter.rs +++ b/src/uucore/src/lib/features/quoting_style/shell_quoter.rs @@ -40,7 +40,7 @@ impl<'a> NonEscapedShellQuoter<'a> { dirname: bool, size_hint: usize, ) -> Self { - let (quotes, must_quote) = initial_quoting(reference, dirname, always_quote); + let (quotes, must_quote) = initial_quoting(reference, dirname, always_quote, false); Self { reference, quotes, @@ -108,7 +108,7 @@ pub(super) struct EscapedShellQuoter<'a> { impl<'a> EscapedShellQuoter<'a> { pub fn new(reference: &'a [u8], always_quote: bool, dirname: bool, size_hint: usize) -> Self { - let (quotes, must_quote) = initial_quoting(reference, dirname, always_quote); + let (quotes, must_quote) = initial_quoting(reference, dirname, always_quote, true); Self { reference, quotes, @@ -185,11 +185,17 @@ impl Quoter for EscapedShellQuoter<'_> { } /// Deduce the initial quoting status from the provided information -fn initial_quoting(input: &[u8], dirname: bool, always_quote: bool) -> (Quotes, bool) { - if input - .iter() - .any(|c| shell_escaped_char_set(dirname).contains(c)) - { +fn initial_quoting( + input: &[u8], + dirname: bool, + always_quote: bool, + check_control_chars: bool, +) -> (Quotes, bool) { + let has_special_chars = input.iter().any(|c| { + shell_escaped_char_set(dirname).contains(c) || (check_control_chars && c.is_ascii_control()) + }); + + if has_special_chars { (Quotes::Single, true) } else if input.contains(&b'\'') { (Quotes::Double, true) @@ -239,3 +245,73 @@ fn finalize_shell_quoter( buffer } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_initial_quoting() { + // Control chars (0-31 and 0x7F) force single quotes in escape mode + assert_eq!( + initial_quoting(b"\x01", false, false, true), + (Quotes::Single, true) + ); + + // Control + quote uses single quotes (segmented) in escape mode + assert_eq!( + initial_quoting(b"\x01'\x01", false, false, true), + (Quotes::Single, true) + ); + + // Simple quote uses double quotes in escape mode + assert_eq!( + initial_quoting(b"a'b", false, false, true), + (Quotes::Double, true) + ); + + // Shell special chars force single quotes in escape mode + assert_eq!( + initial_quoting(b"test$var", false, false, true), + (Quotes::Single, true) + ); + assert_eq!( + initial_quoting(b"test\nline", false, false, true), + (Quotes::Single, true) + ); + + // Empty string forces quotes in escape mode + assert_eq!( + initial_quoting(b"", false, false, true), + (Quotes::Single, true) + ); + + // Always quote flag works in escape mode + assert_eq!( + initial_quoting(b"normal", false, true, true), + (Quotes::Single, true) + ); + + // Normal text doesn't need quoting in escape mode + assert_eq!( + initial_quoting(b"hello", false, false, true), + (Quotes::Single, false) + ); + + // Dirname affects colon handling in escape mode + assert_eq!( + initial_quoting(b"dir:name", true, false, true), + (Quotes::Single, true) + ); + assert_eq!( + initial_quoting(b"file:name", false, false, true), + (Quotes::Single, false) + ); + + // Control chars ignored in non-escape mode + assert_eq!( + initial_quoting(b"\x01", false, false, false), + (Quotes::Single, false) + ); + } +} diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index 6afe0330c..81427fae5 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -1492,3 +1492,16 @@ fn test_extreme_field_width_overflow() { .fails_with_code(1) .stderr_contains("printf: write error"); //could contains additional message like "formatting width too large" not in GNU, thats fine. } + +#[test] +fn test_q_string_control_chars_with_quotes() { + // Test %q with control characters and single quotes combined. + // This tests the fix for the GNU compatibility issue where control + // characters combined with single quotes should use the segmented + // quoting approach rather than double quotes. + let input = "\x01'\x01"; + new_ucmd!() + .args(&["%q", input]) + .succeeds() + .stdout_only("''$'\\001'\\'''$'\\001'"); +} From b9d233f92cd1c2c14dde24d00bf0d7a90d4a3e10 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Feb 2026 20:12:01 +0100 Subject: [PATCH 34/46] mktemp: handle invalid UTF-8 in suffix gracefully (#10818) --- src/uu/mktemp/src/mktemp.rs | 42 +++++++++++++++++++++++++----------- tests/by-util/test_mktemp.rs | 20 +++++++++++++++++ 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index 3efd06901..d22f7500a 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -101,7 +101,7 @@ pub struct Options { pub tmpdir: Option, /// The suffix to append to the temporary file, if any. - pub suffix: Option, + pub suffix: Option, /// Whether to treat the template argument as a single file path component. pub treat_as_template: bool, @@ -150,7 +150,7 @@ impl Options { dry_run: matches.get_flag(OPT_DRY_RUN), quiet: matches.get_flag(OPT_QUIET), tmpdir, - suffix: matches.get_one::(OPT_SUFFIX).map(String::from), + suffix: matches.get_one::(OPT_SUFFIX).cloned(), treat_as_template: matches.get_flag(OPT_T), template, } @@ -214,27 +214,39 @@ fn find_last_contiguous_block_of_xs(s: &str) -> Option<(usize, usize)> { impl Params { fn from(options: Options) -> Result { // Convert OsString template to string for processing - let Some(template_str) = options.template.to_str() else { - // For non-UTF-8 templates, return an error - return Err(MkTempError::InvalidTemplate(options.template)); + // When using -t flag, be permissive with invalid UTF-8 like GNU mktemp + // Otherwise, maintain strict UTF-8 validation (existing behavior) + let template_str = if options.treat_as_template { + // For -t templates, use lossy conversion for GNU compatibility + options.template.to_string_lossy().into_owned() + } else { + // For regular templates, maintain strict validation + match options.template.to_str() { + Some(s) => s.to_string(), + None => { + return Err(MkTempError::InvalidTemplate( + "template contains invalid UTF-8".into(), + )); + } + } }; // The template argument must end in 'X' if a suffix option is given. if options.suffix.is_some() && !template_str.ends_with('X') { - return Err(MkTempError::MustEndInX(template_str.to_string())); + return Err(MkTempError::MustEndInX(template_str.clone())); } // Get the start and end indices of the randomized part of the template. // // For example, if the template is "abcXXXXyz", then `i` is 3 and `j` is 7. - let Some((i, j)) = find_last_contiguous_block_of_xs(template_str) else { + let Some((i, j)) = find_last_contiguous_block_of_xs(&template_str) else { let s = match options.suffix { // If a suffix is specified, the error message includes the template without the suffix. Some(_) => template_str .chars() .take(template_str.len()) .collect::(), - None => template_str.to_string(), + None => template_str.clone(), }; return Err(MkTempError::TooFewXs(s)); }; @@ -249,11 +261,11 @@ impl Params { let prefix_path = Path::new(&prefix_from_option).join(prefix_from_template); if options.treat_as_template && prefix_from_template.contains(MAIN_SEPARATOR) { return Err(MkTempError::PrefixContainsDirSeparator( - template_str.to_string(), + template_str.clone(), )); } if tmpdir.is_some() && Path::new(prefix_from_template).is_absolute() { - return Err(MkTempError::InvalidTemplate(template_str.into())); + return Err(MkTempError::InvalidTemplate(template_str.clone().into())); } // Split the parent directory from the file part of the prefix. @@ -271,7 +283,7 @@ impl Params { }; let prefix = match prefix_path.file_name() { None => String::new(), - Some(f) => f.to_str().unwrap().to_string(), + Some(f) => f.to_string_lossy().to_string(), }; (directory, prefix) } @@ -281,7 +293,10 @@ impl Params { // // For example, if the suffix command-line argument is ".txt" and // the template is "XXXabc", then `suffix` is "abc.txt". - let suffix_from_option = options.suffix.unwrap_or_default(); + let suffix_from_option = options + .suffix + .map(|s| s.to_string_lossy().to_string()) + .unwrap_or_default(); let suffix_from_template = &template_str[j..]; let suffix = format!("{suffix_from_template}{suffix_from_option}"); if suffix.contains(MAIN_SEPARATOR) { @@ -445,7 +460,8 @@ pub fn uu_app() -> Command { Arg::new(OPT_SUFFIX) .long(OPT_SUFFIX) .help(translate!("mktemp-help-suffix")) - .value_name("SUFFIX"), + .value_name("SUFFIX") + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(OPT_P) diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index 306f147d8..a74cb461a 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -1139,6 +1139,26 @@ fn test_non_utf8_tmpdir_long_option() { .succeeds(); } +#[test] +#[cfg(target_os = "linux")] +fn test_invalid_utf8_suffix() { + use std::os::unix::ffi::OsStrExt; + let (at, mut ucmd) = at_and_ucmd!(); + + // Create invalid UTF-8 bytes for suffix + // This mimics the GNU test which tests mktemp with bad unicode characters + let invalid_utf8 = std::ffi::OsStr::from_bytes(b"\xC3|\xED\xBA\xAD"); + + // Test that mktemp handles invalid UTF-8 in suffix gracefully + // It should succeed and create a file with the lossy conversion of the invalid UTF-8 + ucmd.arg("-p") + .arg(at.as_string()) + .arg("--suffix") + .arg(invalid_utf8) + .arg("tmpXXXXXX") + .succeeds(); +} + #[test] #[cfg(target_os = "linux")] fn test_non_utf8_tmpdir_directory_creation() { From 00b4469f497de60c8971b82b26cb09f7c5a80d8e Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Mon, 9 Feb 2026 04:40:50 +0900 Subject: [PATCH 35/46] false: dedup set_exit_code(1) (#10823) --- src/uu/false/src/false.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/uu/false/src/false.rs b/src/uu/false/src/false.rs index d4e6b6738..b0ade6957 100644 --- a/src/uu/false/src/false.rs +++ b/src/uu/false/src/false.rs @@ -33,10 +33,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; if let Err(print_fail) = error { - // Try to display this error. let _ = writeln!(std::io::stderr(), "{}: {print_fail}", uucore::util_name()); - // Completely ignore any error here, no more failover and we will fail in any case. - set_exit_code(1); } Ok(()) From 194d9807def10bba7991c662eeb1979f5cc6d24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20Fleur=20de=20Blue?= <135421389+Xylphy@users.noreply.github.com> Date: Mon, 9 Feb 2026 04:39:33 +0800 Subject: [PATCH 36/46] build: sort coreutils entries at build time (#10820) --- build.rs | 21 ++++++++++++++++----- src/bin/coreutils.rs | 5 ++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/build.rs b/build.rs index 3c4da1edb..9534a79b8 100644 --- a/build.rs +++ b/build.rs @@ -72,26 +72,37 @@ pub fn main() { .unwrap(); let mut phf_map = phf_codegen::OrderedMap::<&str>::new(); + let mut entries = Vec::new(); + for krate in &crates { let map_value = format!("({krate}::uumain, {krate}::uu_app)"); match krate.as_ref() { // 'test' is named uu_test to avoid collision with rust core crate 'test'. // It can also be invoked by name '[' for the '[ expr ] syntax'. "uu_test" => { - phf_map.entry("test", map_value.clone()); - phf_map.entry("[", map_value.clone()); + entries.push(("test", map_value.clone())); + entries.push(("[", map_value.clone())); } k if k.starts_with(OVERRIDE_PREFIX) => { - phf_map.entry(&k[OVERRIDE_PREFIX.len()..], map_value.clone()); + entries.push((&k[OVERRIDE_PREFIX.len()..], map_value.clone())); } "false" | "true" => { - phf_map.entry(krate, format!("(r#{krate}::uumain, r#{krate}::uu_app)")); + entries.push(( + krate.as_str(), + format!("(r#{krate}::uumain, r#{krate}::uu_app)"), + )); } _ => { - phf_map.entry(krate, map_value.clone()); + entries.push((krate.as_str(), map_value.clone())); } } } + entries.sort_by_key(|(name, _)| *name); + + for (name, value) in entries { + phf_map.entry(name, value); + } + write!(mf, "{}", phf_map.build()).unwrap(); mf.write_all(b"\n}\n").unwrap(); diff --git a/src/bin/coreutils.rs b/src/bin/coreutils.rs index 0ea01c778..59634849a 100644 --- a/src/bin/coreutils.rs +++ b/src/bin/coreutils.rs @@ -30,7 +30,7 @@ fn usage(utils: &UtilityMap, name: &str) { println!("Options:"); println!(" --list lists all defined functions, one per row\n"); println!("Currently defined functions:\n"); - let display_list = utils.keys().copied().sorted_unstable().join(", "); + let display_list = utils.keys().copied().join(", "); let width = cmp::min(textwrap::termwidth(), 100) - 4 * 2; // (opinion/heuristic) max 100 chars wide with 4 character side indentions println!( "{}", @@ -81,8 +81,7 @@ fn main() { usage(&utils, binary_as_util); process::exit(0); } - let mut utils: Vec<_> = utils.keys().collect(); - utils.sort(); + let utils: Vec<_> = utils.keys().collect(); for util in utils { println!("{util}"); } From c200e3b9e855e337011084422c2e4ee1084b849c Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 9 Feb 2026 23:36:28 +0100 Subject: [PATCH 37/46] ls: fix hyperlink functionality to use correct OSC 8 format and handle symlink targets (#10824) --- src/uu/ls/src/ls.rs | 62 ++++++++++++++------- tests/by-util/test_ls.rs | 114 ++++++++++++++++++++++++++++++++------- 2 files changed, 136 insertions(+), 40 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 0ec9beb40..7365cb4dc 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -3406,13 +3406,30 @@ fn display_item_name( config, false, ); - name.push(color_name( - escaped_target, - &target_data, - style_manager, - None, - is_wrap(name.len()), - )); + + // Check if the target actually needs coloring + let md_option: Option = target_data + .metadata() + .cloned() + .or_else(|| target_data.p_buf.symlink_metadata().ok()); + let style = style_manager.colors.style_for_path_with_metadata( + &target_data.p_buf, + md_option.as_ref(), + ); + + if style.is_some() { + // Only apply coloring if there's actually a style + name.push(color_name( + escaped_target, + &target_data, + style_manager, + None, + is_wrap(name.len()), + )); + } else { + // For regular files with no coloring, just use plain text + name.push(escaped_target); + } } Err(_) => { name.push( @@ -3463,29 +3480,34 @@ fn create_hyperlink(name: &OsStr, path: &PathData) -> OsString { let hostname = hostname.to_string_lossy(); let absolute_path = fs::canonicalize(path.path()).unwrap_or_default(); - let absolute_path = absolute_path.to_string_lossy(); + // Get bytes for URL encoding in a cross-platform way + let absolute_path_bytes = os_str_as_bytes_lossy(absolute_path.as_os_str()); + + // Create a set of safe ASCII bytes that don't need encoding #[cfg(not(target_os = "windows"))] - let unencoded_chars = "_-.:~/"; + let unencoded_bytes: std::collections::HashSet = "_-.~/".bytes().collect(); #[cfg(target_os = "windows")] - let unencoded_chars = "_-.:~/\\"; + let unencoded_bytes: std::collections::HashSet = "_-.~/\\:".bytes().collect(); - // percentage encoding of path - let absolute_path: String = absolute_path - .chars() - .map(|c| { - if c.is_alphanumeric() || unencoded_chars.contains(c) { - c.to_string() + // Encode at byte level to properly handle UTF-8 sequences and preserve invalid UTF-8 + let full_encoded_path: String = absolute_path_bytes + .iter() + .map(|&b: &u8| { + if b.is_ascii_alphanumeric() || unencoded_bytes.contains(&b) { + (b as char).to_string() } else { - format!("%{:02x}", c as u8) + format!("%{b:02x}") } }) .collect(); - // \x1b = ESC, \x07 = BEL - let mut ret: OsString = format!("\x1b]8;;file://{hostname}{absolute_path}\x07").into(); + // OSC 8 hyperlink format: \x1b]8;;URL\x1b\\TEXT\x1b]8;;\x1b\\ + // \x1b = ESC, \x1b\\ = ESC backslash + let mut ret: OsString = format!("\x1b]8;;file://{hostname}{full_encoded_path}\x1b\\").into(); ret.push(name); - ret.push("\x1b]8;;\x07"); + ret.push("\x1b]8;;\x1b\\"); + ret } diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 56215eac4..37492d80a 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired tmpfs mdir COLORTERM mexe bcdef mfoo timefile -// spell-checker:ignore (words) fakeroot setcap drwxr bcdlps mdangling mentry +// spell-checker:ignore (words) fakeroot setcap drwxr bcdlps mdangling mentry awith acolons #![allow( clippy::similar_names, clippy::too_many_lines, @@ -5701,19 +5701,15 @@ fn test_ls_hyperlink() { let result = scene.ucmd().arg("--hyperlink").succeeds(); assert!(result.stdout_str().contains("\x1b]8;;file://")); - assert!( - result - .stdout_str() - .contains(&format!("{path}{separator}{file}\x07{file}\x1b]8;;\x07")) - ); + assert!(result.stdout_str().contains(&format!( + "{path}{separator}{file}\x1b\\{file}\x1b]8;;\x1b\\" + ))); let result = scene.ucmd().arg("--hyperlink=always").succeeds(); assert!(result.stdout_str().contains("\x1b]8;;file://")); - assert!( - result - .stdout_str() - .contains(&format!("{path}{separator}{file}\x07{file}\x1b]8;;\x07")) - ); + assert!(result.stdout_str().contains(&format!( + "{path}{separator}{file}\x1b\\{file}\x1b]8;;\x1b\\" + ))); for argument in [ "--hyperlink=never", @@ -5748,23 +5744,23 @@ fn test_ls_hyperlink_encode_link() { assert!( result .stdout_str() - .contains("back%5cslash\x07back\\slash\x1b]8;;\x07") + .contains("back%5cslash\x1b\\back\\slash\x1b]8;;\x1b\\") ); assert!( result .stdout_str() - .contains("ques%3ftion\x07ques?tion\x1b]8;;\x07") + .contains("ques%3ftion\x1b\\ques?tion\x1b]8;;\x1b\\") ); } assert!( result .stdout_str() - .contains("encoded%253Fquestion\x07encoded%3Fquestion\x1b]8;;\x07") + .contains("encoded%253Fquestion\x1b\\encoded%3Fquestion\x1b]8;;\x1b\\") ); assert!( result .stdout_str() - .contains("sp%20ace\x07sp ace\x1b]8;;\x07") + .contains("sp%20ace\x1b\\sp ace\x1b]8;;\x1b\\") ); } // spell-checker: enable @@ -5796,7 +5792,9 @@ fn test_ls_hyperlink_dirs() { .lines() .next() .unwrap() - .contains(&format!("{path}{separator}{dir_a}\x07{dir_a}\x1b]8;;\x07:")) + .contains(&format!( + "{path}{separator}{dir_a}\x1b\\{dir_a}\x1b]8;;\x1b\\:" + )) ); assert_eq!(result.stdout_str().lines().nth(1).unwrap(), ""); assert!( @@ -5805,7 +5803,9 @@ fn test_ls_hyperlink_dirs() { .lines() .nth(2) .unwrap() - .contains(&format!("{path}{separator}{dir_b}\x07{dir_b}\x1b]8;;\x07:")) + .contains(&format!( + "{path}{separator}{dir_b}\x1b\\{dir_b}\x1b]8;;\x1b\\:" + )) ); } @@ -5837,21 +5837,95 @@ fn test_ls_hyperlink_recursive_dirs() { let mut lines = result.stdout_str().lines(); assert_hyperlink!( lines.next(), - &format!("{path}{separator}{dir_a}\x07{dir_a}\x1b]8;;\x07:") + &format!("{path}{separator}{dir_a}\x1b\\{dir_a}\x1b]8;;\x1b\\:") ); assert_hyperlink!( lines.next(), - &format!("{path}{separator}{dir_a}{separator}{dir_b}\x07{dir_b}\x1b]8;;\x07") + &format!("{path}{separator}{dir_a}{separator}{dir_b}\x1b\\{dir_b}\x1b]8;;\x1b\\") ); assert!(matches!(lines.next(), Some(l) if l.is_empty())); assert_hyperlink!( lines.next(), &format!( - "{path}{separator}{dir_a}{separator}{dir_b}\x07{dir_a}{separator}{dir_b}\x1b]8;;\x07:" + "{path}{separator}{dir_a}{separator}{dir_b}\x1b\\{dir_a}{separator}{dir_b}\x1b]8;;\x1b\\:" ) ); } +#[test] +fn test_ls_hyperlink_symlink_target_handling() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.mkdir("target_dir"); + at.touch("target_file.txt"); + at.symlink_file("target_file.txt", "link_to_file"); + at.symlink_dir("target_dir", "link_to_dir"); + at.symlink_file("nonexistent", "link_to_missing"); + + let result = scene + .ucmd() + .args(&["-l", "--hyperlink", "--color"]) + .succeeds(); + let output = result.stdout_str(); + + assert!(output.contains("\x1b]8;;file://")); + assert!(!output.contains('\x07')); + assert!(output.contains("\x1b\\")); + + let file_link_line = output + .lines() + .find(|line| line.contains("link_to_file")) + .unwrap(); + assert!(file_link_line.contains(" -> ")); + let target_part = file_link_line.split(" -> ").nth(1).unwrap(); + assert!(!target_part.contains("\x1b[")); + assert!(!target_part.ends_with("\x1b[K")); + + let dir_link_line = output + .lines() + .find(|line| line.contains("link_to_dir")) + .unwrap(); + assert!(dir_link_line.contains(" -> ")); + let target_part = dir_link_line.split(" -> ").nth(1).unwrap(); + assert!(target_part.contains("\x1b[")); + + let missing_link_line = output + .lines() + .find(|line| line.contains("link_to_missing")) + .unwrap(); + assert!(missing_link_line.contains(" -> ")); + let missing_target_part = missing_link_line.split(" -> ").nth(1).unwrap(); + assert!(missing_target_part.contains("\x1b[")); +} + +#[test] +fn test_ls_hyperlink_utf8_encoding() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.touch("café.txt"); + #[cfg(not(target_os = "windows"))] + at.touch("file:with:colons.txt"); + #[cfg(target_os = "windows")] + at.touch("file-with-colons.txt"); + at.touch("file with spaces.txt"); + + let result = scene.ucmd().args(&["--hyperlink"]).succeeds(); + let output = result.stdout_str(); + + assert!(output.contains("caf%c3%a9.txt")); + #[cfg(not(target_os = "windows"))] + assert!(output.contains("file%3awith%3acolons.txt")); + #[cfg(target_os = "windows")] + assert!(output.contains("file-with-colons.txt")); + assert!(output.contains("file%20with%20spaces.txt")); + + let hyperlink_count = output.matches("\x1b]8;;file://").count(); + let terminator_count = output.matches("\x1b\\").count(); + assert_eq!(terminator_count, hyperlink_count * 2); +} + #[test] fn test_ls_color_do_not_reset() { let scene: TestScenario = TestScenario::new(util_name!()); From e4e95a7c7463cbe91b6127a9dc8f1f10c72401ae Mon Sep 17 00:00:00 2001 From: Etienne Cordonnier Date: Tue, 10 Feb 2026 00:33:43 +0100 Subject: [PATCH 38/46] env: fix "--ignore-signal=PIPE" (#9618) - revert https://github.com/uutils/coreutils/pull/9614 because `Command::exec()` resets the default signal handler for SIGPIPE, interfering with the option `--ignore-signal=PIPE` - add regression-test for --ignore-signal=PIPE Signed-off-by: Etienne Cordonnier --- src/uu/env/src/env.rs | 55 ++++++++++++++++++++------- tests/by-util/test_env.rs | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 13 deletions(-) diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index c12cc671b..aab1ef482 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -24,18 +24,20 @@ use nix::sys::signal::{ SigHandler::{SigDfl, SigIgn}, SigSet, SigmaskHow, Signal, signal, sigprocmask, }; +#[cfg(unix)] +use nix::unistd::execvp; use std::borrow::Cow; #[cfg(unix)] use std::collections::{BTreeMap, BTreeSet}; use std::env; +#[cfg(unix)] +use std::ffi::CString; use std::ffi::{OsStr, OsString}; use std::io; use std::io::Write as _; use std::io::stderr; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; -#[cfg(unix)] -use std::os::unix::process::CommandExt; use uucore::display::{Quotable, print_all_env_vars}; use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError}; @@ -784,16 +786,40 @@ impl EnvAppData { #[cfg(unix)] { - // Execute the program using exec, which replaces the current process. - let err = std::process::Command::new(&*prog) - .arg0(&*arg0) - .args(args) - .exec(); + // Use execvp() directly to preserve signal handlers set by apply_signal_action(). + // Command::exec() would reset SIGPIPE, interfering with --ignore-signal=PIPE. - // exec() only returns if there was an error - match err.kind() { - io::ErrorKind::NotFound => Err(self.make_error_no_such_file_or_dir(&prog)), - io::ErrorKind::PermissionDenied => { + // Convert program name to CString. + let prog_os: &OsStr = prog.as_ref(); + let Ok(prog_cstring) = CString::new(prog_os.as_bytes()) else { + return Err(self.make_error_no_such_file_or_dir(&prog)); + }; + + // Prepare arguments for execvp. + let mut argv = Vec::new(); + + // Convert arg0 to CString. + let arg0_os: &OsStr = arg0.as_ref(); + let Ok(arg0_cstring) = CString::new(arg0_os.as_bytes()) else { + return Err(self.make_error_no_such_file_or_dir(&prog)); + }; + argv.push(arg0_cstring); + + // Convert remaining arguments to CString. + for arg in args { + let arg_os = arg; + let Ok(arg_cstring) = CString::new(arg_os.as_bytes()) else { + return Err(self.make_error_no_such_file_or_dir(&prog)); + }; + argv.push(arg_cstring); + } + + // Execute the program using execvp. this replaces the current + // process. The execvp function takes care of appending a NULL + // argument to the argument list so that we don't have to. + match execvp(&prog_cstring, &argv) { + Err(nix::errno::Errno::ENOENT) => Err(self.make_error_no_such_file_or_dir(&prog)), + Err(nix::errno::Errno::EACCES) => { uucore::show_error!( "{}", translate!( @@ -803,16 +829,19 @@ impl EnvAppData { ); Err(126.into()) } - _ => { + Err(_) => { uucore::show_error!( "{}", translate!( "env-error-unknown", - "error" => err + "error" => "execvp failed" ) ); Err(126.into()) } + Ok(_) => { + unreachable!("execvp should never return on success") + } } } diff --git a/tests/by-util/test_env.rs b/tests/by-util/test_env.rs index 8c488e9f3..074d29c29 100644 --- a/tests/by-util/test_env.rs +++ b/tests/by-util/test_env.rs @@ -1962,3 +1962,81 @@ fn test_non_utf8_env_vars() { .succeeds() .stdout_contains_bytes(b"NON_UTF8_VAR=hello\x80world"); } + +#[test] +#[cfg(unix)] +fn test_ignore_signal_pipe_broken_pipe_regression() { + // Test that --ignore-signal=PIPE properly ignores SIGPIPE in child processes. + // When SIGPIPE is ignored, processes should handle broken pipes gracefully + // instead of being terminated by the signal. + // + // Regression test for: https://github.com/uutils/coreutils/issues/9617 + + use std::io::{BufRead, BufReader}; + use std::process::{Command, Stdio}; + + let scene = TestScenario::new(util_name!()); + + // Helper function to simulate a broken pipe scenario (like "seq 1000000 | head -n1") + let test_sigpipe_behavior = |use_ignore_signal: bool| -> i32 { + let mut cmd = Command::new(&scene.bin_path); + cmd.arg("env"); + + if use_ignore_signal { + cmd.arg("--ignore-signal=PIPE"); + } + + // Use seq instead of yes - writes bounded output but enough to trigger SIGPIPE + cmd.arg("seq") + .arg("1") + .arg("1000000") + .stdout(Stdio::piped()) + .stderr(Stdio::null()); + + let mut child = cmd.spawn().expect("Failed to spawn env process"); + + // Read exactly one line then close the pipe to trigger SIGPIPE + if let Some(stdout) = child.stdout.take() { + let mut reader = BufReader::new(stdout); + let mut line = String::new(); + let _ = reader.read_line(&mut line); + // Pipe closes when reader is dropped, sending SIGPIPE to writing process + } + + // seq should exit quickly (either from SIGPIPE or after handling EPIPE) + match child.wait() { + Ok(status) => status.code().unwrap_or(141), // 128 + 13 + Err(_) => 141, + } + }; + + // Test without signal ignoring - should be killed by SIGPIPE + let normal_exit_code = test_sigpipe_behavior(false); + println!("Normal 'env seq' exit code: {normal_exit_code}"); + + // Test with --ignore-signal=PIPE - should handle broken pipe gracefully + let ignore_signal_exit_code = test_sigpipe_behavior(true); + println!("With --ignore-signal=PIPE exit code: {ignore_signal_exit_code}"); + + // Verify the --ignore-signal=PIPE flag changes the behavior + assert!( + ignore_signal_exit_code != 141, + "--ignore-signal=PIPE had no effect! Process was still killed by SIGPIPE (exit code 141). Normal: {normal_exit_code}, --ignore-signal: {ignore_signal_exit_code}" + ); + + // Expected behavior: + assert_eq!( + normal_exit_code, 141, + "Without --ignore-signal, process should be killed by SIGPIPE" + ); + assert_ne!( + ignore_signal_exit_code, 141, + "With --ignore-signal=PIPE, process should NOT be killed by SIGPIPE" + ); + + // Process should exit gracefully when SIGPIPE is ignored + assert!( + ignore_signal_exit_code == 0 || ignore_signal_exit_code == 1, + "With --ignore-signal=PIPE, process should exit gracefully (0 or 1), got: {ignore_signal_exit_code}" + ); +} From 7b0e7e4cbf9c530f90d056aae964edbabb442ea7 Mon Sep 17 00:00:00 2001 From: Reuben Wong Date: Tue, 10 Feb 2026 10:45:20 +0800 Subject: [PATCH 39/46] sync: return after checking all inputs (#10742) * sync: return after checking all inputs * fix missing error context * use set_exit_code and update translation templates to show errors * remove thiserror dependency * fix typo --------- Co-authored-by: Sylvestre Ledru Co-authored-by: Chris Dryden --- src/uu/sync/locales/en-US.ftl | 2 +- src/uu/sync/locales/fr-FR.ftl | 2 +- src/uu/sync/src/sync.rs | 26 ++++++++++++++++---------- tests/by-util/test_sync.rs | 8 ++++++++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/uu/sync/locales/en-US.ftl b/src/uu/sync/locales/en-US.ftl index b473c03d9..272e5a822 100644 --- a/src/uu/sync/locales/en-US.ftl +++ b/src/uu/sync/locales/en-US.ftl @@ -7,7 +7,7 @@ sync-help-data = sync only file data, no unneeded metadata (Linux only) # Error messages sync-error-data-needs-argument = --data needs at least one argument -sync-error-opening-file = error opening { $file } +sync-error-opening-file = error opening { $file }: { $err } sync-error-no-such-file = error opening { $file }: No such file or directory sync-error-syncing-file = error syncing { $file } diff --git a/src/uu/sync/locales/fr-FR.ftl b/src/uu/sync/locales/fr-FR.ftl index e2db79b90..31c51aa4d 100644 --- a/src/uu/sync/locales/fr-FR.ftl +++ b/src/uu/sync/locales/fr-FR.ftl @@ -7,7 +7,7 @@ sync-help-data = synchroniser seulement les données des fichiers, pas les méta # Messages d'erreur sync-error-data-needs-argument = --data nécessite au moins un argument -sync-error-opening-file = erreur lors de l'ouverture de { $file } +sync-error-opening-file = erreur lors de l'ouverture de { $file } : { $err } sync-error-no-such-file = erreur lors de l'ouverture de { $file } : Aucun fichier ou répertoire de ce type sync-error-syncing-file = erreur lors de la synchronisation de { $file } diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index 87873ceda..b91f216aa 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -14,10 +14,9 @@ use nix::fcntl::{OFlag, open}; use nix::sys::stat::Mode; use std::path::Path; use uucore::display::Quotable; -#[cfg(any(target_os = "linux", target_os = "android"))] -use uucore::error::FromIo; -use uucore::error::{UResult, USimpleError}; +use uucore::error::{UResult, USimpleError, get_exit_code, set_exit_code}; use uucore::format_usage; +use uucore::show_error; use uucore::translate; pub mod options { @@ -235,23 +234,30 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let path = Path::new(&f); if let Err(e) = open(path, OFlag::O_NONBLOCK, Mode::empty()) { if e != Errno::EACCES || (e == Errno::EACCES && path.is_dir()) { - e.map_err_context( - || translate!("sync-error-opening-file", "file" => f.quote()), - )?; + show_error!( + "{}", + translate!("sync-error-opening-file", "file" => f.quote(), "err" => e.desc()) + ); + set_exit_code(1); } } } #[cfg(not(any(target_os = "linux", target_os = "android")))] { if !Path::new(&f).exists() { - return Err(USimpleError::new( - 1, - translate!("sync-error-no-such-file", "file" => f.quote()), - )); + show_error!( + "{}", + translate!("sync-error-no-such-file", "file" => f.quote()) + ); + set_exit_code(1); } } } + if get_exit_code() != 0 { + return Err(USimpleError::new(1, "")); + } + #[allow(clippy::if_same_then_else)] if matches.get_flag(options::FILE_SYSTEM) { #[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))] diff --git a/tests/by-util/test_sync.rs b/tests/by-util/test_sync.rs index fe979cc0d..8c09d8637 100644 --- a/tests/by-util/test_sync.rs +++ b/tests/by-util/test_sync.rs @@ -168,6 +168,14 @@ fn test_sync_multiple_files() { new_ucmd!().arg("--data").arg(&file1).arg(&file2).succeeds(); } +#[test] +fn test_sync_multiple_nonexistent_files() { + let result = new_ucmd!().arg("--data").arg("bad1").arg("bad2").fails(); + + result.stderr_contains("sync: error opening 'bad1': No such file or directory"); + result.stderr_contains("sync: error opening 'bad2': No such file or directory"); +} + #[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_sync_data_fifo_fails_immediately() { From f59f86a5871a9c7d466007689998c2a44c203d0c Mon Sep 17 00:00:00 2001 From: Magnus Markling Date: Tue, 10 Feb 2026 07:19:07 +0100 Subject: [PATCH 40/46] head: improve help grammar --- src/uu/head/locales/en-US.ftl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/head/locales/en-US.ftl b/src/uu/head/locales/en-US.ftl index 9b2143206..25cabeaf2 100644 --- a/src/uu/head/locales/en-US.ftl +++ b/src/uu/head/locales/en-US.ftl @@ -7,10 +7,10 @@ head-usage = head [FLAG]... [FILE]... # Help messages head-help-bytes = print the first NUM bytes of each file; - with the leading '-', print all but the last + with a leading '-', print all but the last NUM bytes of each file head-help-lines = print the first NUM lines instead of the first 10; - with the leading '-', print all but the last + with a leading '-', print all but the last NUM lines of each file head-help-quiet = never print headers giving file names head-help-verbose = always print headers giving file names From 04b6f0ad1f1f746a3fd73c5bbb4145a3a0af27c3 Mon Sep 17 00:00:00 2001 From: cerdelen <95369756+cerdelen@users.noreply.github.com> Date: Tue, 10 Feb 2026 09:22:27 +0100 Subject: [PATCH 41/46] Unexpand use buffered reads + tests - improve performance by 58.43% (#10831) * unexpand: use buffered read and new tests for chunking read edge case behaviour * unexpand: tests use no fixtures + fix edgecase where blanks are divided by chunk bounds * unexpand: shrink chunk read edgecase tests --- src/uu/unexpand/src/unexpand.rs | 239 +++++++++++++++++++------------- tests/by-util/test_unexpand.rs | 89 +++++++++++- 2 files changed, 227 insertions(+), 101 deletions(-) diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 14c2b8b0b..cfedbe367 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -8,7 +8,7 @@ use clap::{Arg, ArgAction, Command}; use std::ffi::OsString; use std::fs::File; -use std::io::{BufRead, BufReader, BufWriter, Read, Stdout, Write, stdin, stdout}; +use std::io::{BufReader, BufWriter, Read, Stdout, Write, stdin, stdout}; use std::num::IntErrorKind; use std::path::Path; use std::str::from_utf8; @@ -347,7 +347,7 @@ fn next_tabstop(tab_config: &TabConfig, col: usize) -> Option { fn write_tabs( output: &mut BufWriter, tab_config: &TabConfig, - mut scol: usize, + scol: &mut usize, col: usize, prevtab: bool, init: bool, @@ -357,20 +357,20 @@ fn write_tabs( // We never turn a single space before a non-blank into // a tab, unless it's at the start of the line. let ai = init || amode; - if (ai && !prevtab && col > scol + 1) || (col > scol && (init || ai && prevtab)) { - while let Some(nts) = next_tabstop(tab_config, scol) { - if col < scol + nts { + if (ai && !prevtab && col > *scol + 1) || (col > *scol && (init || ai && prevtab)) { + while let Some(nts) = next_tabstop(tab_config, *scol) { + if col < *scol + nts { break; } output.write_all(b"\t")?; - scol += nts; + *scol += nts; } } - while col > scol { + while col > *scol { output.write_all(b" ")?; - scol += 1; + *scol += 1; } Ok(()) } @@ -424,81 +424,98 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi } #[allow(clippy::cognitive_complexity)] -fn unexpand_line( - buf: &mut Vec, +#[allow(clippy::too_many_arguments)] +fn unexpand_buf( + buf: &[u8], output: &mut BufWriter, options: &Options, lastcol: usize, tab_config: &TabConfig, + col: &mut usize, + scol: &mut usize, + leading: &mut bool, + pctype: &mut CharType, ) -> UResult<()> { - // Fast path: if we're not converting all spaces (-a flag not set) - // and the line doesn't start with spaces, just write it directly - if !options.aflag && !buf.is_empty() && buf[0] != b' ' && buf[0] != b'\t' { - output.write_all(buf)?; - buf.truncate(0); - return Ok(()); - } - - let mut byte = 0; // offset into the buffer - let mut col = 0; // the current column - let mut scol = 0; // the start col for the current span, i.e., the already-printed width - let mut init = true; // are we at the start of the line? - let mut pctype = CharType::Other; - - // Fast path for leading spaces in non-UTF8 mode: count consecutive spaces/tabs at start - if !options.uflag && !options.aflag { - // In default mode (not -a), we only convert leading spaces - // So we can batch process them and then copy the rest - while byte < buf.len() { - match buf[byte] { - b' ' => { - col += 1; - byte += 1; - } - b'\t' => { - col += next_tabstop(tab_config, col).unwrap_or(1); - byte += 1; - pctype = CharType::Tab; - } - _ => break, - } - } - - // If we found spaces/tabs, write them as tabs - if byte > 0 { - write_tabs( - output, - tab_config, - 0, - col, - pctype == CharType::Tab, - true, - true, - )?; - } - - // Write the rest of the line directly (no more tab conversion needed) - if byte < buf.len() { - output.write_all(&buf[byte..])?; - } - buf.truncate(0); - return Ok(()); - } - - while byte < buf.len() { - // when we have a finite number of columns, never convert past the last column - if lastcol > 0 && col >= lastcol { + // We can only fast forward if we don't need to calculate col/scol + if let Some(b'\n') = buf.last() { + // Fast path: if we're not converting all spaces (-a flag not set) + // and the line doesn't start with spaces, just write it directly + if !options.aflag && !buf.is_empty() && ((buf[0] != b' ' && buf[0] != b'\t') || !*leading) { write_tabs( output, tab_config, scol, - col, - pctype == CharType::Tab, - init, + *col, + *pctype == CharType::Tab, + *leading, + options.aflag, + )?; + *scol = *col; + *col += buf.len(); + output.write_all(buf)?; + return Ok(()); + } + } + + let mut byte = 0; // offset into the buffer + + // We can only fast forward if we don't need to calculate col/scol + if let Some(b'\n') = buf.last() { + // Fast path for leading spaces in non-UTF8 mode: count consecutive spaces/tabs at start + if !options.uflag && !options.aflag && *leading { + // In default mode (not -a), we only convert leading spaces + // So we can batch process them and then copy the rest + while byte < buf.len() { + match buf[byte] { + b' ' => { + *col += 1; + byte += 1; + } + b'\t' => { + *col += next_tabstop(tab_config, *col).unwrap_or(1); + byte += 1; + *pctype = CharType::Tab; + } + _ => break, + } + } + + // If we found spaces/tabs, write them as tabs + if byte > 0 { + write_tabs( + output, + tab_config, + scol, + *col, + *pctype == CharType::Tab, + true, + options.aflag, + )?; + } + + // Write the rest of the line directly (no more tab conversion needed) + if byte < buf.len() { + *leading = false; + output.write_all(&buf[byte..])?; + } + return Ok(()); + } + } + + while byte < buf.len() { + // when we have a finite number of columns, never convert past the last column + if lastcol > 0 && *col >= lastcol { + write_tabs( + output, + tab_config, + scol, + *col, + *pctype == CharType::Tab, + *leading, true, )?; output.write_all(&buf[byte..])?; - scol = col; + *scol = *col; break; } @@ -506,19 +523,19 @@ fn unexpand_line( let (ctype, cwidth, nbytes) = next_char_info(options.uflag, buf, byte); // now figure out how many columns this char takes up, and maybe print it - let tabs_buffered = init || options.aflag; + let tabs_buffered = *leading || options.aflag; match ctype { CharType::Space | CharType::Tab => { // compute next col, but only write space or tab chars if not buffering - col += if ctype == CharType::Space { + *col += if ctype == CharType::Space { 1 } else { - next_tabstop(tab_config, col).unwrap_or(1) + next_tabstop(tab_config, *col).unwrap_or(1) }; if !tabs_buffered { output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // now printed up to this column + *scol = *col; // now printed up to this column } } CharType::Other | CharType::Backspace => { @@ -527,42 +544,30 @@ fn unexpand_line( output, tab_config, scol, - col, - pctype == CharType::Tab, - init, + *col, + *pctype == CharType::Tab, + *leading, options.aflag, )?; - init = false; // no longer at the start of a line - col = if ctype == CharType::Other { + *leading = false; // no longer at the start of a line + *col = if ctype == CharType::Other { // use computed width - col + cwidth - } else if col > 0 { + *col + cwidth + } else if *col > 0 { // Backspace case, but only if col > 0 - col - 1 + *col - 1 } else { 0 }; output.write_all(&buf[byte..byte + nbytes])?; - scol = col; // we've now printed up to this column + *scol = *col; // we've now printed up to this column } } byte += nbytes; // move on to next char - pctype = ctype; // save the previous type + *pctype = ctype; // save the previous type } - // write out anything remaining - write_tabs( - output, - tab_config, - scol, - col, - pctype == CharType::Tab, - init, - true, - )?; - buf.truncate(0); // clear out the buffer - Ok(()) } @@ -573,15 +578,49 @@ fn unexpand_file( lastcol: usize, tab_config: &TabConfig, ) -> UResult<()> { - let mut buf = Vec::new(); + let mut buf = [0u8; 4096]; let mut input = open(file)?; + let mut col = 0; + let mut scol = 0; + let mut leading = true; + let mut pctype = CharType::Other; loop { - match input.read_until(b'\n', &mut buf) { + match input.read(&mut buf) { Ok(0) => break, - Ok(_) => unexpand_line(&mut buf, output, options, lastcol, tab_config)?, + Ok(n) => { + for line in buf[..n].split_inclusive(|b| *b == b'\n') { + unexpand_buf( + line, + output, + options, + lastcol, + tab_config, + &mut col, + &mut scol, + &mut leading, + &mut pctype, + )?; + if let Some(b'\n') = line.last() { + col = 0; + scol = 0; + leading = true; + pctype = CharType::Other; + } + } + } Err(e) => return Err(e.map_err_context(|| file.maybe_quote().to_string())), } } + // write out anything remaining + write_tabs( + output, + tab_config, + &mut scol, + col, + pctype == CharType::Tab, + leading, + options.aflag, + )?; Ok(()) } diff --git a/tests/by-util/test_unexpand.rs b/tests/by-util/test_unexpand.rs index d29fecfd3..c6d481f53 100644 --- a/tests/by-util/test_unexpand.rs +++ b/tests/by-util/test_unexpand.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // -// spell-checker:ignore contenta +// spell-checker:ignore contenta edgecase behaviour use uutests::{at_and_ucmd, new_ucmd}; @@ -366,3 +366,90 @@ fn test_extended_tabstop_syntax() { .stdout_is(expected); } } + +#[test] +fn test_buffered_read_edgecase_behaviour() { + // reads are done in 4096 chunks. Tests edgecase spaces around chunk bounds + let test_cases = [ + { + // input has newlines in first chunk and has leading spaces after newline in chunk + let mut input = vec![b'0'; 180]; + input.push(b'\n'); + input.extend([b' '; 8]); + input.extend([b'0'; 3897]); + input.push(b'\n'); // 180 '0' -> 'n' -> 8 spaces -> 3897 '0' -> \n + + let mut expected = vec![b'0'; 180]; + expected.push(b'\n'); + expected.push(b'\t'); + expected.extend([b'0'; 3897]); + expected.push(b'\n'); // 180 '0' -> 'n' -> 1 tab -> 3897 '0' -> \n + (input, expected) + }, + { + // input has newline after first chunk with leading spaces + let mut input = vec![b'0'; 4096]; + input.extend("\n 0000\n".as_bytes()); // 4096 '0' -> \n -> 8 spaces -> 4 '0' -> \n + + let mut expected = vec![b'0'; 4096]; + expected.extend("\n\t0000\n".as_bytes()); // 4096 '0' -> \n -> 1 tab -> 4 '0' -> \n + (input, expected) + }, + { + // fixture has newlines in first chunk and has leading spaces after newline in chunk + let mut input = vec![b'0'; 4095]; + input.extend("\n 0000\n".as_bytes()); // 4095 '0' -> \n -> 8 spaces -> 4 '0' -> \n + + let mut expected = vec![b'0'; 4095]; + expected.extend("\n\t0000\n".as_bytes()); // 4095 '0' -> \n -> 1 tab -> 4 '0' -> \n + (input, expected) + }, + { + // input has trailing spaces in the first chunk (should not be unexpanded) into newline with leading + // spaces which should be unexpanded + let mut input = vec![b'0'; 4088]; + input.extend(" \n 0000\n".as_bytes()); // 4088 '0' -> 8 spaces -> \n -> 8 spaces -> 4 '0' -> \n + + let mut expected = vec![b'0'; 4088]; + expected.extend(" \n\t0000\n".as_bytes()); // 4088 '0' -> 8 spaces -> \n -> 8 spaces -> 4 '0' -> \n + (input, expected) + }, + { + // input has a trailing normal chars after new line in first chunk into leading spaces for new + // chunk (should not be unexpanded) + let mut input = vec![b'0'; 4087]; + input.extend("\n00000000 \n".as_bytes()); // 4087 '0' -> \n -> 8 '0' -> 8 spaces -> \n + + let mut expected = vec![b'0'; 4087]; + expected.extend("\n00000000 \n".as_bytes()); // 4087 '0' -> \n -> 8 '0' -> 8 spaces -> \n + (input, expected) + }, + { + // input has a trailing blanks after new line in first chunk (should be unexpanded) into leading spaces for new + // chunk (should be unexpanded) + let mut input = vec![b'0'; 4087]; + input.extend("\n \n".as_bytes()); // 4087 '0' -> 16 spaces -> \n + + let mut expected = vec![b'0'; 4087]; + expected.extend("\n\t\t\n".as_bytes()); // 4087 '0' -> 2 tabs -> \n + (input, expected) + }, + { + // input has a trailing blanks after new line in first chunk (should be unexpanded) into leading spaces for new + // chunk (should be unexpanded) (tests space counting is done over chunk bounds) + let mut input = vec![b'0'; 4091]; + input.extend("\n \n".as_bytes()); // 4091 '0' -> 8 spaces -> \n + + let mut expected = vec![b'0'; 4091]; + expected.extend("\n\t\n".as_bytes()); // 4091 '0' -> 1 tab -> \n + (input, expected) + }, + ]; + + for (input, expected) in test_cases { + new_ucmd!() + .pipe_in(input) + .succeeds() + .stdout_only(String::from_utf8(expected).unwrap()); + } +} From c5ca269d87d86f8b3a995fb5597fcde04f82752a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 22:38:10 +0000 Subject: [PATCH 42/46] chore(deps): update rust crate libc to v0.2.181 --- Cargo.lock | 4 ++-- fuzz/Cargo.lock | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e517cd19e..91dab319a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1765,9 +1765,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.181" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5" [[package]] name = "libloading" diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index c4d05638d..c6391a169 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -1055,9 +1055,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.180" +version = "0.2.181" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5" [[package]] name = "libfuzzer-sys" From b4a4a38b9ad0fe5a1bfbba49c148ab16ef9367ad Mon Sep 17 00:00:00 2001 From: Chris Dryden Date: Tue, 10 Feb 2026 03:25:11 -0500 Subject: [PATCH 43/46] paste: support multi-byte delimiters and GNU escape sequences (#10840) * paste: support multi-byte delimiters and GNU escape sequences * paste: address review comments - avoid OsString clone and guard mb_char_len --- src/uu/paste/Cargo.toml | 2 +- src/uu/paste/src/paste.rs | 101 ++++++-------- src/uucore/Cargo.toml | 3 +- src/uucore/src/lib/features/i18n/charmap.rs | 140 ++++++++++++++++++++ src/uucore/src/lib/features/i18n/mod.rs | 2 + tests/by-util/test_paste.rs | 52 ++++++++ util/build-gnu.sh | 3 + 7 files changed, 241 insertions(+), 62 deletions(-) create mode 100644 src/uucore/src/lib/features/i18n/charmap.rs diff --git a/src/uu/paste/Cargo.toml b/src/uu/paste/Cargo.toml index 76f0919da..eee948150 100644 --- a/src/uu/paste/Cargo.toml +++ b/src/uu/paste/Cargo.toml @@ -20,7 +20,7 @@ path = "src/paste.rs" [dependencies] clap = { workspace = true } -uucore = { workspace = true } +uucore = { workspace = true, features = ["i18n-charmap"] } fluent = { workspace = true } [[bin]] diff --git a/src/uu/paste/src/paste.rs b/src/uu/paste/src/paste.rs index 5d6ae5ed9..b0f51e8ce 100644 --- a/src/uu/paste/src/paste.rs +++ b/src/uu/paste/src/paste.rs @@ -14,6 +14,7 @@ use std::rc::Rc; use std::slice::Iter; use uucore::error::{UResult, USimpleError}; use uucore::format_usage; +use uucore::i18n::charmap::mb_char_len; use uucore::line_ending::LineEnding; use uucore::translate; @@ -29,7 +30,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?; let serial = matches.get_flag(options::SERIAL); - let delimiters = matches.get_one::(options::DELIMITER).unwrap(); + let delimiters = matches.get_one::(options::DELIMITER).unwrap(); let files = matches .get_many::(options::FILE) .unwrap() @@ -61,7 +62,8 @@ pub fn uu_app() -> Command { .help(translate!("paste-help-delimiter")) .value_name("LIST") .default_value("\t") - .hide_default_value(true), + .hide_default_value(true) + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(options::FILE) @@ -84,7 +86,7 @@ pub fn uu_app() -> Command { fn paste( filenames: Vec, serial: bool, - delimiters: &str, + delimiters: &OsString, line_ending: LineEnding, ) -> UResult<()> { let unescaped_and_encoded_delimiters = parse_delimiters(delimiters)?; @@ -185,65 +187,44 @@ fn paste( Ok(()) } -fn parse_delimiters(delimiters: &str) -> UResult]>> { - /// A single backslash char - const BACKSLASH: char = '\\'; +fn parse_delimiters(delimiters: &OsString) -> UResult]>> { + let bytes = uucore::os_str_as_bytes(delimiters)?; + let mut vec = Vec::>::with_capacity(bytes.len()); + let mut i = 0; - fn add_one_byte_single_char_delimiter(vec: &mut Vec>, byte: u8) { - vec.push(Box::new([byte])); - } - - // a buffer of length four is large enough to encode any char - let mut buffer = [0; 4]; - - let mut add_single_char_delimiter = |vec: &mut Vec>, ch: char| { - let delimiter_encoded = ch.encode_utf8(&mut buffer); - - vec.push(Box::<[u8]>::from(delimiter_encoded.as_bytes())); - }; - - let mut vec = Vec::>::with_capacity(delimiters.len()); - - let mut chars = delimiters.chars(); - - // Unescape all special characters - while let Some(char) = chars.next() { - match char { - BACKSLASH => match chars.next() { - // "Empty string (not a null character)" - // https://pubs.opengroup.org/onlinepubs/9799919799/utilities/paste.html - Some('0') => { - vec.push(Box::<[u8; 0]>::new([])); - } - // "\\" to "\" (U+005C) - Some(BACKSLASH) => { - add_one_byte_single_char_delimiter(&mut vec, b'\\'); - } - // "\n" to U+000A - Some('n') => { - add_one_byte_single_char_delimiter(&mut vec, b'\n'); - } - // "\t" to U+0009 - Some('t') => { - add_one_byte_single_char_delimiter(&mut vec, b'\t'); - } - Some(other_char) => { - // "If any other characters follow the , the results are unspecified." - // https://pubs.opengroup.org/onlinepubs/9799919799/utilities/paste.html - // However, other implementations remove the backslash - // See "test_posix_unspecified_delimiter" - add_single_char_delimiter(&mut vec, other_char); - } - None => { - return Err(USimpleError::new( - 1, - translate!("paste-error-delimiter-unescaped-backslash", "delimiters" => delimiters), - )); - } - }, - non_backslash_char => { - add_single_char_delimiter(&mut vec, non_backslash_char); + while i < bytes.len() { + if bytes[i] == b'\\' { + i += 1; + if i >= bytes.len() { + return Err(USimpleError::new( + 1, + translate!("paste-error-delimiter-unescaped-backslash", "delimiters" => delimiters.to_string_lossy()), + )); } + match bytes[i] { + b'0' => vec.push(Box::new([])), + b'\\' => vec.push(Box::new([b'\\'])), + b'n' => vec.push(Box::new([b'\n'])), + b't' => vec.push(Box::new([b'\t'])), + b'b' => vec.push(Box::new([b'\x08'])), + b'f' => vec.push(Box::new([b'\x0C'])), + b'r' => vec.push(Box::new([b'\r'])), + b'v' => vec.push(Box::new([b'\x0B'])), + _ => { + // Unknown escape: strip backslash, use the following character(s) + let remaining = &bytes[i..]; + let len = mb_char_len(remaining).min(remaining.len()); + vec.push(Box::from(&bytes[i..i + len])); + i += len; + continue; + } + } + i += 1; + } else { + let remaining = &bytes[i..]; + let len = mb_char_len(remaining).min(remaining.len()); + vec.push(Box::from(&bytes[i..i + len])); + i += len; } } diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index a914f9117..29a7189d4 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -151,7 +151,8 @@ format = [ "quoting-style", "unit-prefix", ] -i18n-all = ["i18n-collator", "i18n-decimal", "i18n-datetime"] +i18n-all = ["i18n-charmap", "i18n-collator", "i18n-decimal", "i18n-datetime"] +i18n-charmap = ["i18n-common"] i18n-common = ["icu_locale"] i18n-collator = ["i18n-common", "icu_collator"] i18n-decimal = ["i18n-common", "icu_decimal", "icu_provider"] diff --git a/src/uucore/src/lib/features/i18n/charmap.rs b/src/uucore/src/lib/features/i18n/charmap.rs new file mode 100644 index 000000000..2ec99229b --- /dev/null +++ b/src/uucore/src/lib/features/i18n/charmap.rs @@ -0,0 +1,140 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// spell-checker:ignore langinfo charmap eucjp euckr euctw CTYPE HKSCS hkscs localedata + +//! Locale-aware multi-byte character length detection via `LC_CTYPE`. + +use std::sync::OnceLock; + +enum MbEncoding { + Utf8, + Gb18030, + EucJp, + EucKr, + Big5, +} + +fn encoding_from_name(enc: &str) -> MbEncoding { + match enc { + "gb18030" | "gbk" | "gb2312" => MbEncoding::Gb18030, + "euc-jp" | "eucjp" => MbEncoding::EucJp, + "euc-kr" | "euckr" => MbEncoding::EucKr, + "big5" | "big5-hkscs" | "big5hkscs" | "euc-tw" | "euctw" => MbEncoding::Big5, + _ => MbEncoding::Utf8, + } +} + +fn get_encoding() -> &'static MbEncoding { + static ENCODING: OnceLock = OnceLock::new(); + ENCODING.get_or_init(|| { + let val = ["LC_ALL", "LC_CTYPE", "LANG"] + .iter() + .find_map(|&k| std::env::var(k).ok().filter(|v| !v.is_empty())); + let s = match val.as_deref() { + Some(s) if s != "C" && s != "POSIX" => s, + _ => return MbEncoding::Utf8, + }; + if let Some(enc) = s.split('.').nth(1) { + let enc = enc.split('@').next().unwrap_or(enc); + encoding_from_name(&enc.to_ascii_lowercase()) + } else { + // Bare locale defaults from glibc localedata/SUPPORTED + match s.split('@').next().unwrap_or(s) { + "zh_CN" | "zh_SG" => MbEncoding::Gb18030, + "zh_TW" | "zh_HK" => MbEncoding::Big5, + _ => MbEncoding::Utf8, + } + } + }) +} + +/// Byte length of the first character in `bytes` under the current locale encoding. +pub fn mb_char_len(bytes: &[u8]) -> usize { + debug_assert!(!bytes.is_empty()); + let b0 = bytes[0]; + if b0 <= 0x7F { + return 1; + } + match get_encoding() { + MbEncoding::Utf8 => utf8_len(bytes, b0), + MbEncoding::Gb18030 => gb18030_len(bytes, b0), + MbEncoding::EucJp => eucjp_len(bytes, b0), + MbEncoding::EucKr => euckr_len(bytes, b0), + MbEncoding::Big5 => big5_len(bytes, b0), + } +} + +// All helpers below assume b0 > 0x7F (ASCII already handled by caller). + +fn utf8_len(b: &[u8], b0: u8) -> usize { + let n = match b0 { + 0xC2..=0xDF => 2, + 0xE0..=0xEF => 3, + 0xF0..=0xF4 => 4, + _ => return 1, + }; + if b.len() >= n && b[1..n].iter().all(|&c| c & 0xC0 == 0x80) { + n + } else { + 1 + } +} + +// 2-byte: [81-FE][40-7E,80-FE] 4-byte: [81-FE][30-39][81-FE][30-39] +fn gb18030_len(b: &[u8], b0: u8) -> usize { + if !(0x81..=0xFE).contains(&b0) { + return 1; + } + if b.len() >= 4 + && (0x30..=0x39).contains(&b[1]) + && (0x81..=0xFE).contains(&b[2]) + && (0x30..=0x39).contains(&b[3]) + { + return 4; + } + if b.len() >= 2 && ((0x40..=0x7E).contains(&b[1]) || (0x80..=0xFE).contains(&b[1])) { + return 2; + } + 1 +} + +// 3-byte: [8F][A1-FE][A1-FE] 2-byte: [8E][A1-DF] or [A1-FE][A1-FE] +fn eucjp_len(b: &[u8], b0: u8) -> usize { + if b0 == 0x8F && b.len() >= 3 && (0xA1..=0xFE).contains(&b[1]) && (0xA1..=0xFE).contains(&b[2]) + { + return 3; + } + if b.len() >= 2 { + if b0 == 0x8E && (0xA1..=0xDF).contains(&b[1]) { + return 2; + } + if (0xA1..=0xFE).contains(&b0) && (0xA1..=0xFE).contains(&b[1]) { + return 2; + } + } + 1 +} + +// 2-byte: [A1-FE][A1-FE] +fn euckr_len(b: &[u8], b0: u8) -> usize { + if (0xA1..=0xFE).contains(&b0) && b.len() >= 2 && (0xA1..=0xFE).contains(&b[1]) { + 2 + } else { + 1 + } +} + +// 2-byte: [81-FE][40-7E,A1-FE] +fn big5_len(b: &[u8], b0: u8) -> usize { + if (0x81..=0xFE).contains(&b0) + && b.len() >= 2 + && ((0x40..=0x7E).contains(&b[1]) || (0xA1..=0xFE).contains(&b[1])) + { + 2 + } else { + 1 + } +} diff --git a/src/uucore/src/lib/features/i18n/mod.rs b/src/uucore/src/lib/features/i18n/mod.rs index e8e0f3f3c..13629710c 100644 --- a/src/uucore/src/lib/features/i18n/mod.rs +++ b/src/uucore/src/lib/features/i18n/mod.rs @@ -7,6 +7,8 @@ use std::sync::OnceLock; use icu_locale::{Locale, locale}; +#[cfg(feature = "i18n-charmap")] +pub mod charmap; #[cfg(feature = "i18n-collator")] pub mod collator; #[cfg(feature = "i18n-datetime")] diff --git a/tests/by-util/test_paste.rs b/tests/by-util/test_paste.rs index a87f21598..11767e82a 100644 --- a/tests/by-util/test_paste.rs +++ b/tests/by-util/test_paste.rs @@ -135,6 +135,30 @@ const EXAMPLE_DATA: &[TestData] = &[ ins: &["1 \na \n", "2\t\nb\t\n"], out: "1 |2\t\na |b\t\n", }, + TestData { + name: "utf8-2byte-delim", + args: &["-d", "\u{00A2}"], + ins: &["1\n2\n", "a\nb\n"], + out: "1\u{00A2}a\n2\u{00A2}b\n", + }, + TestData { + name: "utf8-3byte-delim", + args: &["-d", "\u{20AC}"], + ins: &["1\n2\n", "a\nb\n"], + out: "1\u{20AC}a\n2\u{20AC}b\n", + }, + TestData { + name: "utf8-4byte-delim", + args: &["-d", "\u{1F600}", "-s"], + ins: &["1\n2\n3\n"], + out: "1\u{1F600}2\u{1F600}3\n", + }, + TestData { + name: "utf8-multi-delim-cycle", + args: &["-d", "\u{00A2}\u{20AC}"], + ins: &["a\nb\nc\n", "1\n2\n3\n", "x\ny\nz\n"], + out: "a\u{00A2}1\u{20AC}x\nb\u{00A2}2\u{20AC}y\nc\u{00A2}3\u{20AC}z\n", + }, ]; #[test] @@ -334,6 +358,19 @@ fn test_backslash_zero_delimiter() { } } +#[test] +fn test_gnu_escape_sequences() { + let cases: &[(&str, u8)] = &[(r"\b", 0x08), (r"\f", 0x0C), (r"\r", 0x0D), (r"\v", 0x0B)]; + for &(esc, byte) in cases { + let expected = [b'1', byte, b'2', byte, b'3', b'\n']; + new_ucmd!() + .args(&["-s", "-d", esc]) + .pipe_in("1\n2\n3\n") + .succeeds() + .stdout_only_bytes(expected); + } +} + // As of 2024-10-09, only bsdutils (https://github.com/dcantrell/bsdutils, derived from FreeBSD) and toybox handle // multibyte delimiter characters in the way a user would likely expect. BusyBox and GNU Core Utilities do not. #[test] @@ -378,6 +415,21 @@ fn test_data() { } } +#[test] +#[cfg(target_os = "linux")] +fn test_non_utf8_delimiter() { + let (at, mut ucmd) = at_and_ucmd!(); + at.write("f1", "1\n2\n"); + at.write("f2", "a\nb\n"); + let delim = std::ffi::OsString::from_vec(vec![0xA2, 0xE3]); + ucmd.env("LC_ALL", "zh_CN.gb18030") + .arg("-d") + .arg(&delim) + .args(&["f1", "f2"]) + .succeeds() + .stdout_only_bytes(b"1\xA2\xE3a\n2\xA2\xE3b\n"); +} + #[test] #[cfg(target_os = "linux")] fn test_paste_non_utf8_paths() { diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 7cda49fbe..9b1477f7b 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -162,6 +162,9 @@ fi grep -rl 'path_prepend_' tests/* | xargs -r "${SED}" -i 's| path_prepend_ ./src||' # path_prepend_ sets $abs_path_dir_: set it manually instead. grep -rl '\$abs_path_dir_' tests/*/*.sh | xargs -r "${SED}" -i "s|\$abs_path_dir_|${UU_BUILD_DIR//\//\\/}|g" +# Some tests use $abs_top_builddir/src for shebangs: point them to the uutils build dir. +grep -rl '\$abs_top_builddir/src' tests/*/*.sh tests/*/*.pl | xargs -r "${SED}" -i "s|\$abs_top_builddir/src|${UU_BUILD_DIR//\//\\/}|g" +grep -rl '\$ENV{abs_top_builddir}/src' tests/*/*.pl | xargs -r "${SED}" -i "s|\$ENV{abs_top_builddir}/src|${UU_BUILD_DIR//\//\\/}|g" # We can't build runcon and chcon without libselinux. But GNU no longer builds dummies of them. So consider they are SELinux specific. sed -i 's/^print_ver_.*/require_selinux_/' tests/runcon/runcon-compute.sh From 4e2da1a99a2d141e78b194e3f0b32477ee15039e Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Tue, 10 Feb 2026 19:53:34 +0900 Subject: [PATCH 44/46] sort: Use ahash - improve performance by 13.54% (#10783) --- Cargo.lock | 21 +++++++---- Cargo.toml | 4 +-- fuzz/Cargo.lock | 77 ++++++++++++++++++++++------------------- src/uu/sort/Cargo.toml | 4 +-- src/uu/sort/src/sort.rs | 25 +++++-------- 5 files changed, 69 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91dab319a..dabc2e4fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy 0.8.39", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -1093,12 +1106,6 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foldhash" version = "0.1.5" @@ -4027,6 +4034,7 @@ dependencies = [ name = "uu_sort" version = "0.6.0" dependencies = [ + "ahash", "bigdecimal", "binary-heap-plus", "clap", @@ -4034,7 +4042,6 @@ dependencies = [ "compare", "ctrlc", "fluent", - "fnv", "itertools 0.14.0", "memchr", "nix", diff --git a/Cargo.toml b/Cargo.toml index 34d2b069e..c358721eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ # coreutils (uutils) # * see the repository LICENSE, README, and CONTRIBUTING files for more information -# spell-checker:ignore (libs) bigdecimal datetime serde gethostid kqueue libselinux mangen memmap uuhelp startswith constness expl unnested logind cfgs interner +# spell-checker:ignore (libs) ahash bigdecimal datetime serde gethostid kqueue libselinux mangen memmap uuhelp startswith constness expl unnested logind cfgs interner [package] name = "coreutils" @@ -311,6 +311,7 @@ readme = "README.package.md" version = "0.6.0" [workspace.dependencies] +ahash = "0.8.12" ansi-width = "0.1.0" bigdecimal = "0.4" binary-heap-plus = "0.5.0" @@ -329,7 +330,6 @@ dns-lookup = { version = "3.0.0" } exacl = "0.12.0" file_diff = "1.0.0" filetime = "0.2.23" -fnv = "1.0.7" fs_extra = "1.3.0" fts-sys = "0.2.16" gcd = "2.3" diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index c6391a169..bfc7f4383 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -8,6 +8,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -207,9 +220,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.54" +version = "1.2.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" dependencies = [ "find-msvc-tools", "jobserver", @@ -242,18 +255,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.54" +version = "4.5.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.5.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238" dependencies = [ "anstream", "anstyle", @@ -518,9 +531,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixed_decimal" @@ -535,9 +548,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -588,12 +601,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "generic-array" version = "0.14.7" @@ -647,9 +654,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -974,9 +981,9 @@ checksum = "84de9d95a6d2547d9b77ee3f25fa0ee32e3c3a6484d47a55adebc0439c077992" [[package]] name = "jiff" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +checksum = "d89a5b5e10d5a9ad6e5d1f4bd58225f655d6fe9767575a5e8ac5a6fe64e04495" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -1000,9 +1007,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +checksum = "ff7a39c8862fc1369215ccf0a8f12dd4598c7f6484704359f0351bd617034dbf" dependencies = [ "proc-macro2", "quote", @@ -1105,9 +1112,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "miniz_oxide" @@ -1261,15 +1268,15 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" dependencies = [ "portable-atomic", ] @@ -1393,9 +1400,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" [[package]] name = "rust-ini" @@ -1820,13 +1827,13 @@ dependencies = [ name = "uu_sort" version = "0.6.0" dependencies = [ + "ahash", "bigdecimal", "binary-heap-plus", "clap", "compare", "ctrlc", "fluent", - "fnv", "itertools", "memchr", "nix", @@ -2271,18 +2278,18 @@ checksum = "9b3a41ce106832b4da1c065baa4c31cf640cf965fa1483816402b7f6b96f0a64" [[package]] name = "zerocopy" -version = "0.8.34" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71ddd76bcebeed25db614f82bf31a9f4222d3fbba300e6fb6c00afa26cbd4d9d" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.34" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8187381b52e32220d50b255276aa16a084ec0a9017a0ca2152a1f55c539758d" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 2aba9c73b..92bca5884 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -1,4 +1,4 @@ -# spell-checker:ignore bigdecimal +# spell-checker:ignore ahash bigdecimal [package] name = "uu_sort" @@ -29,7 +29,6 @@ bigdecimal = { workspace = true } binary-heap-plus = { workspace = true } clap = { workspace = true } compare = { workspace = true } -fnv = { workspace = true } itertools = { workspace = true } memchr = { workspace = true } rand = { workspace = true } @@ -45,6 +44,7 @@ uucore = { workspace = true, features = [ "i18n-collator", ] } fluent = { workspace = true } +ahash = { workspace = true } [target.'cfg(not(target_os = "redox"))'.dependencies] ctrlc = { workspace = true } diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 26b060a45..52247c4fe 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -18,14 +18,13 @@ mod merge; mod numeric_str_cmp; mod tmp_dir; +use ahash::AHashMap; use bigdecimal::BigDecimal; use chunks::LineData; use clap::builder::ValueParser; use clap::{Arg, ArgAction, ArgMatches, Command}; use custom_str_cmp::custom_str_cmp; - use ext_sort::ext_sort; -use fnv::FnvHasher; use numeric_str_cmp::{NumInfo, NumInfoParseSettings, human_numeric_str_cmp, numeric_str_cmp}; use rand::{Rng, rng}; use rayon::prelude::*; @@ -33,7 +32,7 @@ use std::cmp::Ordering; use std::env; use std::ffi::{OsStr, OsString}; use std::fs::{File, OpenOptions}; -use std::hash::{Hash, Hasher}; +use std::hash::{BuildHasher, Hash, Hasher}; use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout}; use std::num::{IntErrorKind, NonZero}; use std::ops::Range; @@ -1681,7 +1680,7 @@ fn index_legacy_warnings(processed_args: &[OsString], legacy_warnings: &mut [Leg return; } - let mut index_by_arg = std::collections::HashMap::new(); + let mut index_by_arg = AHashMap::default(); for (warning_idx, warning) in legacy_warnings.iter().enumerate() { index_by_arg.insert(warning.arg_index, warning_idx); } @@ -2909,7 +2908,8 @@ fn salt_from_random_source(path: &Path) -> UResult<[u8; SALT_LEN]> { let mut reader = open_with_open_failed_error(path)?; let mut buf = [0u8; BUF_LEN]; let mut total = 0usize; - let mut hasher = FnvHasher::default(); + // freeze seed for --random-source + let mut hasher = ahash::RandomState::with_seeds(1, 1, 1, 1).build_hasher(); loop { let n = reader @@ -2934,7 +2934,8 @@ fn salt_from_random_source(path: &Path) -> UResult<[u8; SALT_LEN]> { } let first = hasher.finish(); - let mut second_hasher = FnvHasher::default(); + // freeze seed for --random-source + let mut second_hasher = ahash::RandomState::with_seeds(2, 2, 2, 2).build_hasher(); second_hasher.write(RANDOM_SOURCE_TAG); second_hasher.write_u64(first); let second = second_hasher.finish(); @@ -2946,9 +2947,8 @@ fn salt_from_random_source(path: &Path) -> UResult<[u8; SALT_LEN]> { } fn get_hash(t: &T) -> u64 { - let mut s = FnvHasher::default(); - t.hash(&mut s); - s.finish() + // Is reproducibility of get_hash itself needed for --random-source ? + ahash::RandomState::with_seeds(0, 0, 0, 0).hash_one(t) } fn random_shuffle(a: &[u8], b: &[u8], salt: &[u8]) -> Ordering { @@ -3086,13 +3086,6 @@ mod tests { buffer } - #[test] - fn test_get_hash() { - let a = "Ted".to_string(); - - assert_eq!(2_646_829_031_758_483_623, get_hash(&a)); - } - #[test] fn test_random_shuffle() { let a = b"Ted"; From 107dc449d1b2502a0f2ee69a64a54a795751c049 Mon Sep 17 00:00:00 2001 From: "Lance (Weiqing) Xu" <47257262+lanceXwq@users.noreply.github.com> Date: Tue, 10 Feb 2026 12:59:06 +0100 Subject: [PATCH 45/46] Fix for potential typos (#10822) --- src/uu/od/locales/en-US.ftl | 2 +- src/uu/od/locales/fr-FR.ftl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/od/locales/en-US.ftl b/src/uu/od/locales/en-US.ftl index bcafe1fe2..2c0d8baed 100644 --- a/src/uu/od/locales/en-US.ftl +++ b/src/uu/od/locales/en-US.ftl @@ -90,5 +90,5 @@ od-help-s = decimal 2-byte units od-help-capital-x = hexadecimal 4-byte units od-help-capital-h = hexadecimal 4-byte units od-help-e = floating point double precision (64-bit) units -od-help-f = floating point double precision (32-bit) units +od-help-f = floating point single precision (32-bit) units od-help-capital-f = floating point double precision (64-bit) units diff --git a/src/uu/od/locales/fr-FR.ftl b/src/uu/od/locales/fr-FR.ftl index df07eebe6..ada0c2d79 100644 --- a/src/uu/od/locales/fr-FR.ftl +++ b/src/uu/od/locales/fr-FR.ftl @@ -90,5 +90,5 @@ od-help-s = unités décimales 2-octets od-help-capital-x = unités hexadécimales 4-octets od-help-capital-h = unités hexadécimales 4-octets od-help-e = unités virgule flottante double précision (64-bits) -od-help-f = unités virgule flottante double précision (32-bits) +od-help-f = unités virgule flottante simple précision (32-bits) od-help-capital-f = unités virgule flottante double précision (64-bits) From ec7e81e6ae526d21748da2d0c58f791ac34b1bfa Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Wed, 11 Feb 2026 02:32:10 +0900 Subject: [PATCH 46/46] Add regression test for coreutils --list (#10858) --- tests/test_util_name.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_util_name.rs b/tests/test_util_name.rs index 745e6814b..17b9dc36e 100644 --- a/tests/test_util_name.rs +++ b/tests/test_util_name.rs @@ -235,3 +235,17 @@ fn test_musl_no_dynamic_deps() { stdout ); } + +#[test] +fn test_sorted_utils() { + let s = TestScenario::new("list_sorted"); + let out = String::from_utf8( + std::process::Command::new(&s.bin_path) + .arg("--list") + .output() + .unwrap() + .stdout, + ) + .unwrap(); + assert!(out.lines().filter(|s| !s.is_empty()).is_sorted()); +}