]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/windows/os.rs
New upstream version 1.42.0+dfsg0+pve1
[rustc.git] / src / libstd / sys / windows / os.rs
CommitLineData
9fa01778 1//! Implementation of `std::os` functionality for Windows.
1a4d82fc 2
b7449926 3#![allow(nonstandard_style)]
1a4d82fc 4
532ac7d7
XL
5use crate::os::windows::prelude::*;
6
7use crate::error::Error as StdError;
d9bb1a4e 8use crate::ffi::{OsStr, OsString};
532ac7d7
XL
9use crate::fmt;
10use crate::io;
11use crate::os::windows::ffi::EncodeWide;
12use crate::path::{self, PathBuf};
13use crate::ptr;
14use crate::slice;
15use crate::sys::{c, cvt};
1a4d82fc 16
92a42be0 17use super::to_u16s;
1a4d82fc 18
85aaf69f 19pub 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 24pub 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 83pub struct Env {
92a42be0
SL
84 base: c::LPWCH,
85 cur: c::LPWCH,
1a4d82fc
JJ
86}
87
85aaf69f
SL
88impl 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
123impl 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 131pub 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
141pub struct SplitPaths<'a> {
142 data: EncodeWide<'a>,
143 must_yield: bool,
1a4d82fc
JJ
144}
145
532ac7d7 146pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
d9bb1a4e 147 SplitPaths { data: unparsed.encode_wide(), must_yield: true }
1a4d82fc
JJ
148}
149
85aaf69f 150impl<'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)]
191pub struct JoinPathsError;
192
193pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
d9bb1a4e
FG
194where
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 221impl 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 227impl 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 234pub 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 241pub 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
245pub 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 253pub 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 271pub 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 278pub 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 283pub 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"))]
288fn 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")]
313fn home_dir_crt() -> Option<PathBuf> {
314 None
315}
316
317pub 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
324pub fn exit(code: i32) -> ! {
92a42be0 325 unsafe { c::ExitProcess(code as c::UINT) }
c34b1796 326}
7cac9316 327
abe05a73
XL
328pub fn getpid() -> u32 {
329 unsafe { c::GetCurrentProcessId() as u32 }
330}
331
7cac9316
XL
332#[cfg(test)]
333mod 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}