1#![cfg_attr(unix, no_std)]
19
20#[cfg(unix)]
21extern crate libc;
22#[cfg(windows)]
23extern crate winapi;
24#[cfg(target_os = "redox")]
25extern crate termion;
26
27#[cfg(windows)]
28use winapi::shared::minwindef::DWORD;
29#[cfg(windows)]
30use winapi::shared::ntdef::WCHAR;
31
32#[derive(Clone, Copy, Debug)]
34pub enum Stream {
35 Stdout,
36 Stderr,
37 Stdin,
38}
39
40#[cfg(all(unix, not(target_arch = "wasm32")))]
42pub fn is(stream: Stream) -> bool {
43 extern crate libc;
44
45 let fd = match stream {
46 Stream::Stdout => libc::STDOUT_FILENO,
47 Stream::Stderr => libc::STDERR_FILENO,
48 Stream::Stdin => libc::STDIN_FILENO,
49 };
50 unsafe { libc::isatty(fd) != 0 }
51}
52
53#[cfg(windows)]
55pub fn is(stream: Stream) -> bool {
56 use winapi::um::winbase::{STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
57 STD_OUTPUT_HANDLE as STD_OUTPUT};
58
59 let (fd, others) = match stream {
60 Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]),
61 Stream::Stderr => (STD_ERROR, [STD_INPUT, STD_OUTPUT]),
62 Stream::Stdout => (STD_OUTPUT, [STD_INPUT, STD_ERROR]),
63 };
64 if unsafe { console_on_any(&[fd]) } {
65 return true;
68 }
69
70 if unsafe { console_on_any(&others) } {
75 return false;
76 }
77
78 unsafe { msys_tty_on(fd) }
81}
82
83pub fn isnt(stream: Stream) -> bool {
85 !is(stream)
86}
87
88#[cfg(windows)]
90unsafe fn console_on_any(fds: &[DWORD]) -> bool {
91 use winapi::um::consoleapi::GetConsoleMode;
92 use winapi::um::processenv::GetStdHandle;
93
94 for &fd in fds {
95 let mut out = 0;
96 let handle = GetStdHandle(fd);
97 if GetConsoleMode(handle, &mut out) != 0 {
98 return true;
99 }
100 }
101 false
102}
103
104#[cfg(windows)]
106unsafe fn msys_tty_on(fd: DWORD) -> bool {
107 use std::mem;
108 use std::slice;
109
110 use winapi::ctypes::c_void;
111 use winapi::um::winbase::GetFileInformationByHandleEx;
112 use winapi::um::fileapi::FILE_NAME_INFO;
113 use winapi::um::minwinbase::FileNameInfo;
114 use winapi::um::processenv::GetStdHandle;
115 use winapi::shared::minwindef::MAX_PATH;
116
117 let size = mem::size_of::<FILE_NAME_INFO>();
118 let mut name_info_bytes = vec![0u8; size + MAX_PATH * mem::size_of::<WCHAR>()];
119 let res = GetFileInformationByHandleEx(
120 GetStdHandle(fd),
121 FileNameInfo,
122 &mut *name_info_bytes as *mut _ as *mut c_void,
123 name_info_bytes.len() as u32,
124 );
125 if res == 0 {
126 return false;
127 }
128 let name_info: &FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const FILE_NAME_INFO);
129 let s = slice::from_raw_parts(
130 name_info.FileName.as_ptr(),
131 name_info.FileNameLength as usize / 2,
132 );
133 let name = String::from_utf16_lossy(s);
134 let is_msys = name.contains("msys-") || name.contains("cygwin-");
139 let is_pty = name.contains("-pty");
140 is_msys && is_pty
141}
142
143#[cfg(target_os = "redox")]
145pub fn is(stream: Stream) -> bool {
146 use std::io;
147 use termion::is_tty;
148
149 match stream {
150 Stream::Stdin => is_tty(&io::stdin()),
151 Stream::Stdout => is_tty(&io::stdout()),
152 Stream::Stderr => is_tty(&io::stderr()),
153 }
154}
155
156#[cfg(target_arch = "wasm32")]
158pub fn is(_stream: Stream) -> bool {
159 false
160}
161
162#[cfg(test)]
163mod tests {
164 use super::{Stream, is};
165
166 #[test]
167 #[cfg(windows)]
168 fn is_err() {
169 assert!(!is(Stream::Stderr))
171 }
172
173 #[test]
174 #[cfg(windows)]
175 fn is_out() {
176 assert!(!is(Stream::Stdout))
178 }
179
180 #[test]
181 #[cfg(windows)]
182 fn is_in() {
183 assert!(is(Stream::Stdin))
184 }
185
186 #[test]
187 #[cfg(unix)]
188 fn is_err() {
189 assert!(is(Stream::Stderr))
190 }
191
192 #[test]
193 #[cfg(unix)]
194 fn is_out() {
195 assert!(is(Stream::Stdout))
196 }
197
198 #[test]
199 #[cfg(target_os = "macos")]
200 fn is_in() {
201 assert!(is(Stream::Stdin))
203 }
204
205 #[test]
206 #[cfg(all(not(target_os = "macos"), unix))]
207 fn is_in() {
208 assert!(is(Stream::Stdin))
209 }
210}