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};
27 use path::{self, PathBuf};
31 use sys::fs::FileDesc;
32 use sys::handle::Handle as RawHandle;
34 use libc::funcs::extra::kernel32::{
35 GetEnvironmentStringsW,
36 FreeEnvironmentStringsW
39 pub fn errno() -> i32 {
40 unsafe { libc::GetLastError() as i32 }
43 /// Get a detailed string description for the given error number
44 pub fn error_string(errnum: i32) -> String {
45 use libc::types::os::arch::extra::DWORD;
46 use libc::types::os::arch::extra::LPWSTR;
47 use libc::types::os::arch::extra::LPVOID;
48 use libc::types::os::arch::extra::WCHAR;
50 #[link_name = "kernel32"]
52 fn FormatMessageW(flags: DWORD,
62 const FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
63 const FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
65 // This value is calculated from the macro
66 // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
67 let langId = 0x0800 as DWORD;
69 let mut buf = [0 as WCHAR; 2048];
72 let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
73 FORMAT_MESSAGE_IGNORE_INSERTS,
81 // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
83 return format!("OS Error {} (FormatMessageW() returned error {})",
87 let b = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
88 let msg = String::from_utf16(&buf[..b]);
91 Err(..) => format!("OS Error {} (FormatMessageW() returned \
92 invalid UTF-16)", errnum),
102 impl Iterator for Env {
103 type Item = (OsString, OsString);
105 fn next(&mut self) -> Option<(OsString, OsString)> {
107 if *self.cur == 0 { return None }
110 while *(p as *const _).offset(len) != 0 {
113 let p = p as *const u16;
114 let s = slice::from_raw_parts(p, len as usize);
115 self.cur = self.cur.offset(len + 1);
117 let (k, v) = match s.iter().position(|&b| b == '=' as u16) {
118 Some(n) => (&s[..n], &s[n+1..]),
119 None => (s, &[][..]),
121 Some((OsStringExt::from_wide(k), OsStringExt::from_wide(v)))
128 unsafe { FreeEnvironmentStringsW(self.base); }
132 pub fn env() -> Env {
134 let ch = GetEnvironmentStringsW();
135 if ch as usize == 0 {
136 panic!("failure getting env string from OS: {}",
137 IoError::last_error());
139 Env { base: ch, cur: ch }
143 pub struct SplitPaths<'a> {
144 data: EncodeWide<'a>,
148 pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
150 data: unparsed.encode_wide(),
155 impl<'a> Iterator for SplitPaths<'a> {
157 fn next(&mut self) -> Option<PathBuf> {
158 // On Windows, the PATH environment variable is semicolon separated.
159 // Double quotes are used as a way of introducing literal semicolons
160 // (since c:\some;dir is a valid Windows path). Double quotes are not
161 // themselves permitted in path names, so there is no way to escape a
162 // double quote. Quoted regions can appear in arbitrary locations, so
164 // c:\foo;c:\som"e;di"r;c:\bar
166 // Should parse as [c:\foo, c:\some;dir, c:\bar].
168 // (The above is based on testing; there is no clear reference available
172 let must_yield = self.must_yield;
173 self.must_yield = false;
175 let mut in_progress = Vec::new();
176 let mut in_quote = false;
177 for b in self.data.by_ref() {
179 in_quote = !in_quote;
180 } else if b == ';' as u16 && !in_quote {
181 self.must_yield = true;
188 if !must_yield && in_progress.is_empty() {
191 Some(super::os2path(&in_progress))
197 pub struct JoinPathsError;
199 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
200 where I: Iterator<Item=T>, T: AsOsStr
202 let mut joined = Vec::new();
203 let sep = b';' as u16;
205 for (i, path) in paths.enumerate() {
206 let path = path.as_os_str();
207 if i > 0 { joined.push(sep) }
208 let v = path.encode_wide().collect::<Vec<u16>>();
209 if v.contains(&(b'"' as u16)) {
210 return Err(JoinPathsError)
211 } else if v.contains(&sep) {
212 joined.push(b'"' as u16);
213 joined.push_all(&v[..]);
214 joined.push(b'"' as u16);
216 joined.push_all(&v[..]);
220 Ok(OsStringExt::from_wide(&joined[..]))
223 impl fmt::Display for JoinPathsError {
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 "path segment contains `\"`".fmt(f)
229 impl StdError for JoinPathsError {
230 fn description(&self) -> &str { "failed to join paths" }
233 pub fn current_exe() -> io::Result<PathBuf> {
234 super::fill_utf16_buf_new(|buf, sz| unsafe {
235 libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
239 pub fn getcwd() -> io::Result<PathBuf> {
240 super::fill_utf16_buf_new(|buf, sz| unsafe {
241 libc::GetCurrentDirectoryW(sz, buf)
245 pub fn chdir(p: &path::Path) -> io::Result<()> {
246 let mut p = p.as_os_str().encode_wide().collect::<Vec<_>>();
250 match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
252 false => Err(io::Error::last_os_error()),
257 pub fn getenv(k: &OsStr) -> Option<OsString> {
258 let k = super::to_utf16_os(k);
259 super::fill_utf16_buf_new(|buf, sz| unsafe {
260 libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
262 OsStringExt::from_wide(buf)
266 pub fn setenv(k: &OsStr, v: &OsStr) {
267 let k = super::to_utf16_os(k);
268 let v = super::to_utf16_os(v);
271 if libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) == 0 {
272 panic!("failed to set env: {}", IoError::last_error());
277 pub fn unsetenv(n: &OsStr) {
278 let v = super::to_utf16_os(n);
280 if libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) == 0 {
281 panic!("failed to unset env: {}", IoError::last_error());
291 impl Iterator for Args {
292 type Item = OsString;
293 fn next(&mut self) -> Option<OsString> {
294 self.range.next().map(|i| unsafe {
295 let ptr = *self.cur.offset(i);
297 while *ptr.offset(len) != 0 { len += 1; }
299 // Push it onto the list.
300 let ptr = ptr as *const u16;
301 let buf = slice::from_raw_parts(ptr, len as usize);
302 OsStringExt::from_wide(buf)
305 fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
308 impl ExactSizeIterator for Args {
309 fn len(&self) -> usize { self.range.len() }
314 unsafe { c::LocalFree(self.cur as *mut c_void); }
318 pub fn args() -> Args {
320 let mut nArgs: c_int = 0;
321 let lpCmdLine = c::GetCommandLineW();
322 let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);
324 Args { cur: szArgList, range: 0..(nArgs as isize) }
328 pub fn page_size() -> usize {
330 let mut info = mem::zeroed();
331 libc::GetSystemInfo(&mut info);
332 return info.dwPageSize as usize;
336 pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
337 // Windows pipes work subtly differently than unix pipes, and their
338 // inheritance has to be handled in a different way that I do not
339 // fully understand. Here we explicitly make the pipe non-inheritable,
340 // which means to pass it to a subprocess they need to be duplicated
341 // first, as in std::run.
342 let mut fds = [0; 2];
343 match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
344 (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
346 assert!(fds[0] != -1 && fds[0] != 0);
347 assert!(fds[1] != -1 && fds[1] != 0);
348 Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
350 _ => Err(IoError::last_error()),
354 pub fn temp_dir() -> PathBuf {
355 super::fill_utf16_buf_new(|buf, sz| unsafe {
356 c::GetTempPathW(sz, buf)
357 }, super::os2path).unwrap()
360 pub fn home_dir() -> Option<PathBuf> {
361 getenv("HOME".as_os_str()).or_else(|| {
362 getenv("USERPROFILE".as_os_str())
364 // FIXME(#22751) should consume `os`
366 }).or_else(|| unsafe {
367 let me = c::GetCurrentProcess();
368 let mut token = ptr::null_mut();
369 if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
372 let _handle = RawHandle::new(token);
373 super::fill_utf16_buf_new(|buf, mut sz| {
374 match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
375 0 if libc::GetLastError() != 0 => 0,
377 n => n as libc::DWORD,
379 }, super::os2path).ok()