]>
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; | |
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; | |
1a4d82fc | 17 | |
92a42be0 | 18 | use super::to_u16s; |
1a4d82fc | 19 | |
85aaf69f | 20 | pub fn errno() -> i32 { |
92a42be0 | 21 | unsafe { c::GetLastError() as i32 } |
1a4d82fc JJ |
22 | } |
23 | ||
9346a6ac | 24 | /// Gets a detailed string description for the given error number. |
7cac9316 | 25 | pub fn error_string(mut errnum: i32) -> String { |
1a4d82fc JJ |
26 | // This value is calculated from the macro |
27 | // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) | |
92a42be0 | 28 | let langId = 0x0800 as c::DWORD; |
1a4d82fc | 29 | |
92a42be0 | 30 | let mut buf = [0 as c::WCHAR; 2048]; |
1a4d82fc JJ |
31 | |
32 | unsafe { | |
7cac9316 XL |
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 | |
0731742a XL |
41 | const NTDLL_DLL: &[u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, |
42 | '.' as _, 'D' as _, 'L' as _, 'L' as _, 0]; | |
7cac9316 XL |
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 | | |
92a42be0 | 52 | c::FORMAT_MESSAGE_IGNORE_INSERTS, |
7cac9316 | 53 | module, |
92a42be0 SL |
54 | errnum as c::DWORD, |
55 | langId, | |
56 | buf.as_mut_ptr(), | |
57 | buf.len() as c::DWORD, | |
58 | ptr::null()) as usize; | |
1a4d82fc | 59 | if res == 0 { |
0731742a | 60 | // Sometimes FormatMessageW can fail e.g., system doesn't like langId, |
1a4d82fc | 61 | let fm_err = errno(); |
85aaf69f SL |
62 | return format!("OS Error {} (FormatMessageW() returned error {})", |
63 | errnum, fm_err); | |
1a4d82fc JJ |
64 | } |
65 | ||
e9174d1e SL |
66 | match String::from_utf16(&buf[..res]) { |
67 | Ok(mut msg) => { | |
68 | // Trim trailing CRLF inserted by FormatMessageW | |
0731742a | 69 | let len = msg.trim_end().len(); |
e9174d1e SL |
70 | msg.truncate(len); |
71 | msg | |
72 | }, | |
1a4d82fc JJ |
73 | Err(..) => format!("OS Error {} (FormatMessageW() returned \ |
74 | invalid UTF-16)", errnum), | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
85aaf69f | 79 | pub struct Env { |
92a42be0 SL |
80 | base: c::LPWCH, |
81 | cur: c::LPWCH, | |
1a4d82fc JJ |
82 | } |
83 | ||
85aaf69f SL |
84 | impl Iterator for Env { |
85 | type Item = (OsString, OsString); | |
86 | ||
87 | fn next(&mut self) -> Option<(OsString, OsString)> { | |
92a42be0 SL |
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 | )) | |
1a4d82fc JJ |
112 | } |
113 | } | |
1a4d82fc JJ |
114 | } |
115 | } | |
116 | ||
85aaf69f SL |
117 | impl Drop for Env { |
118 | fn drop(&mut self) { | |
92a42be0 | 119 | unsafe { c::FreeEnvironmentStringsW(self.base); } |
85aaf69f SL |
120 | } |
121 | } | |
1a4d82fc | 122 | |
85aaf69f | 123 | pub fn env() -> Env { |
1a4d82fc | 124 | unsafe { |
92a42be0 | 125 | let ch = c::GetEnvironmentStringsW(); |
85aaf69f SL |
126 | if ch as usize == 0 { |
127 | panic!("failure getting env string from OS: {}", | |
c34b1796 | 128 | io::Error::last_os_error()); |
1a4d82fc | 129 | } |
85aaf69f | 130 | Env { base: ch, cur: ch } |
1a4d82fc | 131 | } |
85aaf69f | 132 | } |
1a4d82fc | 133 | |
85aaf69f SL |
134 | pub struct SplitPaths<'a> { |
135 | data: EncodeWide<'a>, | |
136 | must_yield: bool, | |
1a4d82fc JJ |
137 | } |
138 | ||
532ac7d7 | 139 | pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { |
85aaf69f SL |
140 | SplitPaths { |
141 | data: unparsed.encode_wide(), | |
142 | must_yield: true, | |
1a4d82fc | 143 | } |
1a4d82fc JJ |
144 | } |
145 | ||
85aaf69f | 146 | impl<'a> Iterator for SplitPaths<'a> { |
c34b1796 AL |
147 | type Item = PathBuf; |
148 | fn next(&mut self) -> Option<PathBuf> { | |
85aaf69f SL |
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 { | |
1a4d82fc | 170 | in_quote = !in_quote; |
85aaf69f SL |
171 | } else if b == ';' as u16 && !in_quote { |
172 | self.must_yield = true; | |
173 | break | |
174 | } else { | |
175 | in_progress.push(b) | |
1a4d82fc | 176 | } |
85aaf69f SL |
177 | } |
178 | ||
179 | if !must_yield && in_progress.is_empty() { | |
180 | None | |
181 | } else { | |
c34b1796 | 182 | Some(super::os2path(&in_progress)) |
1a4d82fc JJ |
183 | } |
184 | } | |
1a4d82fc JJ |
185 | } |
186 | ||
85aaf69f SL |
187 | #[derive(Debug)] |
188 | pub struct JoinPathsError; | |
189 | ||
190 | pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> | |
c34b1796 | 191 | where I: Iterator<Item=T>, T: AsRef<OsStr> |
85aaf69f | 192 | { |
1a4d82fc | 193 | let mut joined = Vec::new(); |
85aaf69f | 194 | let sep = b';' as u16; |
1a4d82fc | 195 | |
85aaf69f | 196 | for (i, path) in paths.enumerate() { |
c34b1796 | 197 | let path = path.as_ref(); |
1a4d82fc | 198 | if i > 0 { joined.push(sep) } |
85aaf69f SL |
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); | |
92a42be0 | 204 | joined.extend_from_slice(&v[..]); |
85aaf69f | 205 | joined.push(b'"' as u16); |
1a4d82fc | 206 | } else { |
92a42be0 | 207 | joined.extend_from_slice(&v[..]); |
1a4d82fc JJ |
208 | } |
209 | } | |
210 | ||
85aaf69f | 211 | Ok(OsStringExt::from_wide(&joined[..])) |
1a4d82fc JJ |
212 | } |
213 | ||
85aaf69f | 214 | impl fmt::Display for JoinPathsError { |
532ac7d7 | 215 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
85aaf69f | 216 | "path segment contains `\"`".fmt(f) |
1a4d82fc JJ |
217 | } |
218 | } | |
219 | ||
85aaf69f SL |
220 | impl StdError for JoinPathsError { |
221 | fn description(&self) -> &str { "failed to join paths" } | |
222 | } | |
223 | ||
c34b1796 | 224 | pub fn current_exe() -> io::Result<PathBuf> { |
9346a6ac | 225 | super::fill_utf16_buf(|buf, sz| unsafe { |
92a42be0 | 226 | c::GetModuleFileNameW(ptr::null_mut(), buf, sz) |
85aaf69f SL |
227 | }, super::os2path) |
228 | } | |
229 | ||
c34b1796 | 230 | pub fn getcwd() -> io::Result<PathBuf> { |
9346a6ac | 231 | super::fill_utf16_buf(|buf, sz| unsafe { |
92a42be0 | 232 | c::GetCurrentDirectoryW(sz, buf) |
85aaf69f SL |
233 | }, super::os2path) |
234 | } | |
235 | ||
c34b1796 AL |
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<_>>(); | |
1a4d82fc JJ |
239 | p.push(0); |
240 | ||
92a42be0 SL |
241 | cvt(unsafe { |
242 | c::SetCurrentDirectoryW(p.as_ptr()) | |
243 | }).map(|_| ()) | |
1a4d82fc JJ |
244 | } |
245 | ||
92a42be0 | 246 | pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> { |
54a0048b | 247 | let k = to_u16s(k)?; |
92a42be0 SL |
248 | let res = super::fill_utf16_buf(|buf, sz| unsafe { |
249 | c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) | |
85aaf69f SL |
250 | }, |buf| { |
251 | OsStringExt::from_wide(buf) | |
92a42be0 SL |
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 | } | |
85aaf69f SL |
263 | } |
264 | ||
92a42be0 | 265 | pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { |
54a0048b SL |
266 | let k = to_u16s(k)?; |
267 | let v = to_u16s(v)?; | |
85aaf69f | 268 | |
92a42be0 SL |
269 | cvt(unsafe { |
270 | c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) | |
271 | }).map(|_| ()) | |
85aaf69f | 272 | } |
1a4d82fc | 273 | |
92a42be0 | 274 | pub fn unsetenv(n: &OsStr) -> io::Result<()> { |
54a0048b | 275 | let v = to_u16s(n)?; |
92a42be0 SL |
276 | cvt(unsafe { |
277 | c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) | |
278 | }).map(|_| ()) | |
85aaf69f SL |
279 | } |
280 | ||
c34b1796 | 281 | pub fn temp_dir() -> PathBuf { |
9346a6ac | 282 | super::fill_utf16_buf(|buf, sz| unsafe { |
85aaf69f SL |
283 | c::GetTempPathW(sz, buf) |
284 | }, super::os2path).unwrap() | |
285 | } | |
286 | ||
c34b1796 | 287 | pub fn home_dir() -> Option<PathBuf> { |
532ac7d7 XL |
288 | crate::env::var_os("HOME").or_else(|| { |
289 | crate::env::var_os("USERPROFILE") | |
c34b1796 | 290 | }).map(PathBuf::from).or_else(|| unsafe { |
85aaf69f SL |
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 | } | |
c34b1796 | 296 | let _handle = Handle::new(token); |
9346a6ac | 297 | super::fill_utf16_buf(|buf, mut sz| { |
85aaf69f | 298 | match c::GetUserProfileDirectoryW(token, buf, &mut sz) { |
7453a54e | 299 | 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0, |
85aaf69f | 300 | 0 => sz, |
7453a54e | 301 | _ => sz - 1, // sz includes the null terminator |
85aaf69f SL |
302 | } |
303 | }, super::os2path).ok() | |
304 | }) | |
305 | } | |
c34b1796 AL |
306 | |
307 | pub fn exit(code: i32) -> ! { | |
92a42be0 | 308 | unsafe { c::ExitProcess(code as c::UINT) } |
c34b1796 | 309 | } |
7cac9316 | 310 | |
abe05a73 XL |
311 | pub fn getpid() -> u32 { |
312 | unsafe { c::GetCurrentProcessId() as u32 } | |
313 | } | |
314 | ||
7cac9316 XL |
315 | #[cfg(test)] |
316 | mod tests { | |
532ac7d7 XL |
317 | use crate::io::Error; |
318 | use crate::sys::c; | |
7cac9316 XL |
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 | } |