1 //! atty is a simple utility that answers one question
4 //! usage is just as simple
7 //! if atty::is(atty::Stream::Stdout) {
8 //! println!("i'm a tty")
13 //! if atty::isnt(atty::Stream::Stdout) {
14 //! println!("i'm not a tty")
19 extern crate kernel32
;
26 use winapi
::minwindef
::DWORD
;
28 /// possible stream sources
29 #[derive(Clone, Copy, Debug)]
36 /// returns true if this is a tty
38 pub fn is(stream
: Stream
) -> bool
{
41 let fd
= match stream
{
42 Stream
::Stdout
=> libc
::STDOUT_FILENO
,
43 Stream
::Stderr
=> libc
::STDERR_FILENO
,
44 Stream
::Stdin
=> libc
::STDIN_FILENO
,
46 unsafe { libc::isatty(fd) != 0 }
49 /// returns true if this is a tty
51 pub fn is(stream
: Stream
) -> bool
{
53 STD_INPUT_HANDLE
as STD_INPUT
,
54 STD_ERROR_HANDLE
as STD_ERROR
,
55 STD_OUTPUT_HANDLE
as STD_OUTPUT
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
]),
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.
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) }
{
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) }
82 /// returns true if this is _not_ a tty
83 pub fn isnt(stream
: Stream
) -> bool
{
87 /// Returns true if any of the given fds are on a console.
89 unsafe fn console_on_any(fds
: &[DWORD
]) -> bool
{
92 let handle
= kernel32
::GetStdHandle(fd
);
93 if kernel32
::GetConsoleMode(handle
, &mut out
) != 0 {
100 /// Returns true if there is an MSYS tty on the given handle.
102 unsafe fn msys_tty_on(fd
: DWORD
) -> bool
{
103 use std
::ffi
::OsString
;
105 use std
::os
::raw
::c_void
;
106 use std
::os
::windows
::ffi
::OsStringExt
;
109 use kernel32
::GetFileInformationByHandleEx
;
110 use winapi
::fileapi
::FILE_NAME_INFO
;
111 use winapi
::minwinbase
::FileNameInfo
;
112 use winapi
::minwindef
::MAX_PATH
;
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
),
119 &mut *name_info_bytes
as *mut _
as *mut c_void
,
120 name_info_bytes
.len() as u32);
124 let name_info
: FILE_NAME_INFO
=
125 *(name_info_bytes
[0..size
].as_ptr() as *const FILE_NAME_INFO
);
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")
137 use super::{is, Stream}
;
142 // appveyor pipes its output
143 assert
!(!is(Stream
::Stderr
))
149 // appveyor pipes its output
150 assert
!(!is(Stream
::Stdout
))
156 assert
!(is(Stream
::Stdin
))
162 assert
!(is(Stream
::Stderr
))
168 assert
!(is(Stream
::Stdout
))
172 #[cfg(target_os = "macos")]
174 // macos on travis seems to pipe its input
175 assert
!(!is(Stream
::Stdin
))
179 #[cfg(all(not(target_os = "macos"), unix))]
181 assert
!(is(Stream
::Stdin
))