Skip to content

Commit 3e4221a

Browse files
committed
tr: generate an error for real if the input is a directory
1 parent 8a481cc commit 3e4221a

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

src/uu/tr/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ path = "src/tr.rs"
1919
[dependencies]
2020
nom = { workspace = true }
2121
clap = { workspace = true }
22-
uucore = { workspace = true }
22+
uucore = { workspace = true, features = ["fs"] }
2323

2424
[[bin]]
2525
name = "tr"

src/uu/tr/src/tr.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@
88
mod operation;
99
mod unicode_table;
1010

11+
use crate::operation::DeleteOperation;
1112
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
1213
use operation::{
1314
translate_input, Sequence, SqueezeOperation, SymbolTranslator, TranslateOperation,
1415
};
1516
use std::ffi::OsString;
1617
use std::io::{stdin, stdout, BufWriter};
17-
use uucore::{format_usage, help_about, help_section, help_usage, os_str_as_bytes, show};
18-
19-
use crate::operation::DeleteOperation;
2018
use uucore::display::Quotable;
2119
use uucore::error::{UResult, USimpleError, UUsageError};
20+
use uucore::fs::is_stdin_directory;
21+
use uucore::{format_usage, help_about, help_section, help_usage, os_str_as_bytes, show};
2222

2323
const ABOUT: &str = help_about!("tr.md");
2424
const AFTER_HELP: &str = help_section!("after help", "tr.md");
@@ -126,6 +126,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
126126
translating,
127127
)?;
128128

129+
if is_stdin_directory(&stdin) {
130+
return Err(USimpleError::new(1, "read error: Is a directory"));
131+
}
132+
129133
// '*_op' are the operations that need to be applied, in order.
130134
if delete_flag {
131135
if squeeze_flag {

src/uucore/src/lib/features/fs.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::ffi::{OsStr, OsString};
2020
use std::fs;
2121
use std::fs::read_dir;
2222
use std::hash::Hash;
23+
use std::io::Stdin;
2324
use std::io::{Error, ErrorKind, Result as IOResult};
2425
#[cfg(unix)]
2526
use std::os::unix::{fs::MetadataExt, io::AsRawFd};
@@ -721,6 +722,34 @@ pub fn path_ends_with_terminator(path: &Path) -> bool {
721722
.map_or(false, |wide| wide == b'/'.into() || wide == b'\\'.into())
722723
}
723724

725+
/// Checks if the standard input (stdin) is a directory.
726+
///
727+
/// # Arguments
728+
///
729+
/// * `stdin` - A reference to the standard input handle.
730+
///
731+
/// # Returns
732+
///
733+
/// * `bool` - Returns `true` if stdin is a directory, `false` otherwise.
734+
pub fn is_stdin_directory(stdin: &Stdin) -> bool {
735+
#[cfg(unix)]
736+
{
737+
use nix::sys::stat::fstat;
738+
let mode = fstat(stdin.as_raw_fd()).unwrap().st_mode as mode_t;
739+
has!(mode, S_IFDIR)
740+
}
741+
742+
#[cfg(windows)]
743+
{
744+
use std::os::windows::io::AsRawHandle;
745+
let handle = stdin.as_raw_handle();
746+
if let Ok(metadata) = fs::metadata(format!("{}", handle as usize)) {
747+
return metadata.is_dir();
748+
}
749+
false
750+
}
751+
}
752+
724753
pub mod sane_blksize {
725754

726755
#[cfg(not(target_os = "windows"))]

tests/by-util/test_tr.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ fn test_invalid_input() {
2020
.fails()
2121
.code_is(1)
2222
.stderr_contains("tr: extra operand '<'");
23+
#[cfg(unix)]
24+
new_ucmd!()
25+
.args(&["1", "1"])
26+
// will test "tr 1 1 < ."
27+
.set_stdin(std::process::Stdio::from(std::fs::File::open(".").unwrap()))
28+
.fails()
29+
.code_is(1)
30+
.stderr_contains("tr: read error: Is a directory");
2331
}
2432

2533
#[test]

0 commit comments

Comments
 (0)