|
1 | 1 | use crate::marker::PhantomData;
|
| 2 | +use crate::mem::size_of; |
| 3 | +use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; |
2 | 4 | use crate::slice;
|
3 | 5 | use crate::sys::c;
|
| 6 | +use core; |
| 7 | +use libc; |
4 | 8 |
|
5 | 9 | #[derive(Copy, Clone)]
|
6 | 10 | #[repr(transparent)]
|
@@ -78,3 +82,58 @@ impl<'a> IoSliceMut<'a> {
|
78 | 82 | unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.len as usize) }
|
79 | 83 | }
|
80 | 84 | }
|
| 85 | + |
| 86 | +pub fn is_terminal(h: &impl AsHandle) -> bool { |
| 87 | + unsafe { handle_is_console(h.as_handle()) } |
| 88 | +} |
| 89 | + |
| 90 | +unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { |
| 91 | + let handle = handle.as_raw_handle(); |
| 92 | + |
| 93 | + let mut out = 0; |
| 94 | + if c::GetConsoleMode(handle, &mut out) != 0 { |
| 95 | + // False positives aren't possible. If we got a console then we definitely have a console. |
| 96 | + return true; |
| 97 | + } |
| 98 | + |
| 99 | + // At this point, we *could* have a false negative. We can determine that this is a true |
| 100 | + // negative if we can detect the presence of a console on any of the standard I/O streams. If |
| 101 | + // another stream has a console, then we know we're in a Windows console and can therefore |
| 102 | + // trust the negative. |
| 103 | + for std_handle in [c::STD_INPUT_HANDLE, c::STD_OUTPUT_HANDLE, c::STD_ERROR_HANDLE] { |
| 104 | + let std_handle = c::GetStdHandle(std_handle); |
| 105 | + if std_handle != handle && c::GetConsoleMode(std_handle, &mut out) != 0 { |
| 106 | + return false; |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty. |
| 111 | + msys_tty_on(handle) |
| 112 | +} |
| 113 | + |
| 114 | +unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { |
| 115 | + let size = size_of::<c::FILE_NAME_INFO>() + c::MAX_PATH * size_of::<c::WCHAR>(); |
| 116 | + let mut name_info_bytes = vec![0u8; size]; |
| 117 | + let res = c::GetFileInformationByHandleEx( |
| 118 | + handle, |
| 119 | + c::FileNameInfo, |
| 120 | + name_info_bytes.as_mut_ptr() as *mut libc::c_void, |
| 121 | + size as u32, |
| 122 | + ); |
| 123 | + if res == 0 { |
| 124 | + return false; |
| 125 | + } |
| 126 | + let name_info: &c::FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const c::FILE_NAME_INFO); |
| 127 | + let s = core::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 | + // This checks whether 'pty' exists in the file name, which indicates that |
| 133 | + // a pseudo-terminal is attached. To mitigate against false positives |
| 134 | + // (e.g., an actual file name that contains 'pty'), we also require that |
| 135 | + // either the strings 'msys-' or 'cygwin-' are in the file name as well.) |
| 136 | + let is_msys = name.contains("msys-") || name.contains("cygwin-"); |
| 137 | + let is_pty = name.contains("-pty"); |
| 138 | + is_msys && is_pty |
| 139 | +} |
0 commit comments