1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

tr: generate an error for real if the input is a directory

This commit is contained in:
Sylvestre Ledru 2024-12-02 22:11:45 +01:00
parent 8a481ccf1c
commit 3e4221a461
4 changed files with 45 additions and 4 deletions

View file

@ -19,7 +19,7 @@ path = "src/tr.rs"
[dependencies] [dependencies]
nom = { workspace = true } nom = { workspace = true }
clap = { workspace = true } clap = { workspace = true }
uucore = { workspace = true } uucore = { workspace = true, features = ["fs"] }
[[bin]] [[bin]]
name = "tr" name = "tr"

View file

@ -8,17 +8,17 @@
mod operation; mod operation;
mod unicode_table; mod unicode_table;
use crate::operation::DeleteOperation;
use clap::{crate_version, value_parser, Arg, ArgAction, Command}; use clap::{crate_version, value_parser, Arg, ArgAction, Command};
use operation::{ use operation::{
translate_input, Sequence, SqueezeOperation, SymbolTranslator, TranslateOperation, translate_input, Sequence, SqueezeOperation, SymbolTranslator, TranslateOperation,
}; };
use std::ffi::OsString; use std::ffi::OsString;
use std::io::{stdin, stdout, BufWriter}; use std::io::{stdin, stdout, BufWriter};
use uucore::{format_usage, help_about, help_section, help_usage, os_str_as_bytes, show};
use crate::operation::DeleteOperation;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError}; use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::fs::is_stdin_directory;
use uucore::{format_usage, help_about, help_section, help_usage, os_str_as_bytes, show};
const ABOUT: &str = help_about!("tr.md"); const ABOUT: &str = help_about!("tr.md");
const AFTER_HELP: &str = help_section!("after help", "tr.md"); const AFTER_HELP: &str = help_section!("after help", "tr.md");
@ -126,6 +126,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
translating, translating,
)?; )?;
if is_stdin_directory(&stdin) {
return Err(USimpleError::new(1, "read error: Is a directory"));
}
// '*_op' are the operations that need to be applied, in order. // '*_op' are the operations that need to be applied, in order.
if delete_flag { if delete_flag {
if squeeze_flag { if squeeze_flag {

View file

@ -20,6 +20,7 @@ use std::ffi::{OsStr, OsString};
use std::fs; use std::fs;
use std::fs::read_dir; use std::fs::read_dir;
use std::hash::Hash; use std::hash::Hash;
use std::io::Stdin;
use std::io::{Error, ErrorKind, Result as IOResult}; use std::io::{Error, ErrorKind, Result as IOResult};
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::{fs::MetadataExt, io::AsRawFd}; use std::os::unix::{fs::MetadataExt, io::AsRawFd};
@ -721,6 +722,34 @@ pub fn path_ends_with_terminator(path: &Path) -> bool {
.map_or(false, |wide| wide == b'/'.into() || wide == b'\\'.into()) .map_or(false, |wide| wide == b'/'.into() || wide == b'\\'.into())
} }
/// Checks if the standard input (stdin) is a directory.
///
/// # Arguments
///
/// * `stdin` - A reference to the standard input handle.
///
/// # Returns
///
/// * `bool` - Returns `true` if stdin is a directory, `false` otherwise.
pub fn is_stdin_directory(stdin: &Stdin) -> bool {
#[cfg(unix)]
{
use nix::sys::stat::fstat;
let mode = fstat(stdin.as_raw_fd()).unwrap().st_mode as mode_t;
has!(mode, S_IFDIR)
}
#[cfg(windows)]
{
use std::os::windows::io::AsRawHandle;
let handle = stdin.as_raw_handle();
if let Ok(metadata) = fs::metadata(format!("{}", handle as usize)) {
return metadata.is_dir();
}
false
}
}
pub mod sane_blksize { pub mod sane_blksize {
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]

View file

@ -20,6 +20,14 @@ fn test_invalid_input() {
.fails() .fails()
.code_is(1) .code_is(1)
.stderr_contains("tr: extra operand '<'"); .stderr_contains("tr: extra operand '<'");
#[cfg(unix)]
new_ucmd!()
.args(&["1", "1"])
// will test "tr 1 1 < ."
.set_stdin(std::process::Stdio::from(std::fs::File::open(".").unwrap()))
.fails()
.code_is(1)
.stderr_contains("tr: read error: Is a directory");
} }
#[test] #[test]