]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/windows/io.rs
Auto merge of #99918 - WaffleLapkin:fnFnfun, r=estebank
[rust.git] / library / std / src / sys / windows / io.rs
1 use crate::marker::PhantomData;
2 use crate::mem::size_of;
3 use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle};
4 use crate::slice;
5 use crate::sys::{c, Align8};
6 use core;
7 use libc;
8
9 #[derive(Copy, Clone)]
10 #[repr(transparent)]
11 pub struct IoSlice<'a> {
12     vec: c::WSABUF,
13     _p: PhantomData<&'a [u8]>,
14 }
15
16 impl<'a> IoSlice<'a> {
17     #[inline]
18     pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
19         assert!(buf.len() <= c::ULONG::MAX as usize);
20         IoSlice {
21             vec: c::WSABUF {
22                 len: buf.len() as c::ULONG,
23                 buf: buf.as_ptr() as *mut u8 as *mut c::CHAR,
24             },
25             _p: PhantomData,
26         }
27     }
28
29     #[inline]
30     pub fn advance(&mut self, n: usize) {
31         if (self.vec.len as usize) < n {
32             panic!("advancing IoSlice beyond its length");
33         }
34
35         unsafe {
36             self.vec.len -= n as c::ULONG;
37             self.vec.buf = self.vec.buf.add(n);
38         }
39     }
40
41     #[inline]
42     pub fn as_slice(&self) -> &[u8] {
43         unsafe { slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize) }
44     }
45 }
46
47 #[repr(transparent)]
48 pub struct IoSliceMut<'a> {
49     vec: c::WSABUF,
50     _p: PhantomData<&'a mut [u8]>,
51 }
52
53 impl<'a> IoSliceMut<'a> {
54     #[inline]
55     pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
56         assert!(buf.len() <= c::ULONG::MAX as usize);
57         IoSliceMut {
58             vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_mut_ptr() as *mut c::CHAR },
59             _p: PhantomData,
60         }
61     }
62
63     #[inline]
64     pub fn advance(&mut self, n: usize) {
65         if (self.vec.len as usize) < n {
66             panic!("advancing IoSliceMut beyond its length");
67         }
68
69         unsafe {
70             self.vec.len -= n as c::ULONG;
71             self.vec.buf = self.vec.buf.add(n);
72         }
73     }
74
75     #[inline]
76     pub fn as_slice(&self) -> &[u8] {
77         unsafe { slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize) }
78     }
79
80     #[inline]
81     pub fn as_mut_slice(&mut self) -> &mut [u8] {
82         unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.len as usize) }
83     }
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     // A null handle means the process has no console.
94     if handle.is_null() {
95         return false;
96     }
97
98     let mut out = 0;
99     if c::GetConsoleMode(handle, &mut out) != 0 {
100         // False positives aren't possible. If we got a console then we definitely have a console.
101         return true;
102     }
103
104     // At this point, we *could* have a false negative. We can determine that this is a true
105     // negative if we can detect the presence of a console on any of the standard I/O streams. If
106     // another stream has a console, then we know we're in a Windows console and can therefore
107     // trust the negative.
108     for std_handle in [c::STD_INPUT_HANDLE, c::STD_OUTPUT_HANDLE, c::STD_ERROR_HANDLE] {
109         let std_handle = c::GetStdHandle(std_handle);
110         if !std_handle.is_null()
111             && std_handle != handle
112             && c::GetConsoleMode(std_handle, &mut out) != 0
113         {
114             return false;
115         }
116     }
117
118     // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty.
119     msys_tty_on(handle)
120 }
121
122 unsafe fn msys_tty_on(handle: c::HANDLE) -> bool {
123     // Early return if the handle is not a pipe.
124     if c::GetFileType(handle) != c::FILE_TYPE_PIPE {
125         return false;
126     }
127
128     const SIZE: usize = size_of::<c::FILE_NAME_INFO>() + c::MAX_PATH * size_of::<c::WCHAR>();
129     let mut name_info_bytes = Align8([0u8; SIZE]);
130     let res = c::GetFileInformationByHandleEx(
131         handle,
132         c::FileNameInfo,
133         name_info_bytes.0.as_mut_ptr() as *mut libc::c_void,
134         SIZE as u32,
135     );
136     if res == 0 {
137         return false;
138     }
139     let name_info: &c::FILE_NAME_INFO = &*(name_info_bytes.0.as_ptr() as *const c::FILE_NAME_INFO);
140     let name_len = name_info.FileNameLength as usize / 2;
141     // Offset to get the `FileName` field.
142     let name_ptr = name_info_bytes.0.as_ptr().offset(size_of::<c::DWORD>() as isize).cast::<u16>();
143     let s = core::slice::from_raw_parts(name_ptr, name_len);
144     let name = String::from_utf16_lossy(s);
145     // Get the file name only.
146     let name = name.rsplit('\\').next().unwrap_or(&name);
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     // the file name begins with either the strings 'msys-' or 'cygwin-'.)
151     let is_msys = name.starts_with("msys-") || name.starts_with("cygwin-");
152     let is_pty = name.contains("-pty");
153     is_msys && is_pty
154 }