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
::{OsStr, OsString}
;
11 use crate::os
::windows
::ffi
::EncodeWide
;
12 use crate::path
::{self, PathBuf}
;
15 use crate::sys
::{c, cvt}
;
19 pub fn errno() -> i32 {
20 unsafe { c::GetLastError() as i32 }
23 /// Gets a detailed string description for the given error number.
24 pub fn error_string(mut errnum
: i32) -> String
{
25 // This value is calculated from the macro
26 // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
27 let langId
= 0x0800 as c
::DWORD
;
29 let mut buf
= [0 as c
::WCHAR
; 2048];
32 let mut module
= ptr
::null_mut();
35 // NTSTATUS errors may be encoded as HRESULT, which may returned from
36 // GetLastError. For more information about Windows error codes, see
37 // `[MS-ERREF]`: https://msdn.microsoft.com/en-us/library/cc231198.aspx
38 if (errnum
& c
::FACILITY_NT_BIT
as i32) != 0 {
39 // format according to https://support.microsoft.com/en-us/help/259693
40 const NTDLL_DLL
: &[u16] = &[
41 'N'
as _
, 'T'
as _
, 'D'
as _
, 'L'
as _
, 'L'
as _
, '
.'
as _
, 'D'
as _
, 'L'
as _
,
44 module
= c
::GetModuleHandleW(NTDLL_DLL
.as_ptr());
46 if module
!= ptr
::null_mut() {
47 errnum ^
= c
::FACILITY_NT_BIT
as i32;
48 flags
= c
::FORMAT_MESSAGE_FROM_HMODULE
;
52 let res
= c
::FormatMessageW(
53 flags
| c
::FORMAT_MESSAGE_FROM_SYSTEM
| c
::FORMAT_MESSAGE_IGNORE_INSERTS
,
58 buf
.len() as c
::DWORD
,
62 // Sometimes FormatMessageW can fail e.g., system doesn't like langId,
64 return format
!("OS Error {} (FormatMessageW() returned error {})", errnum
, fm_err
);
67 match String
::from_utf16(&buf
[..res
]) {
69 // Trim trailing CRLF inserted by FormatMessageW
70 let len
= msg
.trim_end().len();
75 "OS Error {} (FormatMessageW() returned \
88 impl Iterator
for Env
{
89 type Item
= (OsString
, OsString
);
91 fn next(&mut self) -> Option
<(OsString
, OsString
)> {
97 let p
= &*self.cur
as *const u16;
99 while *p
.offset(len
) != 0 {
102 let s
= slice
::from_raw_parts(p
, len
as usize);
103 self.cur
= self.cur
.offset(len
+ 1);
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) {
115 OsStringExt
::from_wide(&s
[..pos
]),
116 OsStringExt
::from_wide(&s
[pos
+ 1..]),
126 c
::FreeEnvironmentStringsW(self.base
);
131 pub fn env() -> Env
{
133 let ch
= c
::GetEnvironmentStringsW();
134 if ch
as usize == 0 {
135 panic
!("failure getting env string from OS: {}", io
::Error
::last_os_error());
137 Env { base: ch, cur: ch }
141 pub struct SplitPaths
<'a
> {
142 data
: EncodeWide
<'a
>,
146 pub fn split_paths(unparsed
: &OsStr
) -> SplitPaths
<'_
> {
147 SplitPaths { data: unparsed.encode_wide(), must_yield: true }
150 impl<'a
> Iterator
for SplitPaths
<'a
> {
152 fn next(&mut self) -> Option
<PathBuf
> {
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
159 // c:\foo;c:\som"e;di"r;c:\bar
161 // Should parse as [c:\foo, c:\some;dir, c:\bar].
163 // (The above is based on testing; there is no clear reference available
166 let must_yield
= self.must_yield
;
167 self.must_yield
= false;
169 let mut in_progress
= Vec
::new();
170 let mut in_quote
= false;
171 for b
in self.data
.by_ref() {
173 in_quote = !in_quote;
174 } else if b == ';' as u16 && !in_quote {
175 self.must_yield = true;
182 if !must_yield && in_progress.is_empty() {
185 Some(super::os2path(&in_progress))
191 pub struct JoinPathsError;
193 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
195 I: Iterator<Item = T>,
198 let mut joined = Vec::new();
199 let sep = b';' as u16;
201 for (i, path) in paths.enumerate() {
202 let path = path.as_ref();
206 let v = path.encode_wide().collect::<Vec<u16>>();
207 if v.contains(&(b'"'
as u16)) {
208 return Err(JoinPathsError
);
209 } else if v
.contains(&sep
) {
210 joined
.push(b'
"' as u16);
211 joined.extend_from_slice(&v[..]);
212 joined.push(b'"'
as u16);
214 joined
.extend_from_slice(&v
[..]);
218 Ok(OsStringExt
::from_wide(&joined
[..]))
221 impl fmt
::Display
for JoinPathsError
{
222 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
223 "path segment contains `\"`".fmt(f
)
227 impl StdError
for JoinPathsError
{
228 fn description(&self) -> &str {
229 "failed to join paths"
233 pub fn current_exe() -> io
::Result
<PathBuf
> {
234 super::fill_utf16_buf(
235 |buf
, sz
| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) }
,
240 pub fn getcwd() -> io
::Result
<PathBuf
> {
241 super::fill_utf16_buf(|buf
, sz
| unsafe { c::GetCurrentDirectoryW(sz, buf) }
, super::os2path
)
244 pub fn chdir(p
: &path
::Path
) -> io
::Result
<()> {
245 let p
: &OsStr
= p
.as_ref();
246 let mut p
= p
.encode_wide().collect
::<Vec
<_
>>();
249 cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }
).map(|_
| ())
252 pub fn getenv(k
: &OsStr
) -> io
::Result
<Option
<OsString
>> {
254 let res
= super::fill_utf16_buf(
255 |buf
, sz
| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }
,
256 |buf
| OsStringExt
::from_wide(buf
),
259 Ok(value
) => Ok(Some(value
)),
261 if e
.raw_os_error() == Some(c
::ERROR_ENVVAR_NOT_FOUND
as i32) {
270 pub fn setenv(k
: &OsStr
, v
: &OsStr
) -> io
::Result
<()> {
274 cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }
).map(|_
| ())
277 pub fn unsetenv(n
: &OsStr
) -> io
::Result
<()> {
279 cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }
).map(|_
| ())
282 pub fn temp_dir() -> PathBuf
{
283 super::fill_utf16_buf(|buf
, sz
| unsafe { c::GetTempPathW(sz, buf) }
, super::os2path
).unwrap()
286 #[cfg(not(target_vendor = "uwp"))]
287 fn home_dir_crt() -> Option
<PathBuf
> {
289 use crate::sys
::handle
::Handle
;
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(
299 match c
::GetUserProfileDirectoryW(token
, buf
, &mut sz
) {
300 0 if c
::GetLastError() != c
::ERROR_INSUFFICIENT_BUFFER
=> 0,
302 _
=> sz
- 1, // sz includes the null terminator
311 #[cfg(target_vendor = "uwp")]
312 fn home_dir_crt() -> Option
<PathBuf
> {
316 pub fn home_dir() -> Option
<PathBuf
> {
317 crate::env
::var_os("HOME")
318 .or_else(|| crate::env
::var_os("USERPROFILE"))
320 .or_else(|| home_dir_crt())
323 pub fn exit(code
: i32) -> ! {
324 unsafe { c::ExitProcess(code as c::UINT) }
327 pub fn getpid() -> u32 {
328 unsafe { c::GetCurrentProcessId() as u32 }
333 use crate::io
::Error
;
336 // tests `error_string` above
338 fn ntstatus_error() {
339 const STATUS_UNSUCCESSFUL
: u32 = 0xc000_0001;
341 !Error
::from_raw_os_error((STATUS_UNSUCCESSFUL
| c
::FACILITY_NT_BIT
) as _
)
343 .contains("FormatMessageW() returned error")