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
13 // FIXME: move various extern bindings from here into liblibc or
19 use io::{IoResult, IoError};
21 use libc::{c_int, c_void};
24 use path::BytesContainer;
27 use sys::fs::FileDesc;
30 use libc::types::os::arch::extra::DWORD;
32 const BUF_BYTES : uint = 2048u;
34 /// Return a slice of `v` ending at (and not including) the first NUL
36 pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
37 match v.iter().position(|c| *c == 0) {
38 // don't include the 0
44 pub fn errno() -> uint {
45 use libc::types::os::arch::extra::DWORD;
47 #[link_name = "kernel32"]
49 fn GetLastError() -> DWORD;
53 GetLastError() as uint
57 /// Get a detailed string description for the given error number
58 pub fn error_string(errnum: i32) -> String {
59 use libc::types::os::arch::extra::DWORD;
60 use libc::types::os::arch::extra::LPWSTR;
61 use libc::types::os::arch::extra::LPVOID;
62 use libc::types::os::arch::extra::WCHAR;
64 #[link_name = "kernel32"]
66 fn FormatMessageW(flags: DWORD,
76 static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
77 static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
79 // This value is calculated from the macro
80 // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
81 let langId = 0x0800 as DWORD;
83 let mut buf = [0 as WCHAR; TMPBUF_SZ];
86 let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
87 FORMAT_MESSAGE_IGNORE_INSERTS,
95 // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
97 return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
100 let msg = String::from_utf16(truncate_utf16_at_nul(&buf));
102 Ok(msg) => format!("OS Error {}: {}", errnum, msg),
103 Err(..) => format!("OS Error {} (FormatMessageW() returned \
104 invalid UTF-16)", errnum),
109 pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
110 // Windows pipes work subtly differently than unix pipes, and their
111 // inheritance has to be handled in a different way that I do not
112 // fully understand. Here we explicitly make the pipe non-inheritable,
113 // which means to pass it to a subprocess they need to be duplicated
114 // first, as in std::run.
115 let mut fds = [0; 2];
116 match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
117 (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
119 assert!(fds[0] != -1 && fds[0] != 0);
120 assert!(fds[1] != -1 && fds[1] != 0);
121 Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
123 _ => Err(IoError::last_error()),
127 pub fn fill_utf16_buf_and_decode<F>(mut f: F) -> Option<String> where
128 F: FnMut(*mut u16, DWORD) -> DWORD,
131 let mut n = TMPBUF_SZ as DWORD;
133 let mut done = false;
135 let mut buf: Vec<u16> = repeat(0u16).take(n as uint).collect();
136 let k = f(buf.as_mut_ptr(), n);
137 if k == (0 as DWORD) {
140 libc::GetLastError() ==
141 libc::ERROR_INSUFFICIENT_BUFFER as DWORD {
149 let sub = buf.slice(0, k as uint);
150 // We want to explicitly catch the case when the
151 // closure returned invalid UTF-16, rather than
152 // set `res` to None and continue.
153 let s = String::from_utf16(sub).ok()
154 .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16");
162 pub fn getcwd() -> IoResult<Path> {
164 use libc::GetCurrentDirectoryW;
165 use io::OtherIoError;
167 let mut buf = [0 as u16; BUF_BYTES];
169 if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD {
170 return Err(IoError::last_error());
174 match String::from_utf16(truncate_utf16_at_nul(&buf)) {
175 Ok(ref cwd) => Ok(Path::new(cwd)),
176 Err(..) => Err(IoError {
178 desc: "GetCurrentDirectoryW returned invalid UTF-16",
184 pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
185 use libc::funcs::extra::kernel32::{
186 GetEnvironmentStringsW,
187 FreeEnvironmentStringsW
189 let ch = GetEnvironmentStringsW();
191 panic!("os::env() failure getting env string from OS: {}",
192 os::last_os_error());
194 // Here, we lossily decode the string as UTF16.
196 // The docs suggest that the result should be in Unicode, but
197 // Windows doesn't guarantee it's actually UTF16 -- it doesn't
198 // validate the environment string passed to CreateProcess nor
199 // SetEnvironmentVariable. Yet, it's unlikely that returning a
200 // raw u16 buffer would be of practical use since the result would
201 // be inherently platform-dependent and introduce additional
202 // complexity to this code.
204 // Using the non-Unicode version of GetEnvironmentStrings is even
205 // worse since the result is in an OEM code page. Characters that
206 // can't be encoded in the code page would be turned into question
208 let mut result = Vec::new();
210 while *ch.offset(i) != 0 {
211 let p = &*ch.offset(i);
213 while *(p as *const _).offset(len) != 0 {
216 let p = p as *const u16;
217 let s = slice::from_raw_buf(&p, len as uint);
218 result.push(String::from_utf16_lossy(s).into_bytes());
221 FreeEnvironmentStringsW(ch);
225 pub fn split_paths(unparsed: &[u8]) -> Vec<Path> {
226 // On Windows, the PATH environment variable is semicolon separated. Double
227 // quotes are used as a way of introducing literal semicolons (since
228 // c:\some;dir is a valid Windows path). Double quotes are not themselves
229 // permitted in path names, so there is no way to escape a double quote.
230 // Quoted regions can appear in arbitrary locations, so
232 // c:\foo;c:\som"e;di"r;c:\bar
234 // Should parse as [c:\foo, c:\some;dir, c:\bar].
236 // (The above is based on testing; there is no clear reference available
239 let mut parsed = Vec::new();
240 let mut in_progress = Vec::new();
241 let mut in_quote = false;
243 for b in unparsed.iter() {
245 b';' if !in_quote => {
246 parsed.push(Path::new(in_progress.as_slice()));
247 in_progress.truncate(0)
250 in_quote = !in_quote;
253 in_progress.push(*b);
257 parsed.push(Path::new(in_progress));
261 pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
262 let mut joined = Vec::new();
265 for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() {
266 if i > 0 { joined.push(sep) }
267 if path.contains(&b'"') {
268 return Err("path segment contains `\"`");
269 } else if path.contains(&sep) {
271 joined.push_all(path);
274 joined.push_all(path);
281 pub fn load_self() -> Option<Vec<u8>> {
283 fill_utf16_buf_and_decode(|buf, sz| {
284 libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
285 }).map(|s| s.to_string().into_bytes())
289 pub fn chdir(p: &Path) -> IoResult<()> {
290 let mut p = p.as_str().unwrap().utf16_units().collect::<Vec<u16>>();
294 match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
296 false => Err(IoError::last_error()),
301 pub fn page_size() -> uint {
304 let mut info = mem::zeroed();
305 libc::GetSystemInfo(&mut info);
307 return info.dwPageSize as uint;
313 use super::truncate_utf16_at_nul;
316 fn test_truncate_utf16_at_nul() {
319 assert_eq!(truncate_utf16_at_nul(&v), b);
322 assert_eq!(truncate_utf16_at_nul(&v), b);
325 let b: &[u16] = &[1];
326 assert_eq!(truncate_utf16_at_nul(&v), b);
329 let b: &[u16] = &[1, 2];
330 assert_eq!(truncate_utf16_at_nul(&v), b);
333 let b: &[u16] = &[1, 2, 3];
334 assert_eq!(truncate_utf16_at_nul(&v), b);