forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwindows.rs
71 lines (64 loc) · 2.49 KB
/
windows.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use crate::ffi::c_void;
use crate::mem::size_of;
use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle};
use crate::sys::c;
pub fn is_terminal(h: &impl AsHandle) -> bool {
handle_is_console(h.as_handle())
}
fn handle_is_console(handle: BorrowedHandle<'_>) -> bool {
// A null handle means the process has no console.
if handle.as_raw_handle().is_null() {
return false;
}
let mut out = 0;
if unsafe { c::GetConsoleMode(handle.as_raw_handle(), &mut out) != 0 } {
// False positives aren't possible. If we got a console then we definitely have a console.
return true;
}
// Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty.
msys_tty_on(handle)
}
fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool {
// Early return if the handle is not a pipe.
if unsafe { c::GetFileType(handle.as_raw_handle()) != c::FILE_TYPE_PIPE } {
return false;
}
/// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack
/// allocate
///
/// [`FILE_NAME_INFO`]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_name_info
#[repr(C)]
#[allow(non_snake_case)]
struct FILE_NAME_INFO {
FileNameLength: u32,
FileName: [u16; c::MAX_PATH as usize],
}
let mut name_info = FILE_NAME_INFO { FileNameLength: 0, FileName: [0; c::MAX_PATH as usize] };
// Safety: buffer length is fixed.
let res = unsafe {
c::GetFileInformationByHandleEx(
handle.as_raw_handle(),
c::FileNameInfo,
(&raw mut name_info) as *mut c_void,
size_of::<FILE_NAME_INFO>() as u32,
)
};
if res == 0 {
return false;
}
// Use `get` because `FileNameLength` can be out of range.
let s = match name_info.FileName.get(..name_info.FileNameLength as usize / 2) {
None => return false,
Some(s) => s,
};
let name = String::from_utf16_lossy(s);
// Get the file name only.
let name = name.rsplit('\\').next().unwrap_or(&name);
// This checks whether 'pty' exists in the file name, which indicates that
// a pseudo-terminal is attached. To mitigate against false positives
// (e.g., an actual file name that contains 'pty'), we also require that
// the file name begins with either the strings 'msys-' or 'cygwin-'.)
let is_msys = name.starts_with("msys-") || name.starts_with("cygwin-");
let is_pty = name.contains("-pty");
is_msys && is_pty
}