Skip to content

Commit 71c92ef

Browse files
committed
tty: support printing msys2 tty path
1 parent 5316f58 commit 71c92ef

4 files changed

Lines changed: 60 additions & 5 deletions

File tree

.vscode/cspell.dictionaries/acronyms+names.wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ MinGW
6161
Minix
6262
MS-DOS
6363
MSDOS
64+
msys
6465
NetBSD
6566
Novell
6667
Nushell

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/tty/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ fluent = { workspace = true }
2727
[target.'cfg(unix)'.dependencies]
2828
rustix = { workspace = true, features = ["fs", "termios"] }
2929

30+
[target.'cfg(windows)'.dependencies]
31+
windows-sys = { workspace = true, features = [
32+
"Win32_Storage_FileSystem",
33+
"Win32_Foundation",
34+
] }
35+
3036
[[bin]]
3137
name = "tty"
3238
path = "src/main.rs"

src/uu/tty/src/tty.rs

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,20 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
5858
writeln!(stdout, "{}", translate!("tty-not-a-tty"))
5959
};
6060
#[cfg(target_os = "windows")]
61-
let write_result = if std::io::stdin().is_terminal() {
62-
writeln!(stdout, r"\\.\CON")
63-
} else {
64-
set_exit_code(1);
65-
writeln!(stdout, "{}", translate!("tty-not-a-tty"))
61+
let write_result = {
62+
use std::os::windows::io::AsHandle;
63+
let stdin = std::io::stdin();
64+
let stdin_handle = stdin.as_handle();
65+
if stdin_handle.is_terminal() {
66+
writeln!(
67+
stdout,
68+
"{}",
69+
file_name(stdin_handle).as_deref().unwrap_or(r"\\.\CON")
70+
)
71+
} else {
72+
set_exit_code(1);
73+
writeln!(stdout, "{}", translate!("tty-not-a-tty"))
74+
}
6675
};
6776

6877
if write_result.is_err() || stdout.flush().is_err() {
@@ -74,6 +83,44 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
7483
Ok(())
7584
}
7685

86+
#[cfg(target_os = "windows")]
87+
fn file_name(handle: std::os::windows::io::BorrowedHandle) -> Option<String> {
88+
// This code is adapted from rust's standard library
89+
// https://github.com/rust-lang/rust/blob/0424cc16731e6141a18077f8ccde77ba148d9649/library/std/src/sys/io/is_terminal/windows.rs#L25
90+
use std::mem::MaybeUninit;
91+
use std::os::windows::io::AsRawHandle;
92+
use windows_sys::Win32::Foundation::MAX_PATH;
93+
use windows_sys::Win32::Storage::FileSystem::{FileNameInfo, GetFileInformationByHandleEx};
94+
// Manually define FILE_NAME_INFO so we can more easily construct a stack buffer.
95+
#[repr(C)]
96+
#[allow(non_snake_case)]
97+
struct FILE_NAME_INFO {
98+
FileNameLength: u32,
99+
FileName: [MaybeUninit<u16>; MAX_PATH as usize],
100+
}
101+
let mut name = FILE_NAME_INFO {
102+
FileNameLength: 0,
103+
FileName: [MaybeUninit::uninit(); MAX_PATH as usize],
104+
};
105+
unsafe {
106+
let result = GetFileInformationByHandleEx(
107+
handle.as_raw_handle(),
108+
FileNameInfo,
109+
(&raw mut name).cast(),
110+
size_of::<FILE_NAME_INFO>() as u32,
111+
);
112+
if result == 0 {
113+
None
114+
} else {
115+
let name = name.FileName.get(..name.FileNameLength as usize / 2)?;
116+
// SAFETY: all elements up to FileNameLength have been initialized
117+
let name: &[u16] = &*(std::ptr::from_ref::<[MaybeUninit<u16>]>(name) as *const [u16]);
118+
// This should never fail for a valid msys terminal because they use ASCII names.
119+
String::from_utf16(name).ok()
120+
}
121+
}
122+
}
123+
77124
pub fn uu_app() -> Command {
78125
let cmd = Command::new("tty")
79126
.version(uucore::crate_version!())

0 commit comments

Comments
 (0)