diff --git a/src/uu/tac/src/error.rs b/src/uu/tac/src/error.rs index a69a34a0d..133a46266 100644 --- a/src/uu/tac/src/error.rs +++ b/src/uu/tac/src/error.rs @@ -4,6 +4,7 @@ // file that was distributed with this source code. //! Errors returned by tac during processing of a file. +use std::ffi::OsString; use thiserror::Error; use uucore::display::Quotable; use uucore::error::UError; @@ -16,16 +17,16 @@ pub enum TacError { InvalidRegex(regex::Error), /// An argument to tac is invalid. #[error("{}", translate!("tac-error-invalid-argument", "argument" => .0.maybe_quote()))] - InvalidArgument(String), + InvalidArgument(OsString), /// The specified file is not found on the filesystem. #[error("{}", translate!("tac-error-file-not-found", "filename" => .0.quote()))] - FileNotFound(String), + FileNotFound(OsString), /// An error reading the contents of a file or stdin. /// /// The parameters are the name of the file and the underlying /// [`std::io::Error`] that caused this error. - #[error("{}", translate!("tac-error-read-error", "filename" => .0.clone(), "error" => .1))] - ReadError(String, std::io::Error), + #[error("{}", translate!("tac-error-read-error", "filename" => .0.quote(), "error" => .1))] + ReadError(OsString, std::io::Error), /// An error writing the (reversed) contents of a file or stdin. /// /// The parameter is the underlying [`std::io::Error`] that caused diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index c41cc8198..521e2c9db 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -244,7 +244,7 @@ fn tac(filenames: &[OsString], before: bool, regex: bool, separator: &str) -> UR } else { let mut buf1 = Vec::new(); if let Err(e) = stdin().read_to_end(&mut buf1) { - let e: Box = TacError::ReadError("stdin".to_string(), e).into(); + let e: Box = TacError::ReadError(OsString::from("stdin"), e).into(); show!(e); continue; } @@ -254,15 +254,13 @@ fn tac(filenames: &[OsString], before: bool, regex: bool, separator: &str) -> UR } else { let path = Path::new(filename); if path.is_dir() { - let e: Box = - TacError::InvalidArgument(filename.to_string_lossy().to_string()).into(); + let e: Box = TacError::InvalidArgument(filename.clone()).into(); show!(e); continue; } if path.metadata().is_err() { - let e: Box = - TacError::FileNotFound(filename.to_string_lossy().to_string()).into(); + let e: Box = TacError::FileNotFound(filename.clone()).into(); show!(e); continue; } @@ -277,8 +275,7 @@ fn tac(filenames: &[OsString], before: bool, regex: bool, separator: &str) -> UR &buf } Err(e) => { - let s = filename.to_string_lossy(); - let e: Box = TacError::ReadError(s.to_string(), e).into(); + let e: Box = TacError::ReadError(filename.clone(), e).into(); show!(e); continue; } diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 7354c20c9..6994a5f70 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -159,21 +159,18 @@ fn is_first_filename_timestamp( timestamp: Option<&str>, files: &[&OsString], ) -> bool { - if timestamp.is_none() + timestamp.is_none() && reference.is_none() && date.is_none() && files.len() >= 2 // env check is last as the slowest op && matches!(std::env::var("_POSIX2_VERSION").as_deref(), Ok("199209")) - { - if let Some(s) = files[0].to_str() { - all_digits(s) && (s.len() == 8 || (s.len() == 10 && (69..=99).contains(&get_year(s)))) - } else { - false - } - } else { - false - } + && files[0].to_str().is_some_and(is_timestamp) +} + +// Check if string is a valid POSIX timestamp (8 digits or 10 digits with valid year range) +fn is_timestamp(s: &str) -> bool { + all_digits(s) && (s.len() == 8 || (s.len() == 10 && (69..=99).contains(&get_year(s)))) } /// Cycle the last two characters to the beginning of the string. @@ -214,14 +211,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .map(|t| t.to_owned()); if is_first_filename_timestamp(reference, date.as_deref(), timestamp.as_deref(), &filenames) { - if let Some(first_file) = filenames[0].to_str() { - timestamp = if first_file.len() == 10 { - Some(shr2(first_file)) - } else { - Some(first_file.to_string()) - }; - filenames = filenames[1..].to_vec(); - } + let first_file = filenames[0].to_str().unwrap(); + timestamp = if first_file.len() == 10 { + Some(shr2(first_file)) + } else { + Some(first_file.to_string()) + }; + filenames = filenames[1..].to_vec(); } let source = if let Some(reference) = reference { diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index ef1ea872c..36ce92dc9 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -424,23 +424,12 @@ fn test_fifo_error_reference_and_size() { #[test] #[cfg(target_os = "linux")] fn test_truncate_non_utf8_paths() { - use std::fs; - + use std::os::unix::ffi::OsStrExt; let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; + let file_name = std::ffi::OsStr::from_bytes(b"test_\xFF\xFE.txt"); + at.write(&file_name.to_string_lossy(), "test content"); - // Create test file with normal name first - at.write("temp.txt", "test content"); - - // Rename to non-UTF-8 name - #[cfg(unix)] - { - use std::os::unix::ffi::OsStrExt; - let file_name = std::ffi::OsStr::from_bytes(b"test_\xFF\xFE.txt"); - - fs::rename(at.subdir.join("temp.txt"), at.subdir.join(file_name)).unwrap(); - - // Test that truncate can handle non-UTF-8 filenames - ts.ucmd().arg("-s").arg("10").arg(file_name).succeeds(); - } + // Test that truncate can handle non-UTF-8 filenames + ts.ucmd().arg("-s").arg("10").arg(file_name).succeeds(); } diff --git a/tests/by-util/test_unlink.rs b/tests/by-util/test_unlink.rs index b508fef38..7a2b50ac3 100644 --- a/tests/by-util/test_unlink.rs +++ b/tests/by-util/test_unlink.rs @@ -81,23 +81,17 @@ fn test_unlink_symlink() { fn test_unlink_non_utf8_paths() { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; - use uutests::util::TestScenario; - use uutests::util_name; - - let scene = TestScenario::new(util_name!()); - let at = &scene.fixtures; + let (at, mut ucmd) = at_and_ucmd!(); // Create a test file with non-UTF-8 bytes in the name let non_utf8_bytes = b"test_\xFF\xFE.txt"; let non_utf8_name = OsStr::from_bytes(non_utf8_bytes); - // Create the actual file at.touch(non_utf8_name); assert!(at.file_exists(non_utf8_name)); // Test that unlink handles non-UTF-8 file names without crashing - scene.ucmd().arg(non_utf8_name).succeeds(); + ucmd.arg(non_utf8_name).succeeds(); - // The file should be removed assert!(!at.file_exists(non_utf8_name)); }