]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/os.rs
Rollup merge of #60429 - estebank:pub-path, r=michaelwoerister
[rust.git] / src / libstd / sys / windows / os.rs
1 //! Implementation of `std::os` functionality for Windows.
2
3 #![allow(nonstandard_style)]
4
5 use crate::os::windows::prelude::*;
6
7 use crate::error::Error as StdError;
8 use crate::ffi::{OsString, OsStr};
9 use crate::fmt;
10 use crate::io;
11 use crate::os::windows::ffi::EncodeWide;
12 use crate::path::{self, PathBuf};
13 use crate::ptr;
14 use crate::slice;
15 use crate::sys::{c, cvt};
16 use crate::sys::handle::Handle;
17
18 use super::to_u16s;
19
20 pub fn errno() -> i32 {
21     unsafe { c::GetLastError() as i32 }
22 }
23
24 /// Gets a detailed string description for the given error number.
25 pub fn error_string(mut errnum: i32) -> String {
26     // This value is calculated from the macro
27     // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
28     let langId = 0x0800 as c::DWORD;
29
30     let mut buf = [0 as c::WCHAR; 2048];
31
32     unsafe {
33         let mut module = ptr::null_mut();
34         let mut flags = 0;
35
36         // NTSTATUS errors may be encoded as HRESULT, which may returned from
37         // GetLastError. For more information about Windows error codes, see
38         // `[MS-ERREF]`: https://msdn.microsoft.com/en-us/library/cc231198.aspx
39         if (errnum & c::FACILITY_NT_BIT as i32) != 0 {
40             // format according to https://support.microsoft.com/en-us/help/259693
41             const NTDLL_DLL: &[u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _,
42                                         '.' as _, 'D' as _, 'L' as _, 'L' as _, 0];
43             module = c::GetModuleHandleW(NTDLL_DLL.as_ptr());
44
45             if module != ptr::null_mut() {
46                 errnum ^= c::FACILITY_NT_BIT as i32;
47                 flags = c::FORMAT_MESSAGE_FROM_HMODULE;
48             }
49         }
50
51         let res = c::FormatMessageW(flags | c::FORMAT_MESSAGE_FROM_SYSTEM |
52                                         c::FORMAT_MESSAGE_IGNORE_INSERTS,
53                                     module,
54                                     errnum as c::DWORD,
55                                     langId,
56                                     buf.as_mut_ptr(),
57                                     buf.len() as c::DWORD,
58                                     ptr::null()) as usize;
59         if res == 0 {
60             // Sometimes FormatMessageW can fail e.g., system doesn't like langId,
61             let fm_err = errno();
62             return format!("OS Error {} (FormatMessageW() returned error {})",
63                            errnum, fm_err);
64         }
65
66         match String::from_utf16(&buf[..res]) {
67             Ok(mut msg) => {
68                 // Trim trailing CRLF inserted by FormatMessageW
69                 let len = msg.trim_end().len();
70                 msg.truncate(len);
71                 msg
72             },
73             Err(..) => format!("OS Error {} (FormatMessageW() returned \
74                                 invalid UTF-16)", errnum),
75         }
76     }
77 }
78
79 pub struct Env {
80     base: c::LPWCH,
81     cur: c::LPWCH,
82 }
83
84 impl Iterator for Env {
85     type Item = (OsString, OsString);
86
87     fn next(&mut self) -> Option<(OsString, OsString)> {
88         loop {
89             unsafe {
90                 if *self.cur == 0 { return None }
91                 let p = &*self.cur as *const u16;
92                 let mut len = 0;
93                 while *p.offset(len) != 0 {
94                     len += 1;
95                 }
96                 let s = slice::from_raw_parts(p, len as usize);
97                 self.cur = self.cur.offset(len + 1);
98
99                 // Windows allows environment variables to start with an equals
100                 // symbol (in any other position, this is the separator between
101                 // variable name and value). Since`s` has at least length 1 at
102                 // this point (because the empty string terminates the array of
103                 // environment variables), we can safely slice.
104                 let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
105                     Some(p) => p,
106                     None => continue,
107                 };
108                 return Some((
109                     OsStringExt::from_wide(&s[..pos]),
110                     OsStringExt::from_wide(&s[pos+1..]),
111                 ))
112             }
113         }
114     }
115 }
116
117 impl Drop for Env {
118     fn drop(&mut self) {
119         unsafe { c::FreeEnvironmentStringsW(self.base); }
120     }
121 }
122
123 pub fn env() -> Env {
124     unsafe {
125         let ch = c::GetEnvironmentStringsW();
126         if ch as usize == 0 {
127             panic!("failure getting env string from OS: {}",
128                    io::Error::last_os_error());
129         }
130         Env { base: ch, cur: ch }
131     }
132 }
133
134 pub struct SplitPaths<'a> {
135     data: EncodeWide<'a>,
136     must_yield: bool,
137 }
138
139 pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
140     SplitPaths {
141         data: unparsed.encode_wide(),
142         must_yield: true,
143     }
144 }
145
146 impl<'a> Iterator for SplitPaths<'a> {
147     type Item = PathBuf;
148     fn next(&mut self) -> Option<PathBuf> {
149         // On Windows, the PATH environment variable is semicolon separated.
150         // Double quotes are used as a way of introducing literal semicolons
151         // (since c:\some;dir is a valid Windows path). Double quotes are not
152         // themselves permitted in path names, so there is no way to escape a
153         // double quote.  Quoted regions can appear in arbitrary locations, so
154         //
155         //   c:\foo;c:\som"e;di"r;c:\bar
156         //
157         // Should parse as [c:\foo, c:\some;dir, c:\bar].
158         //
159         // (The above is based on testing; there is no clear reference available
160         // for the grammar.)
161
162
163         let must_yield = self.must_yield;
164         self.must_yield = false;
165
166         let mut in_progress = Vec::new();
167         let mut in_quote = false;
168         for b in self.data.by_ref() {
169             if b == '"' as u16 {
170                 in_quote = !in_quote;
171             } else if b == ';' as u16 && !in_quote {
172                 self.must_yield = true;
173                 break
174             } else {
175                 in_progress.push(b)
176             }
177         }
178
179         if !must_yield && in_progress.is_empty() {
180             None
181         } else {
182             Some(super::os2path(&in_progress))
183         }
184     }
185 }
186
187 #[derive(Debug)]
188 pub struct JoinPathsError;
189
190 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
191     where I: Iterator<Item=T>, T: AsRef<OsStr>
192 {
193     let mut joined = Vec::new();
194     let sep = b';' as u16;
195
196     for (i, path) in paths.enumerate() {
197         let path = path.as_ref();
198         if i > 0 { joined.push(sep) }
199         let v = path.encode_wide().collect::<Vec<u16>>();
200         if v.contains(&(b'"' as u16)) {
201             return Err(JoinPathsError)
202         } else if v.contains(&sep) {
203             joined.push(b'"' as u16);
204             joined.extend_from_slice(&v[..]);
205             joined.push(b'"' as u16);
206         } else {
207             joined.extend_from_slice(&v[..]);
208         }
209     }
210
211     Ok(OsStringExt::from_wide(&joined[..]))
212 }
213
214 impl fmt::Display for JoinPathsError {
215     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216         "path segment contains `\"`".fmt(f)
217     }
218 }
219
220 impl StdError for JoinPathsError {
221     fn description(&self) -> &str { "failed to join paths" }
222 }
223
224 pub fn current_exe() -> io::Result<PathBuf> {
225     super::fill_utf16_buf(|buf, sz| unsafe {
226         c::GetModuleFileNameW(ptr::null_mut(), buf, sz)
227     }, super::os2path)
228 }
229
230 pub fn getcwd() -> io::Result<PathBuf> {
231     super::fill_utf16_buf(|buf, sz| unsafe {
232         c::GetCurrentDirectoryW(sz, buf)
233     }, super::os2path)
234 }
235
236 pub fn chdir(p: &path::Path) -> io::Result<()> {
237     let p: &OsStr = p.as_ref();
238     let mut p = p.encode_wide().collect::<Vec<_>>();
239     p.push(0);
240
241     cvt(unsafe {
242         c::SetCurrentDirectoryW(p.as_ptr())
243     }).map(|_| ())
244 }
245
246 pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
247     let k = to_u16s(k)?;
248     let res = super::fill_utf16_buf(|buf, sz| unsafe {
249         c::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
250     }, |buf| {
251         OsStringExt::from_wide(buf)
252     });
253     match res {
254         Ok(value) => Ok(Some(value)),
255         Err(e) => {
256             if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) {
257                 Ok(None)
258             } else {
259                 Err(e)
260             }
261         }
262     }
263 }
264
265 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
266     let k = to_u16s(k)?;
267     let v = to_u16s(v)?;
268
269     cvt(unsafe {
270         c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())
271     }).map(|_| ())
272 }
273
274 pub fn unsetenv(n: &OsStr) -> io::Result<()> {
275     let v = to_u16s(n)?;
276     cvt(unsafe {
277         c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())
278     }).map(|_| ())
279 }
280
281 pub fn temp_dir() -> PathBuf {
282     super::fill_utf16_buf(|buf, sz| unsafe {
283         c::GetTempPathW(sz, buf)
284     }, super::os2path).unwrap()
285 }
286
287 pub fn home_dir() -> Option<PathBuf> {
288     crate::env::var_os("HOME").or_else(|| {
289         crate::env::var_os("USERPROFILE")
290     }).map(PathBuf::from).or_else(|| unsafe {
291         let me = c::GetCurrentProcess();
292         let mut token = ptr::null_mut();
293         if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
294             return None
295         }
296         let _handle = Handle::new(token);
297         super::fill_utf16_buf(|buf, mut sz| {
298             match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
299                 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0,
300                 0 => sz,
301                 _ => sz - 1, // sz includes the null terminator
302             }
303         }, super::os2path).ok()
304     })
305 }
306
307 pub fn exit(code: i32) -> ! {
308     unsafe { c::ExitProcess(code as c::UINT) }
309 }
310
311 pub fn getpid() -> u32 {
312     unsafe { c::GetCurrentProcessId() as u32 }
313 }
314
315 #[cfg(test)]
316 mod tests {
317     use crate::io::Error;
318     use crate::sys::c;
319
320     // tests `error_string` above
321     #[test]
322     fn ntstatus_error() {
323         const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001;
324         assert!(!Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _)
325             .to_string().contains("FormatMessageW() returned error"));
326     }
327 }