]> git.proxmox.com Git - rustc.git/blame - vendor/atty/src/lib.rs
New upstream version 1.49.0+dfsg1
[rustc.git] / vendor / atty / src / lib.rs
CommitLineData
041b39d2
XL
1//! atty is a simple utility that answers one question
2//! > is this a tty?
3//!
4//! usage is just as simple
5//!
6//! ```
7//! if atty::is(atty::Stream::Stdout) {
8//! println!("i'm a tty")
9//! }
10//! ```
11//!
12//! ```
13//! if atty::isnt(atty::Stream::Stdout) {
14//! println!("i'm not a tty")
15//! }
16//! ```
17
abe05a73
XL
18#![cfg_attr(unix, no_std)]
19
abe05a73 20#[cfg(unix)]
041b39d2
XL
21extern crate libc;
22#[cfg(windows)]
23extern crate winapi;
24
25#[cfg(windows)]
2c00a5a8 26use winapi::shared::minwindef::DWORD;
8faf50e0
XL
27#[cfg(windows)]
28use winapi::shared::ntdef::WCHAR;
041b39d2
XL
29
30/// possible stream sources
31#[derive(Clone, Copy, Debug)]
32pub enum Stream {
33 Stdout,
34 Stderr,
35 Stdin,
36}
37
38/// returns true if this is a tty
8faf50e0 39#[cfg(all(unix, not(target_arch = "wasm32")))]
041b39d2
XL
40pub fn is(stream: Stream) -> bool {
41 extern crate libc;
42
43 let fd = match stream {
44 Stream::Stdout => libc::STDOUT_FILENO,
45 Stream::Stderr => libc::STDERR_FILENO,
46 Stream::Stdin => libc::STDIN_FILENO,
47 };
48 unsafe { libc::isatty(fd) != 0 }
49}
50
72b1a166
FG
51/// returns true if this is a tty
52#[cfg(target_os = "hermit")]
53pub fn is(stream: Stream) -> bool {
54 extern crate hermit_abi;
55
56 let fd = match stream {
57 Stream::Stdout => hermit_abi::STDOUT_FILENO,
58 Stream::Stderr => hermit_abi::STDERR_FILENO,
59 Stream::Stdin => hermit_abi::STDIN_FILENO,
60 };
61 hermit_abi::isatty(fd)
62}
63
041b39d2
XL
64/// returns true if this is a tty
65#[cfg(windows)]
66pub fn is(stream: Stream) -> bool {
72b1a166
FG
67 use winapi::um::winbase::{
68 STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
69 STD_OUTPUT_HANDLE as STD_OUTPUT,
70 };
041b39d2
XL
71
72 let (fd, others) = match stream {
73 Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]),
74 Stream::Stderr => (STD_ERROR, [STD_INPUT, STD_OUTPUT]),
75 Stream::Stdout => (STD_OUTPUT, [STD_INPUT, STD_ERROR]),
76 };
77 if unsafe { console_on_any(&[fd]) } {
78 // False positives aren't possible. If we got a console then
79 // we definitely have a tty on stdin.
80 return true;
81 }
82
83 // At this point, we *could* have a false negative. We can determine that
84 // this is true negative if we can detect the presence of a console on
85 // any of the other streams. If another stream has a console, then we know
86 // we're in a Windows console and can therefore trust the negative.
87 if unsafe { console_on_any(&others) } {
88 return false;
89 }
90
91 // Otherwise, we fall back to a very strange msys hack to see if we can
92 // sneakily detect the presence of a tty.
93 unsafe { msys_tty_on(fd) }
94}
95
96/// returns true if this is _not_ a tty
97pub fn isnt(stream: Stream) -> bool {
98 !is(stream)
99}
100
101/// Returns true if any of the given fds are on a console.
102#[cfg(windows)]
103unsafe fn console_on_any(fds: &[DWORD]) -> bool {
72b1a166 104 use winapi::um::{consoleapi::GetConsoleMode, processenv::GetStdHandle};
2c00a5a8 105
041b39d2
XL
106 for &fd in fds {
107 let mut out = 0;
2c00a5a8
XL
108 let handle = GetStdHandle(fd);
109 if GetConsoleMode(handle, &mut out) != 0 {
041b39d2
XL
110 return true;
111 }
112 }
113 false
114}
115
116/// Returns true if there is an MSYS tty on the given handle.
117#[cfg(windows)]
118unsafe fn msys_tty_on(fd: DWORD) -> bool {
72b1a166
FG
119 use std::{mem, slice};
120
121 use winapi::{
122 ctypes::c_void,
123 shared::minwindef::MAX_PATH,
124 um::{
125 fileapi::FILE_NAME_INFO, minwinbase::FileNameInfo, processenv::GetStdHandle,
126 winbase::GetFileInformationByHandleEx,
127 },
128 };
041b39d2
XL
129
130 let size = mem::size_of::<FILE_NAME_INFO>();
8faf50e0 131 let mut name_info_bytes = vec![0u8; size + MAX_PATH * mem::size_of::<WCHAR>()];
041b39d2 132 let res = GetFileInformationByHandleEx(
2c00a5a8 133 GetStdHandle(fd),
041b39d2
XL
134 FileNameInfo,
135 &mut *name_info_bytes as *mut _ as *mut c_void,
abe05a73
XL
136 name_info_bytes.len() as u32,
137 );
041b39d2 138 if res == 0 {
0531ce1d 139 return false;
041b39d2 140 }
8faf50e0
XL
141 let name_info: &FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const FILE_NAME_INFO);
142 let s = slice::from_raw_parts(
143 name_info.FileName.as_ptr(),
144 name_info.FileNameLength as usize / 2,
abe05a73 145 );
8faf50e0
XL
146 let name = String::from_utf16_lossy(s);
147 // This checks whether 'pty' exists in the file name, which indicates that
148 // a pseudo-terminal is attached. To mitigate against false positives
149 // (e.g., an actual file name that contains 'pty'), we also require that
150 // either the strings 'msys-' or 'cygwin-' are in the file name as well.)
151 let is_msys = name.contains("msys-") || name.contains("cygwin-");
152 let is_pty = name.contains("-pty");
153 is_msys && is_pty
041b39d2
XL
154}
155
2c00a5a8
XL
156/// returns true if this is a tty
157#[cfg(target_arch = "wasm32")]
158pub fn is(_stream: Stream) -> bool {
159 false
160}
161
041b39d2
XL
162#[cfg(test)]
163mod tests {
72b1a166 164 use super::{is, Stream};
041b39d2
XL
165
166 #[test]
167 #[cfg(windows)]
168 fn is_err() {
169 // appveyor pipes its output
170 assert!(!is(Stream::Stderr))
171 }
172
173 #[test]
174 #[cfg(windows)]
175 fn is_out() {
176 // appveyor pipes its output
177 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 // macos on travis seems to pipe its input
abe05a73 202 assert!(is(Stream::Stdin))
041b39d2
XL
203 }
204
205 #[test]
206 #[cfg(all(not(target_os = "macos"), unix))]
207 fn is_in() {
208 assert!(is(Stream::Stdin))
209 }
210}