]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/windows/os.rs
New upstream version 1.35.0+dfsg1
[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;
8use crate::ffi::{OsString, OsStr};
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};
16use crate::sys::handle::Handle;
1a4d82fc 17
92a42be0 18use super::to_u16s;
1a4d82fc 19
85aaf69f 20pub 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 25pub 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 79pub struct Env {
92a42be0
SL
80 base: c::LPWCH,
81 cur: c::LPWCH,
1a4d82fc
JJ
82}
83
85aaf69f
SL
84impl 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
117impl Drop for Env {
118 fn drop(&mut self) {
92a42be0 119 unsafe { c::FreeEnvironmentStringsW(self.base); }
85aaf69f
SL
120 }
121}
1a4d82fc 122
85aaf69f 123pub 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
134pub struct SplitPaths<'a> {
135 data: EncodeWide<'a>,
136 must_yield: bool,
1a4d82fc
JJ
137}
138
532ac7d7 139pub 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 146impl<'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)]
188pub struct JoinPathsError;
189
190pub 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 214impl 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
220impl StdError for JoinPathsError {
221 fn description(&self) -> &str { "failed to join paths" }
222}
223
c34b1796 224pub 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 230pub 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
236pub 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 246pub 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 265pub 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 274pub 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 281pub 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 287pub 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
307pub fn exit(code: i32) -> ! {
92a42be0 308 unsafe { c::ExitProcess(code as c::UINT) }
c34b1796 309}
7cac9316 310
abe05a73
XL
311pub fn getpid() -> u32 {
312 unsafe { c::GetCurrentProcessId() as u32 }
313}
314
7cac9316
XL
315#[cfg(test)]
316mod 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}