]> git.proxmox.com Git - rustc.git/blob - src/vendor/atty/src/lib.rs
New upstream version 1.20.0+dfsg1
[rustc.git] / src / vendor / atty / src / lib.rs
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
18 #[cfg(windows)]
19 extern crate kernel32;
20 #[cfg(not(windows))]
21 extern crate libc;
22 #[cfg(windows)]
23 extern crate winapi;
24
25 #[cfg(windows)]
26 use winapi::minwindef::DWORD;
27
28 /// possible stream sources
29 #[derive(Clone, Copy, Debug)]
30 pub enum Stream {
31 Stdout,
32 Stderr,
33 Stdin,
34 }
35
36 /// returns true if this is a tty
37 #[cfg(unix)]
38 pub fn is(stream: Stream) -> bool {
39 extern crate libc;
40
41 let fd = match stream {
42 Stream::Stdout => libc::STDOUT_FILENO,
43 Stream::Stderr => libc::STDERR_FILENO,
44 Stream::Stdin => libc::STDIN_FILENO,
45 };
46 unsafe { libc::isatty(fd) != 0 }
47 }
48
49 /// returns true if this is a tty
50 #[cfg(windows)]
51 pub fn is(stream: Stream) -> bool {
52 use winapi::{
53 STD_INPUT_HANDLE as STD_INPUT,
54 STD_ERROR_HANDLE as STD_ERROR,
55 STD_OUTPUT_HANDLE as STD_OUTPUT
56 };
57
58 let (fd, others) = match stream {
59 Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]),
60 Stream::Stderr => (STD_ERROR, [STD_INPUT, STD_OUTPUT]),
61 Stream::Stdout => (STD_OUTPUT, [STD_INPUT, STD_ERROR]),
62 };
63 if unsafe { console_on_any(&[fd]) } {
64 // False positives aren't possible. If we got a console then
65 // we definitely have a tty on stdin.
66 return true;
67 }
68
69 // At this point, we *could* have a false negative. We can determine that
70 // this is true negative if we can detect the presence of a console on
71 // any of the other streams. If another stream has a console, then we know
72 // we're in a Windows console and can therefore trust the negative.
73 if unsafe { console_on_any(&others) } {
74 return false;
75 }
76
77 // Otherwise, we fall back to a very strange msys hack to see if we can
78 // sneakily detect the presence of a tty.
79 unsafe { msys_tty_on(fd) }
80 }
81
82 /// returns true if this is _not_ a tty
83 pub fn isnt(stream: Stream) -> bool {
84 !is(stream)
85 }
86
87 /// Returns true if any of the given fds are on a console.
88 #[cfg(windows)]
89 unsafe fn console_on_any(fds: &[DWORD]) -> bool {
90 for &fd in fds {
91 let mut out = 0;
92 let handle = kernel32::GetStdHandle(fd);
93 if kernel32::GetConsoleMode(handle, &mut out) != 0 {
94 return true;
95 }
96 }
97 false
98 }
99
100 /// Returns true if there is an MSYS tty on the given handle.
101 #[cfg(windows)]
102 unsafe fn msys_tty_on(fd: DWORD) -> bool {
103 use std::ffi::OsString;
104 use std::mem;
105 use std::os::raw::c_void;
106 use std::os::windows::ffi::OsStringExt;
107 use std::slice;
108
109 use kernel32::GetFileInformationByHandleEx;
110 use winapi::fileapi::FILE_NAME_INFO;
111 use winapi::minwinbase::FileNameInfo;
112 use winapi::minwindef::MAX_PATH;
113
114 let size = mem::size_of::<FILE_NAME_INFO>();
115 let mut name_info_bytes = vec![0u8; size + MAX_PATH];
116 let res = GetFileInformationByHandleEx(
117 kernel32::GetStdHandle(fd),
118 FileNameInfo,
119 &mut *name_info_bytes as *mut _ as *mut c_void,
120 name_info_bytes.len() as u32);
121 if res == 0 {
122 return true;
123 }
124 let name_info: FILE_NAME_INFO =
125 *(name_info_bytes[0..size].as_ptr() as *const FILE_NAME_INFO);
126 let name_bytes =
127 &name_info_bytes[size..size + name_info.FileNameLength as usize];
128 let name_u16 = slice::from_raw_parts(
129 name_bytes.as_ptr() as *const u16, name_bytes.len() / 2);
130 let name = OsString::from_wide(name_u16)
131 .as_os_str().to_string_lossy().into_owned();
132 name.contains("msys-") || name.contains("-pty")
133 }
134
135 #[cfg(test)]
136 mod tests {
137 use super::{is, Stream};
138
139 #[test]
140 #[cfg(windows)]
141 fn is_err() {
142 // appveyor pipes its output
143 assert!(!is(Stream::Stderr))
144 }
145
146 #[test]
147 #[cfg(windows)]
148 fn is_out() {
149 // appveyor pipes its output
150 assert!(!is(Stream::Stdout))
151 }
152
153 #[test]
154 #[cfg(windows)]
155 fn is_in() {
156 assert!(is(Stream::Stdin))
157 }
158
159 #[test]
160 #[cfg(unix)]
161 fn is_err() {
162 assert!(is(Stream::Stderr))
163 }
164
165 #[test]
166 #[cfg(unix)]
167 fn is_out() {
168 assert!(is(Stream::Stdout))
169 }
170
171 #[test]
172 #[cfg(target_os = "macos")]
173 fn is_in() {
174 // macos on travis seems to pipe its input
175 assert!(!is(Stream::Stdin))
176 }
177
178 #[test]
179 #[cfg(all(not(target_os = "macos"), unix))]
180 fn is_in() {
181 assert!(is(Stream::Stdin))
182 }
183 }