1 //! Implementation of `std::os` functionality for Windows.
3 #![allow(nonstandard_style)]
5 use crate::os::windows::prelude::*;
7 use crate::error::Error as StdError;
8 use crate::ffi::{OsString, OsStr};
11 use crate::os::windows::ffi::EncodeWide;
12 use crate::path::{self, PathBuf};
15 use crate::sys::{c, cvt};
16 use crate::sys::handle::Handle;
20 pub fn errno() -> i32 {
21 unsafe { c::GetLastError() as i32 }
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;
30 let mut buf = [0 as c::WCHAR; 2048];
33 let mut module = ptr::null_mut();
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());
45 if module != ptr::null_mut() {
46 errnum ^= c::FACILITY_NT_BIT as i32;
47 flags = c::FORMAT_MESSAGE_FROM_HMODULE;
51 let res = c::FormatMessageW(flags | c::FORMAT_MESSAGE_FROM_SYSTEM |
52 c::FORMAT_MESSAGE_IGNORE_INSERTS,
57 buf.len() as c::DWORD,
58 ptr::null()) as usize;
60 // Sometimes FormatMessageW can fail e.g., system doesn't like langId,
62 return format!("OS Error {} (FormatMessageW() returned error {})",
66 match String::from_utf16(&buf[..res]) {
68 // Trim trailing CRLF inserted by FormatMessageW
69 let len = msg.trim_end().len();
73 Err(..) => format!("OS Error {} (FormatMessageW() returned \
74 invalid UTF-16)", errnum),
84 impl Iterator for Env {
85 type Item = (OsString, OsString);
87 fn next(&mut self) -> Option<(OsString, OsString)> {
90 if *self.cur == 0 { return None }
91 let p = &*self.cur as *const u16;
93 while *p.offset(len) != 0 {
96 let s = slice::from_raw_parts(p, len as usize);
97 self.cur = self.cur.offset(len + 1);
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) {
109 OsStringExt::from_wide(&s[..pos]),
110 OsStringExt::from_wide(&s[pos+1..]),
119 unsafe { c::FreeEnvironmentStringsW(self.base); }
123 pub fn env() -> Env {
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());
130 Env { base: ch, cur: ch }
134 pub struct SplitPaths<'a> {
135 data: EncodeWide<'a>,
139 pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
141 data: unparsed.encode_wide(),
146 impl<'a> Iterator for SplitPaths<'a> {
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
155 // c:\foo;c:\som"e;di"r;c:\bar
157 // Should parse as [c:\foo, c:\some;dir, c:\bar].
159 // (The above is based on testing; there is no clear reference available
163 let must_yield = self.must_yield;
164 self.must_yield = false;
166 let mut in_progress = Vec::new();
167 let mut in_quote = false;
168 for b in self.data.by_ref() {
170 in_quote = !in_quote;
171 } else if b == ';' as u16 && !in_quote {
172 self.must_yield = true;
179 if !must_yield && in_progress.is_empty() {
182 Some(super::os2path(&in_progress))
188 pub struct JoinPathsError;
190 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
191 where I: Iterator<Item=T>, T: AsRef<OsStr>
193 let mut joined = Vec::new();
194 let sep = b';' as u16;
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);
207 joined.extend_from_slice(&v[..]);
211 Ok(OsStringExt::from_wide(&joined[..]))
214 impl fmt::Display for JoinPathsError {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 "path segment contains `\"`".fmt(f)
220 impl StdError for JoinPathsError {
221 fn description(&self) -> &str { "failed to join paths" }
224 pub fn current_exe() -> io::Result<PathBuf> {
225 super::fill_utf16_buf(|buf, sz| unsafe {
226 c::GetModuleFileNameW(ptr::null_mut(), buf, sz)
230 pub fn getcwd() -> io::Result<PathBuf> {
231 super::fill_utf16_buf(|buf, sz| unsafe {
232 c::GetCurrentDirectoryW(sz, buf)
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<_>>();
242 c::SetCurrentDirectoryW(p.as_ptr())
246 pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
248 let res = super::fill_utf16_buf(|buf, sz| unsafe {
249 c::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
251 OsStringExt::from_wide(buf)
254 Ok(value) => Ok(Some(value)),
256 if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) {
265 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
270 c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())
274 pub fn unsetenv(n: &OsStr) -> io::Result<()> {
277 c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())
281 pub fn temp_dir() -> PathBuf {
282 super::fill_utf16_buf(|buf, sz| unsafe {
283 c::GetTempPathW(sz, buf)
284 }, super::os2path).unwrap()
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 {
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,
301 _ => sz - 1, // sz includes the null terminator
303 }, super::os2path).ok()
307 pub fn exit(code: i32) -> ! {
308 unsafe { c::ExitProcess(code as c::UINT) }
311 pub fn getpid() -> u32 {
312 unsafe { c::GetCurrentProcessId() as u32 }
317 use crate::io::Error;
320 // tests `error_string` above
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"));