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
18 use error::Error as StdError;
19 use ffi::{OsString, OsStr, AsOsStr};
22 use libc::types::os::arch::extra::LPWCH;
23 use libc::{self, c_int, c_void};
25 use old_io::{IoError, IoResult};
29 use sys::fs::FileDesc;
30 use sys::handle::Handle as RawHandle;
32 use libc::funcs::extra::kernel32::{
33 GetEnvironmentStringsW,
34 FreeEnvironmentStringsW
37 pub fn errno() -> i32 {
38 unsafe { libc::GetLastError() as i32 }
41 /// Get a detailed string description for the given error number
42 pub fn error_string(errnum: i32) -> String {
43 use libc::types::os::arch::extra::DWORD;
44 use libc::types::os::arch::extra::LPWSTR;
45 use libc::types::os::arch::extra::LPVOID;
46 use libc::types::os::arch::extra::WCHAR;
48 #[link_name = "kernel32"]
50 fn FormatMessageW(flags: DWORD,
60 static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
61 static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
63 // This value is calculated from the macro
64 // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
65 let langId = 0x0800 as DWORD;
67 let mut buf = [0 as WCHAR; 2048];
70 let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
71 FORMAT_MESSAGE_IGNORE_INSERTS,
79 // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
81 return format!("OS Error {} (FormatMessageW() returned error {})",
85 let b = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
86 let msg = String::from_utf16(&buf[..b]);
89 Err(..) => format!("OS Error {} (FormatMessageW() returned \
90 invalid UTF-16)", errnum),
100 impl Iterator for Env {
101 type Item = (OsString, OsString);
103 fn next(&mut self) -> Option<(OsString, OsString)> {
105 if *self.cur == 0 { return None }
108 while *(p as *const _).offset(len) != 0 {
111 let p = p as *const u16;
112 let s = slice::from_raw_parts(p, len as usize);
113 self.cur = self.cur.offset(len + 1);
115 let (k, v) = match s.iter().position(|&b| b == '=' as u16) {
116 Some(n) => (&s[..n], &s[n+1..]),
117 None => (s, &[][..]),
119 Some((OsStringExt::from_wide(k), OsStringExt::from_wide(v)))
126 unsafe { FreeEnvironmentStringsW(self.base); }
130 pub fn env() -> Env {
132 let ch = GetEnvironmentStringsW();
133 if ch as usize == 0 {
134 panic!("failure getting env string from OS: {}",
135 IoError::last_error());
137 Env { base: ch, cur: ch }
141 pub struct SplitPaths<'a> {
142 data: EncodeWide<'a>,
146 pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
148 data: unparsed.encode_wide(),
153 impl<'a> Iterator for SplitPaths<'a> {
155 fn next(&mut self) -> Option<Path> {
156 // On Windows, the PATH environment variable is semicolon separated.
157 // Double quotes are used as a way of introducing literal semicolons
158 // (since c:\some;dir is a valid Windows path). Double quotes are not
159 // themselves permitted in path names, so there is no way to escape a
160 // double quote. Quoted regions can appear in arbitrary locations, so
162 // c:\foo;c:\som"e;di"r;c:\bar
164 // Should parse as [c:\foo, c:\some;dir, c:\bar].
166 // (The above is based on testing; there is no clear reference available
170 let must_yield = self.must_yield;
171 self.must_yield = false;
173 let mut in_progress = Vec::new();
174 let mut in_quote = false;
175 for b in self.data.by_ref() {
177 in_quote = !in_quote;
178 } else if b == ';' as u16 && !in_quote {
179 self.must_yield = true;
186 if !must_yield && in_progress.is_empty() {
189 Some(super::os2path(&in_progress[..]))
195 pub struct JoinPathsError;
197 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
198 where I: Iterator<Item=T>, T: AsOsStr
200 let mut joined = Vec::new();
201 let sep = b';' as u16;
203 for (i, path) in paths.enumerate() {
204 let path = path.as_os_str();
205 if i > 0 { joined.push(sep) }
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.push_all(&v[..]);
212 joined.push(b'"' as u16);
214 joined.push_all(&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 { "failed to join paths" }
231 pub fn current_exe() -> IoResult<Path> {
232 super::fill_utf16_buf(|buf, sz| unsafe {
233 libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
237 pub fn getcwd() -> IoResult<Path> {
238 super::fill_utf16_buf(|buf, sz| unsafe {
239 libc::GetCurrentDirectoryW(sz, buf)
243 pub fn chdir(p: &Path) -> IoResult<()> {
244 let mut p = p.as_os_str().encode_wide().collect::<Vec<_>>();
248 match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
250 false => Err(IoError::last_error()),
255 pub fn getenv(k: &OsStr) -> Option<OsString> {
256 let k = super::to_utf16_os(k);
257 super::fill_utf16_buf(|buf, sz| unsafe {
258 libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
260 OsStringExt::from_wide(buf)
264 pub fn setenv(k: &OsStr, v: &OsStr) {
265 let k = super::to_utf16_os(k);
266 let v = super::to_utf16_os(v);
269 if libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) == 0 {
270 panic!("failed to set env: {}", IoError::last_error());
275 pub fn unsetenv(n: &OsStr) {
276 let v = super::to_utf16_os(n);
278 if libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) == 0 {
279 panic!("failed to unset env: {}", IoError::last_error());
289 impl Iterator for Args {
290 type Item = OsString;
291 fn next(&mut self) -> Option<OsString> {
292 self.range.next().map(|i| unsafe {
293 let ptr = *self.cur.offset(i);
295 while *ptr.offset(len) != 0 { len += 1; }
297 // Push it onto the list.
298 let ptr = ptr as *const u16;
299 let buf = slice::from_raw_parts(ptr, len as usize);
300 OsStringExt::from_wide(buf)
303 fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
306 impl ExactSizeIterator for Args {
307 fn len(&self) -> usize { self.range.len() }
312 unsafe { c::LocalFree(self.cur as *mut c_void); }
316 pub fn args() -> Args {
318 let mut nArgs: c_int = 0;
319 let lpCmdLine = c::GetCommandLineW();
320 let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);
322 Args { cur: szArgList, range: 0..(nArgs as isize) }
326 pub fn page_size() -> usize {
328 let mut info = mem::zeroed();
329 libc::GetSystemInfo(&mut info);
330 return info.dwPageSize as usize;
334 pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
335 // Windows pipes work subtly differently than unix pipes, and their
336 // inheritance has to be handled in a different way that I do not
337 // fully understand. Here we explicitly make the pipe non-inheritable,
338 // which means to pass it to a subprocess they need to be duplicated
339 // first, as in std::run.
340 let mut fds = [0; 2];
341 match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
342 (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
344 assert!(fds[0] != -1 && fds[0] != 0);
345 assert!(fds[1] != -1 && fds[1] != 0);
346 Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
348 _ => Err(IoError::last_error()),
352 pub fn temp_dir() -> Path {
353 super::fill_utf16_buf(|buf, sz| unsafe {
354 c::GetTempPathW(sz, buf)
355 }, super::os2path).unwrap()
358 pub fn home_dir() -> Option<Path> {
359 getenv("HOME".as_os_str()).or_else(|| {
360 getenv("USERPROFILE".as_os_str())
362 // FIXME: OsString => Path
363 Path::new(os.to_str().unwrap())
364 }).or_else(|| unsafe {
365 let me = c::GetCurrentProcess();
366 let mut token = ptr::null_mut();
367 if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
370 let _handle = RawHandle::new(token);
371 super::fill_utf16_buf(|buf, mut sz| {
372 match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
373 0 if libc::GetLastError() != 0 => 0,
375 n => n as libc::DWORD,
377 }, super::os2path).ok()