mirror of
https://github.com/uutils/coreutils.git
synced 2025-12-23 08:47:37 +00:00
tac/touch: fix support non-utf-8
This commit is contained in:
parent
a15f9646e8
commit
39fbdeecb2
5 changed files with 30 additions and 53 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<dyn UError> = TacError::ReadError("stdin".to_string(), e).into();
|
||||
let e: Box<dyn UError> = 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<dyn UError> =
|
||||
TacError::InvalidArgument(filename.to_string_lossy().to_string()).into();
|
||||
let e: Box<dyn UError> = TacError::InvalidArgument(filename.clone()).into();
|
||||
show!(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if path.metadata().is_err() {
|
||||
let e: Box<dyn UError> =
|
||||
TacError::FileNotFound(filename.to_string_lossy().to_string()).into();
|
||||
let e: Box<dyn UError> = 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<dyn UError> = TacError::ReadError(s.to_string(), e).into();
|
||||
let e: Box<dyn UError> = TacError::ReadError(filename.clone(), e).into();
|
||||
show!(e);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue