From 93dd508a8bf1c885ab4c2807d50e35ee129a0fa8 Mon Sep 17 00:00:00 2001 From: karanabe <152078880+karanabe@users.noreply.github.com> Date: Sat, 8 Nov 2025 19:12:07 +0900 Subject: [PATCH 1/2] fix(readlink): emit GNU-style Invalid argument for non-symlinks --- src/uu/readlink/src/readlink.rs | 20 ++++++++++++-------- tests/by-util/test_readlink.rs | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/uu/readlink/src/readlink.rs b/src/uu/readlink/src/readlink.rs index d3d3b298473..8d6088c05f8 100644 --- a/src/uu/readlink/src/readlink.rs +++ b/src/uu/readlink/src/readlink.rs @@ -10,8 +10,9 @@ use std::ffi::OsString; use std::fs; use std::io::{Write, stdout}; use std::path::{Path, PathBuf}; -use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; +use uucore::error::{FromIo, UResult, UUsageError}; use uucore::fs::{MissingHandling, ResolveMode, canonicalize}; +use uucore::libc::EINVAL; use uucore::line_ending::LineEnding; use uucore::translate; use uucore::{format_usage, show_error}; @@ -88,15 +89,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { show(&path, line_ending).map_err_context(String::new)?; } Err(err) => { - return if verbose { - Err(USimpleError::new( - 1, - err.map_err_context(move || p.to_string_lossy().to_string()) - .to_string(), - )) + if silent && !verbose { + return Err(1.into()); + } + + let path = p.to_string_lossy().into_owned(); + let message = if err.raw_os_error() == Some(EINVAL) { + format!("{path}: Invalid argument") } else { - Err(1.into()) + err.map_err_context(|| path.clone()).to_string() }; + show_error!("{message}"); + return Err(1.into()); } } } diff --git a/tests/by-util/test_readlink.rs b/tests/by-util/test_readlink.rs index ebc85f543fe..c6a126279c8 100644 --- a/tests/by-util/test_readlink.rs +++ b/tests/by-util/test_readlink.rs @@ -102,6 +102,20 @@ fn test_symlink_to_itself_verbose() { .stderr_contains("Too many levels of symbolic links"); } +#[test] +fn test_posixly_correct_regular_file() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.touch("regfile"); + scene + .ucmd() + .env("POSIXLY_CORRECT", "1") + .arg("regfile") + .fails_with_code(1) + .stderr_contains("Invalid argument") + .no_stdout(); +} + #[test] fn test_trailing_slash_regular_file() { let scene = TestScenario::new(util_name!()); From ce21dffd55656f4089b4d62a09355edfacedcea0 Mon Sep 17 00:00:00 2001 From: karanabe <152078880+karanabe@users.noreply.github.com> Date: Sat, 8 Nov 2025 19:39:31 +0900 Subject: [PATCH 2/2] Replace format! with translate! and test skip on Windows --- src/uu/readlink/locales/en-US.ftl | 1 + src/uu/readlink/locales/fr-FR.ftl | 1 + src/uu/readlink/src/readlink.rs | 2 +- tests/by-util/test_readlink.rs | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/uu/readlink/locales/en-US.ftl b/src/uu/readlink/locales/en-US.ftl index d784e62e15f..bdf8e1e87bc 100644 --- a/src/uu/readlink/locales/en-US.ftl +++ b/src/uu/readlink/locales/en-US.ftl @@ -14,3 +14,4 @@ readlink-help-zero = separate output with NUL rather than newline # Error messages readlink-error-missing-operand = missing operand readlink-error-ignoring-no-newline = ignoring --no-newline with multiple arguments +readlink-error-invalid-argument = {$path}: Invalid argument diff --git a/src/uu/readlink/locales/fr-FR.ftl b/src/uu/readlink/locales/fr-FR.ftl index e670f9c17c5..610e6c2a662 100644 --- a/src/uu/readlink/locales/fr-FR.ftl +++ b/src/uu/readlink/locales/fr-FR.ftl @@ -14,3 +14,4 @@ readlink-help-zero = séparer la sortie avec NUL plutôt qu'une nouvelle ligne # Messages d'erreur readlink-error-missing-operand = opérande manquant readlink-error-ignoring-no-newline = ignorer --no-newline avec plusieurs arguments +readlink-error-invalid-argument = {$path} : argument non valide diff --git a/src/uu/readlink/src/readlink.rs b/src/uu/readlink/src/readlink.rs index 8d6088c05f8..2c019d6bb11 100644 --- a/src/uu/readlink/src/readlink.rs +++ b/src/uu/readlink/src/readlink.rs @@ -95,7 +95,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let path = p.to_string_lossy().into_owned(); let message = if err.raw_os_error() == Some(EINVAL) { - format!("{path}: Invalid argument") + translate!("readlink-error-invalid-argument", "path" => path.clone()) } else { err.map_err_context(|| path.clone()).to_string() }; diff --git a/tests/by-util/test_readlink.rs b/tests/by-util/test_readlink.rs index c6a126279c8..848800cc398 100644 --- a/tests/by-util/test_readlink.rs +++ b/tests/by-util/test_readlink.rs @@ -103,6 +103,7 @@ fn test_symlink_to_itself_verbose() { } #[test] +#[cfg(not(windows))] fn test_posixly_correct_regular_file() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures;