1#[cfg(windows)]
15use winapi::shared::minwindef::DWORD;
16#[cfg(windows)]
17use winapi::shared::ntdef::WCHAR;
18
19#[derive(Clone, Copy, Debug)]
21pub enum Stream {
22 Stdout,
23 Stderr,
24 Stdin,
25}
26
27#[cfg(target_family = "unix")]
29pub fn is(stream: Stream) -> bool {
30 let fd = match stream {
31 Stream::Stdout => libc::STDOUT_FILENO,
32 Stream::Stderr => libc::STDERR_FILENO,
33 Stream::Stdin => libc::STDIN_FILENO,
34 };
35 unsafe { libc::isatty(fd) != 0 }
36}
37
38#[cfg(target_os = "hermit")]
40pub fn is(stream: Stream) -> bool {
41 let fd = match stream {
42 Stream::Stdout => hermit_abi::STDOUT_FILENO,
43 Stream::Stderr => hermit_abi::STDERR_FILENO,
44 Stream::Stdin => hermit_abi::STDIN_FILENO,
45 };
46 hermit_abi::isatty(fd)
47}
48
49#[cfg(windows)]
51pub fn is(stream: Stream) -> bool {
52 use winapi::um::winbase::{
53 STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
54 STD_OUTPUT_HANDLE as STD_OUTPUT,
55 };
56
57 let (fd, others) = match stream {
58 Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]),
59 Stream::Stderr => (STD_ERROR, [STD_INPUT, STD_OUTPUT]),
60 Stream::Stdout => (STD_OUTPUT, [STD_INPUT, STD_ERROR]),
61 };
62 if unsafe { console_on_any(&[fd]) } {
63 return true;
66 }
67
68 if unsafe { console_on_any(&others) } {
73 return false;
74 }
75
76 unsafe { msys_tty_on(fd) }
79}
80
81pub fn isnt(stream: Stream) -> bool {
83 !is(stream)
84}
85
86#[cfg(windows)]
88unsafe fn console_on_any(fds: &[DWORD]) -> bool {
89 use winapi::um::{consoleapi::GetConsoleMode, processenv::GetStdHandle};
90
91 for &fd in fds {
92 let mut out = 0;
93 let handle = GetStdHandle(fd);
94 if GetConsoleMode(handle, &mut out) != 0 {
95 return true;
96 }
97 }
98 false
99}
100
101#[cfg(windows)]
103unsafe fn msys_tty_on(fd: DWORD) -> bool {
104 use std::{mem, slice};
105
106 use winapi::{
107 ctypes::c_void,
108 shared::minwindef::MAX_PATH,
109 um::{
110 fileapi::FILE_NAME_INFO, minwinbase::FileNameInfo, processenv::GetStdHandle,
111 winbase::GetFileInformationByHandleEx,
112 },
113 };
114
115 let size = mem::size_of::<FILE_NAME_INFO>();
116 let mut name_info_bytes = vec![0u8; size + MAX_PATH * mem::size_of::<WCHAR>()];
117 let res = GetFileInformationByHandleEx(
118 GetStdHandle(fd),
119 FileNameInfo,
120 &mut *name_info_bytes as *mut _ as *mut c_void,
121 name_info_bytes.len() as u32,
122 );
123 if res == 0 {
124 return false;
125 }
126 let name_info: &FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const FILE_NAME_INFO);
127 let s = slice::from_raw_parts(
128 name_info.FileName.as_ptr(),
129 name_info.FileNameLength as usize / 2,
130 );
131 let name = String::from_utf16_lossy(s);
132 let is_msys = name.contains("msys-") || name.contains("cygwin-");
137 let is_pty = name.contains("-pty");
138 is_msys && is_pty
139}
140
141#[cfg(target_family = "wasm")]
143pub fn is(_stream: Stream) -> bool {
144 false
145}