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