1d0b88b7eSopenharmony_ci//! is-terminal is a simple utility that answers one question:
2d0b88b7eSopenharmony_ci//!
3d0b88b7eSopenharmony_ci//! > Is this a terminal?
4d0b88b7eSopenharmony_ci//!
5d0b88b7eSopenharmony_ci//! A "terminal", also known as a "tty", is an I/O device which may be
6d0b88b7eSopenharmony_ci//! interactive and may support color and other special features. This crate
7d0b88b7eSopenharmony_ci//! doesn't provide any of those features; it just answers this one question.
8d0b88b7eSopenharmony_ci//!
9d0b88b7eSopenharmony_ci//! On Unix-family platforms, this is effectively the same as the [`isatty`]
10d0b88b7eSopenharmony_ci//! function for testing whether a given stream is a terminal, though it
11d0b88b7eSopenharmony_ci//! accepts high-level stream types instead of raw file descriptors.
12d0b88b7eSopenharmony_ci//!
13d0b88b7eSopenharmony_ci//! On Windows, it uses a variety of techniques to determine whether the
14d0b88b7eSopenharmony_ci//! given stream is a terminal.
15d0b88b7eSopenharmony_ci//!
16d0b88b7eSopenharmony_ci//! # Example
17d0b88b7eSopenharmony_ci//!
18d0b88b7eSopenharmony_ci//! ```rust
19d0b88b7eSopenharmony_ci//! use is_terminal::IsTerminal;
20d0b88b7eSopenharmony_ci//!
21d0b88b7eSopenharmony_ci//! if std::io::stdout().is_terminal() {
22d0b88b7eSopenharmony_ci//!     println!("stdout is a terminal")
23d0b88b7eSopenharmony_ci//! }
24d0b88b7eSopenharmony_ci//! ```
25d0b88b7eSopenharmony_ci//!
26d0b88b7eSopenharmony_ci//! [`isatty`]: https://man7.org/linux/man-pages/man3/isatty.3.html
27d0b88b7eSopenharmony_ci
28d0b88b7eSopenharmony_ci#![cfg_attr(unix, no_std)]
29d0b88b7eSopenharmony_ci
30d0b88b7eSopenharmony_ci#[cfg(not(target_os = "unknown"))]
31d0b88b7eSopenharmony_ciuse io_lifetimes::AsFilelike;
32d0b88b7eSopenharmony_ci#[cfg(windows)]
33d0b88b7eSopenharmony_ciuse io_lifetimes::BorrowedHandle;
34d0b88b7eSopenharmony_ci#[cfg(windows)]
35d0b88b7eSopenharmony_ciuse windows_sys::Win32::Foundation::HANDLE;
36d0b88b7eSopenharmony_ci#[cfg(windows)]
37d0b88b7eSopenharmony_ciuse windows_sys::Win32::System::Console::STD_HANDLE;
38d0b88b7eSopenharmony_ci
39d0b88b7eSopenharmony_cipub trait IsTerminal {
40d0b88b7eSopenharmony_ci    /// Returns true if this is a terminal.
41d0b88b7eSopenharmony_ci    ///
42d0b88b7eSopenharmony_ci    /// # Example
43d0b88b7eSopenharmony_ci    ///
44d0b88b7eSopenharmony_ci    /// ```
45d0b88b7eSopenharmony_ci    /// use is_terminal::IsTerminal;
46d0b88b7eSopenharmony_ci    ///
47d0b88b7eSopenharmony_ci    /// if std::io::stdout().is_terminal() {
48d0b88b7eSopenharmony_ci    ///     println!("stdout is a terminal")
49d0b88b7eSopenharmony_ci    /// }
50d0b88b7eSopenharmony_ci    /// ```
51d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool;
52d0b88b7eSopenharmony_ci}
53d0b88b7eSopenharmony_ci
54d0b88b7eSopenharmony_ci#[cfg(not(target_os = "unknown"))]
55d0b88b7eSopenharmony_ciimpl<Stream: AsFilelike> IsTerminal for Stream {
56d0b88b7eSopenharmony_ci    #[inline]
57d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
58d0b88b7eSopenharmony_ci        #[cfg(any(unix, target_os = "wasi"))]
59d0b88b7eSopenharmony_ci        {
60d0b88b7eSopenharmony_ci            rustix::termios::isatty(self)
61d0b88b7eSopenharmony_ci        }
62d0b88b7eSopenharmony_ci
63d0b88b7eSopenharmony_ci        #[cfg(target_os = "hermit")]
64d0b88b7eSopenharmony_ci        {
65d0b88b7eSopenharmony_ci            hermit_abi::isatty(self.as_filelike().as_fd())
66d0b88b7eSopenharmony_ci        }
67d0b88b7eSopenharmony_ci
68d0b88b7eSopenharmony_ci        #[cfg(windows)]
69d0b88b7eSopenharmony_ci        {
70d0b88b7eSopenharmony_ci            _is_terminal(self.as_filelike())
71d0b88b7eSopenharmony_ci        }
72d0b88b7eSopenharmony_ci    }
73d0b88b7eSopenharmony_ci}
74d0b88b7eSopenharmony_ci
75d0b88b7eSopenharmony_ci// The Windows implementation here is copied from atty, with #51 and #54
76d0b88b7eSopenharmony_ci// applied. The only significant modification is to take a `BorrowedHandle`
77d0b88b7eSopenharmony_ci// argument instead of using a `Stream` enum.
78d0b88b7eSopenharmony_ci
79d0b88b7eSopenharmony_ci#[cfg(windows)]
80d0b88b7eSopenharmony_cifn _is_terminal(stream: BorrowedHandle<'_>) -> bool {
81d0b88b7eSopenharmony_ci    use std::os::windows::io::AsRawHandle;
82d0b88b7eSopenharmony_ci    use windows_sys::Win32::System::Console::GetStdHandle;
83d0b88b7eSopenharmony_ci    use windows_sys::Win32::System::Console::{
84d0b88b7eSopenharmony_ci        STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
85d0b88b7eSopenharmony_ci        STD_OUTPUT_HANDLE as STD_OUTPUT,
86d0b88b7eSopenharmony_ci    };
87d0b88b7eSopenharmony_ci
88d0b88b7eSopenharmony_ci    let (fd, others) = unsafe {
89d0b88b7eSopenharmony_ci        if stream.as_raw_handle() == GetStdHandle(STD_INPUT) as _ {
90d0b88b7eSopenharmony_ci            (STD_INPUT, [STD_ERROR, STD_OUTPUT])
91d0b88b7eSopenharmony_ci        } else if stream.as_raw_handle() == GetStdHandle(STD_OUTPUT) as _ {
92d0b88b7eSopenharmony_ci            (STD_OUTPUT, [STD_INPUT, STD_ERROR])
93d0b88b7eSopenharmony_ci        } else if stream.as_raw_handle() == GetStdHandle(STD_ERROR) as _ {
94d0b88b7eSopenharmony_ci            (STD_ERROR, [STD_INPUT, STD_OUTPUT])
95d0b88b7eSopenharmony_ci        } else {
96d0b88b7eSopenharmony_ci            return false;
97d0b88b7eSopenharmony_ci        }
98d0b88b7eSopenharmony_ci    };
99d0b88b7eSopenharmony_ci    if unsafe { console_on_any(&[fd]) } {
100d0b88b7eSopenharmony_ci        // False positives aren't possible. If we got a console then
101d0b88b7eSopenharmony_ci        // we definitely have a tty on stdin.
102d0b88b7eSopenharmony_ci        return true;
103d0b88b7eSopenharmony_ci    }
104d0b88b7eSopenharmony_ci
105d0b88b7eSopenharmony_ci    // At this point, we *could* have a false negative. We can determine that
106d0b88b7eSopenharmony_ci    // this is true negative if we can detect the presence of a console on
107d0b88b7eSopenharmony_ci    // any of the other streams. If another stream has a console, then we know
108d0b88b7eSopenharmony_ci    // we're in a Windows console and can therefore trust the negative.
109d0b88b7eSopenharmony_ci    if unsafe { console_on_any(&others) } {
110d0b88b7eSopenharmony_ci        return false;
111d0b88b7eSopenharmony_ci    }
112d0b88b7eSopenharmony_ci
113d0b88b7eSopenharmony_ci    // Otherwise, we fall back to a very strange msys hack to see if we can
114d0b88b7eSopenharmony_ci    // sneakily detect the presence of a tty.
115d0b88b7eSopenharmony_ci    // Safety: function has no invariants. an invalid handle id will cause
116d0b88b7eSopenharmony_ci    // GetFileInformationByHandleEx to return an error.
117d0b88b7eSopenharmony_ci    let handle = unsafe { GetStdHandle(fd) };
118d0b88b7eSopenharmony_ci    unsafe { msys_tty_on(handle) }
119d0b88b7eSopenharmony_ci}
120d0b88b7eSopenharmony_ci
121d0b88b7eSopenharmony_ci/// Returns true if any of the given fds are on a console.
122d0b88b7eSopenharmony_ci#[cfg(windows)]
123d0b88b7eSopenharmony_ciunsafe fn console_on_any(fds: &[STD_HANDLE]) -> bool {
124d0b88b7eSopenharmony_ci    use windows_sys::Win32::System::Console::{GetConsoleMode, GetStdHandle};
125d0b88b7eSopenharmony_ci
126d0b88b7eSopenharmony_ci    for &fd in fds {
127d0b88b7eSopenharmony_ci        let mut out = 0;
128d0b88b7eSopenharmony_ci        let handle = GetStdHandle(fd);
129d0b88b7eSopenharmony_ci        if GetConsoleMode(handle, &mut out) != 0 {
130d0b88b7eSopenharmony_ci            return true;
131d0b88b7eSopenharmony_ci        }
132d0b88b7eSopenharmony_ci    }
133d0b88b7eSopenharmony_ci    false
134d0b88b7eSopenharmony_ci}
135d0b88b7eSopenharmony_ci
136d0b88b7eSopenharmony_ci/// Returns true if there is an MSYS tty on the given handle.
137d0b88b7eSopenharmony_ci#[cfg(windows)]
138d0b88b7eSopenharmony_ciunsafe fn msys_tty_on(handle: HANDLE) -> bool {
139d0b88b7eSopenharmony_ci    use std::ffi::c_void;
140d0b88b7eSopenharmony_ci    use windows_sys::Win32::{
141d0b88b7eSopenharmony_ci        Foundation::MAX_PATH,
142d0b88b7eSopenharmony_ci        Storage::FileSystem::{FileNameInfo, GetFileInformationByHandleEx},
143d0b88b7eSopenharmony_ci    };
144d0b88b7eSopenharmony_ci
145d0b88b7eSopenharmony_ci    /// Mirrors windows_sys::Win32::Storage::FileSystem::FILE_NAME_INFO, giving
146d0b88b7eSopenharmony_ci    /// it a fixed length that we can stack allocate
147d0b88b7eSopenharmony_ci    #[repr(C)]
148d0b88b7eSopenharmony_ci    #[allow(non_snake_case)]
149d0b88b7eSopenharmony_ci    struct FILE_NAME_INFO {
150d0b88b7eSopenharmony_ci        FileNameLength: u32,
151d0b88b7eSopenharmony_ci        FileName: [u16; MAX_PATH as usize],
152d0b88b7eSopenharmony_ci    }
153d0b88b7eSopenharmony_ci    let mut name_info = FILE_NAME_INFO {
154d0b88b7eSopenharmony_ci        FileNameLength: 0,
155d0b88b7eSopenharmony_ci        FileName: [0; MAX_PATH as usize],
156d0b88b7eSopenharmony_ci    };
157d0b88b7eSopenharmony_ci    // Safety: buffer length is fixed.
158d0b88b7eSopenharmony_ci    let res = GetFileInformationByHandleEx(
159d0b88b7eSopenharmony_ci        handle,
160d0b88b7eSopenharmony_ci        FileNameInfo,
161d0b88b7eSopenharmony_ci        &mut name_info as *mut _ as *mut c_void,
162d0b88b7eSopenharmony_ci        std::mem::size_of::<FILE_NAME_INFO>() as u32,
163d0b88b7eSopenharmony_ci    );
164d0b88b7eSopenharmony_ci    if res == 0 {
165d0b88b7eSopenharmony_ci        return false;
166d0b88b7eSopenharmony_ci    }
167d0b88b7eSopenharmony_ci
168d0b88b7eSopenharmony_ci    let s = &name_info.FileName[..name_info.FileNameLength as usize / 2];
169d0b88b7eSopenharmony_ci    let name = String::from_utf16_lossy(s);
170d0b88b7eSopenharmony_ci    // This checks whether 'pty' exists in the file name, which indicates that
171d0b88b7eSopenharmony_ci    // a pseudo-terminal is attached. To mitigate against false positives
172d0b88b7eSopenharmony_ci    // (e.g., an actual file name that contains 'pty'), we also require that
173d0b88b7eSopenharmony_ci    // either the strings 'msys-' or 'cygwin-' are in the file name as well.)
174d0b88b7eSopenharmony_ci    let is_msys = name.contains("msys-") || name.contains("cygwin-");
175d0b88b7eSopenharmony_ci    let is_pty = name.contains("-pty");
176d0b88b7eSopenharmony_ci    is_msys && is_pty
177d0b88b7eSopenharmony_ci}
178d0b88b7eSopenharmony_ci
179d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
180d0b88b7eSopenharmony_ciimpl IsTerminal for std::io::Stdin {
181d0b88b7eSopenharmony_ci    #[inline]
182d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
183d0b88b7eSopenharmony_ci        false
184d0b88b7eSopenharmony_ci    }
185d0b88b7eSopenharmony_ci}
186d0b88b7eSopenharmony_ci
187d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
188d0b88b7eSopenharmony_ciimpl IsTerminal for std::io::Stdout {
189d0b88b7eSopenharmony_ci    #[inline]
190d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
191d0b88b7eSopenharmony_ci        false
192d0b88b7eSopenharmony_ci    }
193d0b88b7eSopenharmony_ci}
194d0b88b7eSopenharmony_ci
195d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
196d0b88b7eSopenharmony_ciimpl IsTerminal for std::io::Stderr {
197d0b88b7eSopenharmony_ci    #[inline]
198d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
199d0b88b7eSopenharmony_ci        false
200d0b88b7eSopenharmony_ci    }
201d0b88b7eSopenharmony_ci}
202d0b88b7eSopenharmony_ci
203d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
204d0b88b7eSopenharmony_ciimpl<'a> IsTerminal for std::io::StdinLock<'a> {
205d0b88b7eSopenharmony_ci    #[inline]
206d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
207d0b88b7eSopenharmony_ci        false
208d0b88b7eSopenharmony_ci    }
209d0b88b7eSopenharmony_ci}
210d0b88b7eSopenharmony_ci
211d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
212d0b88b7eSopenharmony_ciimpl<'a> IsTerminal for std::io::StdoutLock<'a> {
213d0b88b7eSopenharmony_ci    #[inline]
214d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
215d0b88b7eSopenharmony_ci        false
216d0b88b7eSopenharmony_ci    }
217d0b88b7eSopenharmony_ci}
218d0b88b7eSopenharmony_ci
219d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
220d0b88b7eSopenharmony_ciimpl<'a> IsTerminal for std::io::StderrLock<'a> {
221d0b88b7eSopenharmony_ci    #[inline]
222d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
223d0b88b7eSopenharmony_ci        false
224d0b88b7eSopenharmony_ci    }
225d0b88b7eSopenharmony_ci}
226d0b88b7eSopenharmony_ci
227d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
228d0b88b7eSopenharmony_ciimpl<'a> IsTerminal for std::fs::File {
229d0b88b7eSopenharmony_ci    #[inline]
230d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
231d0b88b7eSopenharmony_ci        false
232d0b88b7eSopenharmony_ci    }
233d0b88b7eSopenharmony_ci}
234d0b88b7eSopenharmony_ci
235d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
236d0b88b7eSopenharmony_ciimpl IsTerminal for std::process::ChildStdin {
237d0b88b7eSopenharmony_ci    #[inline]
238d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
239d0b88b7eSopenharmony_ci        false
240d0b88b7eSopenharmony_ci    }
241d0b88b7eSopenharmony_ci}
242d0b88b7eSopenharmony_ci
243d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
244d0b88b7eSopenharmony_ciimpl IsTerminal for std::process::ChildStdout {
245d0b88b7eSopenharmony_ci    #[inline]
246d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
247d0b88b7eSopenharmony_ci        false
248d0b88b7eSopenharmony_ci    }
249d0b88b7eSopenharmony_ci}
250d0b88b7eSopenharmony_ci
251d0b88b7eSopenharmony_ci#[cfg(target_os = "unknown")]
252d0b88b7eSopenharmony_ciimpl IsTerminal for std::process::ChildStderr {
253d0b88b7eSopenharmony_ci    #[inline]
254d0b88b7eSopenharmony_ci    fn is_terminal(&self) -> bool {
255d0b88b7eSopenharmony_ci        false
256d0b88b7eSopenharmony_ci    }
257d0b88b7eSopenharmony_ci}
258d0b88b7eSopenharmony_ci
259d0b88b7eSopenharmony_ci#[cfg(test)]
260d0b88b7eSopenharmony_cimod tests {
261d0b88b7eSopenharmony_ci    #[cfg(not(target_os = "unknown"))]
262d0b88b7eSopenharmony_ci    use super::IsTerminal;
263d0b88b7eSopenharmony_ci
264d0b88b7eSopenharmony_ci    #[test]
265d0b88b7eSopenharmony_ci    #[cfg(windows)]
266d0b88b7eSopenharmony_ci    fn stdin() {
267d0b88b7eSopenharmony_ci        assert_eq!(
268d0b88b7eSopenharmony_ci            atty::is(atty::Stream::Stdin),
269d0b88b7eSopenharmony_ci            std::io::stdin().is_terminal()
270d0b88b7eSopenharmony_ci        )
271d0b88b7eSopenharmony_ci    }
272d0b88b7eSopenharmony_ci
273d0b88b7eSopenharmony_ci    #[test]
274d0b88b7eSopenharmony_ci    #[cfg(windows)]
275d0b88b7eSopenharmony_ci    fn stdout() {
276d0b88b7eSopenharmony_ci        assert_eq!(
277d0b88b7eSopenharmony_ci            atty::is(atty::Stream::Stdout),
278d0b88b7eSopenharmony_ci            std::io::stdout().is_terminal()
279d0b88b7eSopenharmony_ci        )
280d0b88b7eSopenharmony_ci    }
281d0b88b7eSopenharmony_ci
282d0b88b7eSopenharmony_ci    #[test]
283d0b88b7eSopenharmony_ci    #[cfg(windows)]
284d0b88b7eSopenharmony_ci    fn stderr() {
285d0b88b7eSopenharmony_ci        assert_eq!(
286d0b88b7eSopenharmony_ci            atty::is(atty::Stream::Stderr),
287d0b88b7eSopenharmony_ci            std::io::stderr().is_terminal()
288d0b88b7eSopenharmony_ci        )
289d0b88b7eSopenharmony_ci    }
290d0b88b7eSopenharmony_ci
291d0b88b7eSopenharmony_ci    #[test]
292d0b88b7eSopenharmony_ci    #[cfg(any(unix, target_os = "wasi"))]
293d0b88b7eSopenharmony_ci    fn stdin() {
294d0b88b7eSopenharmony_ci        unsafe {
295d0b88b7eSopenharmony_ci            assert_eq!(
296d0b88b7eSopenharmony_ci                atty::is(atty::Stream::Stdin),
297d0b88b7eSopenharmony_ci                rustix::io::stdin().is_terminal()
298d0b88b7eSopenharmony_ci            )
299d0b88b7eSopenharmony_ci        }
300d0b88b7eSopenharmony_ci    }
301d0b88b7eSopenharmony_ci
302d0b88b7eSopenharmony_ci    #[test]
303d0b88b7eSopenharmony_ci    #[cfg(any(unix, target_os = "wasi"))]
304d0b88b7eSopenharmony_ci    fn stdout() {
305d0b88b7eSopenharmony_ci        unsafe {
306d0b88b7eSopenharmony_ci            assert_eq!(
307d0b88b7eSopenharmony_ci                atty::is(atty::Stream::Stdout),
308d0b88b7eSopenharmony_ci                rustix::io::stdout().is_terminal()
309d0b88b7eSopenharmony_ci            )
310d0b88b7eSopenharmony_ci        }
311d0b88b7eSopenharmony_ci    }
312d0b88b7eSopenharmony_ci
313d0b88b7eSopenharmony_ci    #[test]
314d0b88b7eSopenharmony_ci    #[cfg(any(unix, target_os = "wasi"))]
315d0b88b7eSopenharmony_ci    fn stderr() {
316d0b88b7eSopenharmony_ci        unsafe {
317d0b88b7eSopenharmony_ci            assert_eq!(
318d0b88b7eSopenharmony_ci                atty::is(atty::Stream::Stderr),
319d0b88b7eSopenharmony_ci                rustix::io::stderr().is_terminal()
320d0b88b7eSopenharmony_ci            )
321d0b88b7eSopenharmony_ci        }
322d0b88b7eSopenharmony_ci    }
323d0b88b7eSopenharmony_ci
324d0b88b7eSopenharmony_ci    #[test]
325d0b88b7eSopenharmony_ci    #[cfg(any(unix, target_os = "wasi"))]
326d0b88b7eSopenharmony_ci    fn stdin_vs_libc() {
327d0b88b7eSopenharmony_ci        unsafe {
328d0b88b7eSopenharmony_ci            assert_eq!(
329d0b88b7eSopenharmony_ci                libc::isatty(libc::STDIN_FILENO) != 0,
330d0b88b7eSopenharmony_ci                rustix::io::stdin().is_terminal()
331d0b88b7eSopenharmony_ci            )
332d0b88b7eSopenharmony_ci        }
333d0b88b7eSopenharmony_ci    }
334d0b88b7eSopenharmony_ci
335d0b88b7eSopenharmony_ci    #[test]
336d0b88b7eSopenharmony_ci    #[cfg(any(unix, target_os = "wasi"))]
337d0b88b7eSopenharmony_ci    fn stdout_vs_libc() {
338d0b88b7eSopenharmony_ci        unsafe {
339d0b88b7eSopenharmony_ci            assert_eq!(
340d0b88b7eSopenharmony_ci                libc::isatty(libc::STDOUT_FILENO) != 0,
341d0b88b7eSopenharmony_ci                rustix::io::stdout().is_terminal()
342d0b88b7eSopenharmony_ci            )
343d0b88b7eSopenharmony_ci        }
344d0b88b7eSopenharmony_ci    }
345d0b88b7eSopenharmony_ci
346d0b88b7eSopenharmony_ci    #[test]
347d0b88b7eSopenharmony_ci    #[cfg(any(unix, target_os = "wasi"))]
348d0b88b7eSopenharmony_ci    fn stderr_vs_libc() {
349d0b88b7eSopenharmony_ci        unsafe {
350d0b88b7eSopenharmony_ci            assert_eq!(
351d0b88b7eSopenharmony_ci                libc::isatty(libc::STDERR_FILENO) != 0,
352d0b88b7eSopenharmony_ci                rustix::io::stderr().is_terminal()
353d0b88b7eSopenharmony_ci            )
354d0b88b7eSopenharmony_ci        }
355d0b88b7eSopenharmony_ci    }
356d0b88b7eSopenharmony_ci
357d0b88b7eSopenharmony_ci    // Verify that the msys_tty_on function works with long path.
358d0b88b7eSopenharmony_ci    #[test]
359d0b88b7eSopenharmony_ci    #[cfg(windows)]
360d0b88b7eSopenharmony_ci    fn msys_tty_on_path_length() {
361d0b88b7eSopenharmony_ci        use std::{fs::File, os::windows::io::AsRawHandle};
362d0b88b7eSopenharmony_ci        use windows_sys::Win32::Foundation::MAX_PATH;
363d0b88b7eSopenharmony_ci
364d0b88b7eSopenharmony_ci        let dir = tempfile::tempdir().expect("Unable to create temporary directory");
365d0b88b7eSopenharmony_ci        let file_path = dir.path().join("ten_chars_".repeat(25));
366d0b88b7eSopenharmony_ci        // Ensure that the path is longer than MAX_PATH.
367d0b88b7eSopenharmony_ci        assert!(file_path.to_string_lossy().len() > MAX_PATH as usize);
368d0b88b7eSopenharmony_ci        let file = File::create(file_path).expect("Unable to create file");
369d0b88b7eSopenharmony_ci
370d0b88b7eSopenharmony_ci        assert!(!unsafe { crate::msys_tty_on(file.as_raw_handle() as isize) });
371d0b88b7eSopenharmony_ci    }
372d0b88b7eSopenharmony_ci}
373