1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Implementation of `std::os` functionality for Windows
16 use os
::windows
::prelude
::*;
18 use error
::Error
as StdError
;
19 use ffi
::{OsString, OsStr}
;
22 use libc
::{c_int, c_void}
;
24 use os
::windows
::ffi
::EncodeWide
;
25 use path
::{self, PathBuf}
;
29 use sys
::handle
::Handle
;
33 pub fn errno() -> i32 {
34 unsafe { c::GetLastError() as i32 }
37 /// Gets a detailed string description for the given error number.
38 pub fn error_string(errnum
: i32) -> String
{
39 // This value is calculated from the macro
40 // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
41 let langId
= 0x0800 as c
::DWORD
;
43 let mut buf
= [0 as c
::WCHAR
; 2048];
46 let res
= c
::FormatMessageW(c
::FORMAT_MESSAGE_FROM_SYSTEM
|
47 c
::FORMAT_MESSAGE_IGNORE_INSERTS
,
52 buf
.len() as c
::DWORD
,
53 ptr
::null()) as usize;
55 // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
57 return format
!("OS Error {} (FormatMessageW() returned error {})",
61 match String
::from_utf16(&buf
[..res
]) {
63 // Trim trailing CRLF inserted by FormatMessageW
64 let len
= msg
.trim_right().len();
68 Err(..) => format
!("OS Error {} (FormatMessageW() returned \
69 invalid UTF-16)", errnum
),
79 impl Iterator
for Env
{
80 type Item
= (OsString
, OsString
);
82 fn next(&mut self) -> Option
<(OsString
, OsString
)> {
85 if *self.cur
== 0 { return None }
86 let p
= &*self.cur
as *const u16;
88 while *p
.offset(len
) != 0 {
91 let s
= slice
::from_raw_parts(p
, len
as usize);
92 self.cur
= self.cur
.offset(len
+ 1);
94 // Windows allows environment variables to start with an equals
95 // symbol (in any other position, this is the separator between
96 // variable name and value). Since`s` has at least length 1 at
97 // this point (because the empty string terminates the array of
98 // environment variables), we can safely slice.
99 let pos
= match s
[1..].iter().position(|&u
| u
== b'
='
as u16).map(|p
| p
+ 1) {
104 OsStringExt
::from_wide(&s
[..pos
]),
105 OsStringExt
::from_wide(&s
[pos
+1..]),
114 unsafe { c::FreeEnvironmentStringsW(self.base); }
118 pub fn env() -> Env
{
120 let ch
= c
::GetEnvironmentStringsW();
121 if ch
as usize == 0 {
122 panic
!("failure getting env string from OS: {}",
123 io
::Error
::last_os_error());
125 Env { base: ch, cur: ch }
129 pub struct SplitPaths
<'a
> {
130 data
: EncodeWide
<'a
>,
134 pub fn split_paths(unparsed
: &OsStr
) -> SplitPaths
{
136 data
: unparsed
.encode_wide(),
141 impl<'a
> Iterator
for SplitPaths
<'a
> {
143 fn next(&mut self) -> Option
<PathBuf
> {
144 // On Windows, the PATH environment variable is semicolon separated.
145 // Double quotes are used as a way of introducing literal semicolons
146 // (since c:\some;dir is a valid Windows path). Double quotes are not
147 // themselves permitted in path names, so there is no way to escape a
148 // double quote. Quoted regions can appear in arbitrary locations, so
150 // c:\foo;c:\som"e;di"r;c:\bar
152 // Should parse as [c:\foo, c:\some;dir, c:\bar].
154 // (The above is based on testing; there is no clear reference available
158 let must_yield
= self.must_yield
;
159 self.must_yield
= false;
161 let mut in_progress
= Vec
::new();
162 let mut in_quote
= false;
163 for b
in self.data
.by_ref() {
165 in_quote = !in_quote;
166 } else if b == ';' as u16 && !in_quote {
167 self.must_yield = true;
174 if !must_yield && in_progress.is_empty() {
177 Some(super::os2path(&in_progress))
183 pub struct JoinPathsError;
185 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
186 where I: Iterator<Item=T>, T: AsRef<OsStr>
188 let mut joined = Vec::new();
189 let sep = b';' as u16;
191 for (i, path) in paths.enumerate() {
192 let path = path.as_ref();
193 if i > 0 { joined.push(sep) }
194 let v = path.encode_wide().collect::<Vec<u16>>();
195 if v.contains(&(b'"'
as u16)) {
196 return Err(JoinPathsError
)
197 } else if v
.contains(&sep
) {
198 joined
.push(b'
"' as u16);
199 joined.extend_from_slice(&v[..]);
200 joined.push(b'"'
as u16);
202 joined
.extend_from_slice(&v
[..]);
206 Ok(OsStringExt
::from_wide(&joined
[..]))
209 impl fmt
::Display
for JoinPathsError
{
210 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
211 "path segment contains `\"`".fmt(f
)
215 impl StdError
for JoinPathsError
{
216 fn description(&self) -> &str { "failed to join paths" }
219 pub fn current_exe() -> io
::Result
<PathBuf
> {
220 super::fill_utf16_buf(|buf
, sz
| unsafe {
221 c
::GetModuleFileNameW(ptr
::null_mut(), buf
, sz
)
225 pub fn getcwd() -> io
::Result
<PathBuf
> {
226 super::fill_utf16_buf(|buf
, sz
| unsafe {
227 c
::GetCurrentDirectoryW(sz
, buf
)
231 pub fn chdir(p
: &path
::Path
) -> io
::Result
<()> {
232 let p
: &OsStr
= p
.as_ref();
233 let mut p
= p
.encode_wide().collect
::<Vec
<_
>>();
237 c
::SetCurrentDirectoryW(p
.as_ptr())
241 pub fn getenv(k
: &OsStr
) -> io
::Result
<Option
<OsString
>> {
243 let res
= super::fill_utf16_buf(|buf
, sz
| unsafe {
244 c
::GetEnvironmentVariableW(k
.as_ptr(), buf
, sz
)
246 OsStringExt
::from_wide(buf
)
249 Ok(value
) => Ok(Some(value
)),
251 if e
.raw_os_error() == Some(c
::ERROR_ENVVAR_NOT_FOUND
as i32) {
260 pub fn setenv(k
: &OsStr
, v
: &OsStr
) -> io
::Result
<()> {
265 c
::SetEnvironmentVariableW(k
.as_ptr(), v
.as_ptr())
269 pub fn unsetenv(n
: &OsStr
) -> io
::Result
<()> {
272 c
::SetEnvironmentVariableW(v
.as_ptr(), ptr
::null())
281 impl Iterator
for Args
{
282 type Item
= OsString
;
283 fn next(&mut self) -> Option
<OsString
> {
284 self.range
.next().map(|i
| unsafe {
285 let ptr
= *self.cur
.offset(i
);
287 while *ptr
.offset(len
) != 0 { len += 1; }
289 // Push it onto the list.
290 let ptr
= ptr
as *const u16;
291 let buf
= slice
::from_raw_parts(ptr
, len
as usize);
292 OsStringExt
::from_wide(buf
)
295 fn size_hint(&self) -> (usize, Option
<usize>) { self.range.size_hint() }
298 impl ExactSizeIterator
for Args
{
299 fn len(&self) -> usize { self.range.len() }
304 // self.cur can be null if CommandLineToArgvW previously failed,
305 // but LocalFree ignores NULL pointers
306 unsafe { c::LocalFree(self.cur as *mut c_void); }
310 pub fn args() -> Args
{
312 let mut nArgs
: c_int
= 0;
313 let lpCmdLine
= c
::GetCommandLineW();
314 let szArgList
= c
::CommandLineToArgvW(lpCmdLine
, &mut nArgs
);
316 // szArcList can be NULL if CommandLinToArgvW failed,
317 // but in that case nArgs is 0 so we won't actually
318 // try to read a null pointer
319 Args { cur: szArgList, range: 0..(nArgs as isize) }
323 pub fn temp_dir() -> PathBuf
{
324 super::fill_utf16_buf(|buf
, sz
| unsafe {
325 c
::GetTempPathW(sz
, buf
)
326 }, super::os2path
).unwrap()
329 pub fn home_dir() -> Option
<PathBuf
> {
330 ::env
::var_os("HOME").or_else(|| {
331 ::env
::var_os("USERPROFILE")
332 }).map(PathBuf
::from
).or_else(|| unsafe {
333 let me
= c
::GetCurrentProcess();
334 let mut token
= ptr
::null_mut();
335 if c
::OpenProcessToken(me
, c
::TOKEN_READ
, &mut token
) == 0 {
338 let _handle
= Handle
::new(token
);
339 super::fill_utf16_buf(|buf
, mut sz
| {
340 match c
::GetUserProfileDirectoryW(token
, buf
, &mut sz
) {
341 0 if c
::GetLastError() != c
::ERROR_INSUFFICIENT_BUFFER
=> 0,
343 _
=> sz
- 1, // sz includes the null terminator
345 }, super::os2path
).ok()
349 pub fn exit(code
: i32) -> ! {
350 unsafe { c::ExitProcess(code as c::UINT) }