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
18 use io::{IoResult, IoError};
19 use libc::{c_int, c_void};
22 use path::BytesContainer;
24 use sys::fs::FileDesc;
28 use libc::types::os::arch::extra::DWORD;
30 const BUF_BYTES : uint = 2048u;
32 /// Return a slice of `v` ending at (and not including) the first NUL
34 pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
35 match v.iter().position(|c| *c == 0) {
36 // don't include the 0
42 pub fn errno() -> uint {
43 use libc::types::os::arch::extra::DWORD;
45 #[link_name = "kernel32"]
47 fn GetLastError() -> DWORD;
51 GetLastError() as uint
55 /// Get a detailed string description for the given error number
56 pub fn error_string(errnum: i32) -> String {
57 use libc::types::os::arch::extra::DWORD;
58 use libc::types::os::arch::extra::LPWSTR;
59 use libc::types::os::arch::extra::LPVOID;
60 use libc::types::os::arch::extra::WCHAR;
62 #[link_name = "kernel32"]
64 fn FormatMessageW(flags: DWORD,
74 static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
75 static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
77 // This value is calculated from the macro
78 // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
79 let langId = 0x0800 as DWORD;
81 let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
84 let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
85 FORMAT_MESSAGE_IGNORE_INSERTS,
93 // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
95 return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
98 let msg = String::from_utf16(truncate_utf16_at_nul(&buf));
100 Ok(msg) => format!("OS Error {}: {}", errnum, msg),
101 Err(..) => format!("OS Error {} (FormatMessageW() returned \
102 invalid UTF-16)", errnum),
107 pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
108 // Windows pipes work subtly differently than unix pipes, and their
109 // inheritance has to be handled in a different way that I do not
110 // fully understand. Here we explicitly make the pipe non-inheritable,
111 // which means to pass it to a subprocess they need to be duplicated
112 // first, as in std::run.
113 let mut fds = [0, ..2];
114 match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
115 (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
117 assert!(fds[0] != -1 && fds[0] != 0);
118 assert!(fds[1] != -1 && fds[1] != 0);
119 Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
121 _ => Err(IoError::last_error()),
125 pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD) -> Option<String> {
127 let mut n = TMPBUF_SZ as DWORD;
129 let mut done = false;
131 let mut buf = Vec::from_elem(n as uint, 0u16);
132 let k = f(buf.as_mut_ptr(), n);
133 if k == (0 as DWORD) {
136 libc::GetLastError() ==
137 libc::ERROR_INSUFFICIENT_BUFFER as DWORD {
145 let sub = buf.slice(0, k as uint);
146 // We want to explicitly catch the case when the
147 // closure returned invalid UTF-16, rather than
148 // set `res` to None and continue.
149 let s = String::from_utf16(sub).ok()
150 .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16");
158 pub fn getcwd() -> IoResult<Path> {
160 use libc::GetCurrentDirectoryW;
161 use io::OtherIoError;
163 let mut buf = [0 as u16, ..BUF_BYTES];
165 if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD {
166 return Err(IoError::last_error());
170 match String::from_utf16(truncate_utf16_at_nul(&buf)) {
171 Ok(ref cwd) => Ok(Path::new(cwd)),
172 Err(..) => Err(IoError {
174 desc: "GetCurrentDirectoryW returned invalid UTF-16",
180 pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
181 use libc::funcs::extra::kernel32::{
182 GetEnvironmentStringsW,
183 FreeEnvironmentStringsW
185 let ch = GetEnvironmentStringsW();
187 panic!("os::env() failure getting env string from OS: {}",
188 os::last_os_error());
190 // Here, we lossily decode the string as UTF16.
192 // The docs suggest that the result should be in Unicode, but
193 // Windows doesn't guarantee it's actually UTF16 -- it doesn't
194 // validate the environment string passed to CreateProcess nor
195 // SetEnvironmentVariable. Yet, it's unlikely that returning a
196 // raw u16 buffer would be of practical use since the result would
197 // be inherently platform-dependent and introduce additional
198 // complexity to this code.
200 // Using the non-Unicode version of GetEnvironmentStrings is even
201 // worse since the result is in an OEM code page. Characters that
202 // can't be encoded in the code page would be turned into question
204 let mut result = Vec::new();
206 while *ch.offset(i) != 0 {
207 let p = &*ch.offset(i);
209 while *(p as *const _).offset(len) != 0 {
212 let p = p as *const u16;
213 let s = slice::from_raw_buf(&p, len as uint);
214 result.push(String::from_utf16_lossy(s).into_bytes());
217 FreeEnvironmentStringsW(ch);
221 pub fn split_paths(unparsed: &[u8]) -> Vec<Path> {
222 // On Windows, the PATH environment variable is semicolon separated. Double
223 // quotes are used as a way of introducing literal semicolons (since
224 // c:\some;dir is a valid Windows path). Double quotes are not themselves
225 // permitted in path names, so there is no way to escape a double quote.
226 // Quoted regions can appear in arbitrary locations, so
228 // c:\foo;c:\som"e;di"r;c:\bar
230 // Should parse as [c:\foo, c:\some;dir, c:\bar].
232 // (The above is based on testing; there is no clear reference available
235 let mut parsed = Vec::new();
236 let mut in_progress = Vec::new();
237 let mut in_quote = false;
239 for b in unparsed.iter() {
241 b';' if !in_quote => {
242 parsed.push(Path::new(in_progress.as_slice()));
243 in_progress.truncate(0)
246 in_quote = !in_quote;
249 in_progress.push(*b);
253 parsed.push(Path::new(in_progress));
257 pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
258 let mut joined = Vec::new();
261 for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() {
262 if i > 0 { joined.push(sep) }
263 if path.contains(&b'"') {
264 return Err("path segment contains `\"`");
265 } else if path.contains(&sep) {
267 joined.push_all(path);
270 joined.push_all(path);
277 pub fn load_self() -> Option<Vec<u8>> {
279 fill_utf16_buf_and_decode(|buf, sz| {
280 libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
281 }).map(|s| s.to_string().into_bytes())
285 pub fn chdir(p: &Path) -> IoResult<()> {
286 let mut p = p.as_str().unwrap().utf16_units().collect::<Vec<u16>>();
290 match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
292 false => Err(IoError::last_error()),
297 pub fn page_size() -> uint {
300 let mut info = mem::zeroed();
301 libc::GetSystemInfo(&mut info);
303 return info.dwPageSize as uint;
309 use super::truncate_utf16_at_nul;
312 fn test_truncate_utf16_at_nul() {
315 assert_eq!(truncate_utf16_at_nul(&v), b);
318 assert_eq!(truncate_utf16_at_nul(&v), b);
321 let b: &[u16] = &[1];
322 assert_eq!(truncate_utf16_at_nul(&v), b);
325 let b: &[u16] = &[1, 2];
326 assert_eq!(truncate_utf16_at_nul(&v), b);
329 let b: &[u16] = &[1, 2, 3];
330 assert_eq!(truncate_utf16_at_nul(&v), b);