Skip to content

Commit 915b333

Browse files
Rollup merge of rust-lang#98033 - joshtriplett:is-terminal-fd-handle, r=thomcc
Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX implementation uses `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `Stdin`/`Stdout`/`Stderr`/`File` on all platforms. On Unix, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on rust-lang#91121 Co-authored-by: Matt Wilkinson <mattwilki17@gmail.com>
2 parents e1c28e0 + 170a8de commit 915b333

File tree

15 files changed

+164
-37
lines changed

15 files changed

+164
-37
lines changed

library/std/src/io/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ pub use self::buffered::WriterPanicked;
265265
#[unstable(feature = "internal_output_capture", issue = "none")]
266266
#[doc(no_inline, hidden)]
267267
pub use self::stdio::set_output_capture;
268+
#[unstable(feature = "is_terminal", issue = "98070")]
269+
pub use self::stdio::IsTerminal;
268270
#[unstable(feature = "print_internals", issue = "none")]
269271
pub use self::stdio::{_eprint, _print};
270272
#[stable(feature = "rust1", since = "1.0.0")]

library/std/src/io/stdio.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::io::prelude::*;
77

88
use crate::cell::{Cell, RefCell};
99
use crate::fmt;
10+
use crate::fs::File;
1011
use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines};
1112
use crate::sync::atomic::{AtomicBool, Ordering};
1213
use crate::sync::{Arc, Mutex, MutexGuard, OnceLock};
@@ -1019,6 +1020,34 @@ where
10191020
}
10201021
}
10211022

1023+
/// Trait to determine if a descriptor/handle refers to a terminal/tty.
1024+
#[unstable(feature = "is_terminal", issue = "98070")]
1025+
pub trait IsTerminal: crate::sealed::Sealed {
1026+
/// Returns `true` if the descriptor/handle refers to a terminal/tty.
1027+
///
1028+
/// On platforms where Rust does not know how to detect a terminal yet, this will return
1029+
/// `false`. This will also return `false` if an unexpected error occurred, such as from
1030+
/// passing an invalid file descriptor.
1031+
fn is_terminal(&self) -> bool;
1032+
}
1033+
1034+
macro_rules! impl_is_terminal {
1035+
($($t:ty),*$(,)?) => {$(
1036+
#[unstable(feature = "sealed", issue = "none")]
1037+
impl crate::sealed::Sealed for $t {}
1038+
1039+
#[unstable(feature = "is_terminal", issue = "98070")]
1040+
impl IsTerminal for $t {
1041+
#[inline]
1042+
fn is_terminal(&self) -> bool {
1043+
crate::sys::io::is_terminal(self)
1044+
}
1045+
}
1046+
)*}
1047+
}
1048+
1049+
impl_is_terminal!(File, Stdin, StdinLock<'_>, Stdout, StdoutLock<'_>, Stderr, StderrLock<'_>);
1050+
10221051
#[unstable(
10231052
feature = "print_internals",
10241053
reason = "implementation detail which may disappear or be replaced at any time",

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@
252252
#![feature(dropck_eyepatch)]
253253
#![feature(exhaustive_patterns)]
254254
#![feature(intra_doc_pointers)]
255+
#![feature(is_terminal)]
255256
#![cfg_attr(bootstrap, feature(label_break_value))]
256257
#![feature(lang_items)]
257258
#![feature(let_chains)]

library/std/src/os/fd/owned.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,23 @@ impl fmt::Debug for OwnedFd {
192192
}
193193
}
194194

195+
macro_rules! impl_is_terminal {
196+
($($t:ty),*$(,)?) => {$(
197+
#[unstable(feature = "sealed", issue = "none")]
198+
impl crate::sealed::Sealed for $t {}
199+
200+
#[unstable(feature = "is_terminal", issue = "98070")]
201+
impl crate::io::IsTerminal for $t {
202+
#[inline]
203+
fn is_terminal(&self) -> bool {
204+
crate::sys::io::is_terminal(self)
205+
}
206+
}
207+
)*}
208+
}
209+
210+
impl_is_terminal!(BorrowedFd<'_>, OwnedFd);
211+
195212
/// A trait to borrow the file descriptor from an underlying object.
196213
///
197214
/// This is only available on unix platforms and must be imported in order to

library/std/src/os/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ pub mod solid;
147147
pub mod vxworks;
148148

149149
#[cfg(any(unix, target_os = "wasi", doc))]
150-
mod fd;
150+
pub(crate) mod fd;
151151

152152
#[cfg(any(target_os = "linux", target_os = "android", doc))]
153153
mod net;

library/std/src/os/windows/io/handle.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,23 @@ impl fmt::Debug for OwnedHandle {
384384
}
385385
}
386386

387+
macro_rules! impl_is_terminal {
388+
($($t:ty),*$(,)?) => {$(
389+
#[unstable(feature = "sealed", issue = "none")]
390+
impl crate::sealed::Sealed for $t {}
391+
392+
#[unstable(feature = "is_terminal", issue = "98070")]
393+
impl crate::io::IsTerminal for $t {
394+
#[inline]
395+
fn is_terminal(&self) -> bool {
396+
crate::sys::io::is_terminal(self)
397+
}
398+
}
399+
)*}
400+
}
401+
402+
impl_is_terminal!(BorrowedHandle<'_>, OwnedHandle);
403+
387404
/// A trait to borrow the handle from an underlying object.
388405
#[stable(feature = "io_safety", since = "1.63.0")]
389406
pub trait AsHandle {

library/std/src/sys/unix/io.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use crate::marker::PhantomData;
2+
use crate::os::fd::owned::AsFd;
3+
use crate::os::fd::raw::AsRawFd;
24
use crate::slice;
35

46
use libc::{c_void, iovec};
@@ -74,3 +76,8 @@ impl<'a> IoSliceMut<'a> {
7476
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
7577
}
7678
}
79+
80+
pub fn is_terminal(fd: &impl AsFd) -> bool {
81+
let fd = fd.as_fd();
82+
unsafe { libc::isatty(fd.as_raw_fd()) != 0 }
83+
}

library/std/src/sys/unsupported/io.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ impl<'a> IoSliceMut<'a> {
4545
self.0
4646
}
4747
}
48+
49+
pub fn is_terminal<T>(_: &T) -> bool {
50+
false
51+
}

library/std/src/sys/wasi/io.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![deny(unsafe_op_in_unsafe_fn)]
22

33
use crate::marker::PhantomData;
4+
use crate::os::fd::owned::AsFd;
5+
use crate::os::fd::raw::AsRawFd;
46
use crate::slice;
57

68
#[derive(Copy, Clone)]
@@ -71,3 +73,8 @@ impl<'a> IoSliceMut<'a> {
7173
unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) }
7274
}
7375
}
76+
77+
pub fn is_terminal(fd: &impl AsFd) -> bool {
78+
let fd = fd.as_fd();
79+
unsafe { libc::isatty(fd.as_raw_fd()) != 0 }
80+
}

library/std/src/sys/windows/c.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ pub const SECURITY_SQOS_PRESENT: DWORD = 0x00100000;
126126

127127
pub const FIONBIO: c_ulong = 0x8004667e;
128128

129+
pub const MAX_PATH: usize = 260;
130+
129131
#[repr(C)]
130132
#[derive(Copy)]
131133
pub struct WIN32_FIND_DATAW {
@@ -534,6 +536,12 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
534536

535537
/// NB: Use carefully! In general using this as a reference is likely to get the
536538
/// provenance wrong for the `PathBuffer` field!
539+
#[repr(C)]
540+
pub struct FILE_NAME_INFO {
541+
pub FileNameLength: DWORD,
542+
pub FileName: [WCHAR; 1],
543+
}
544+
537545
#[repr(C)]
538546
pub struct MOUNT_POINT_REPARSE_BUFFER {
539547
pub SubstituteNameOffset: c_ushort,

0 commit comments

Comments
 (0)