]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/os.rs
Kill RacyCell in favor of marking SyncSender explicitly Send.
[rust.git] / src / libstd / sys / windows / os.rs
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.
4 //
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.
10
11 //! Implementation of `std::os` functionality for Windows
12
13 // FIXME: move various extern bindings from here into liblibc or
14 // something similar
15
16 use prelude::v1::*;
17
18 use fmt;
19 use io::{IoResult, IoError};
20 use iter::repeat;
21 use libc::{c_int, c_void};
22 use libc;
23 use os;
24 use path::BytesContainer;
25 use ptr;
26 use slice;
27 use sys::fs::FileDesc;
28
29 use os::TMPBUF_SZ;
30 use libc::types::os::arch::extra::DWORD;
31
32 const BUF_BYTES : uint = 2048u;
33
34 /// Return a slice of `v` ending at (and not including) the first NUL
35 /// (0).
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
39         Some(i) => &v[..i],
40         None => v
41     }
42 }
43
44 pub fn errno() -> uint {
45     use libc::types::os::arch::extra::DWORD;
46
47     #[link_name = "kernel32"]
48     extern "system" {
49         fn GetLastError() -> DWORD;
50     }
51
52     unsafe {
53         GetLastError() as uint
54     }
55 }
56
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;
63
64     #[link_name = "kernel32"]
65     extern "system" {
66         fn FormatMessageW(flags: DWORD,
67                           lpSrc: LPVOID,
68                           msgId: DWORD,
69                           langId: DWORD,
70                           buf: LPWSTR,
71                           nsize: DWORD,
72                           args: *const c_void)
73                           -> DWORD;
74     }
75
76     static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
77     static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
78
79     // This value is calculated from the macro
80     // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
81     let langId = 0x0800 as DWORD;
82
83     let mut buf = [0 as WCHAR; TMPBUF_SZ];
84
85     unsafe {
86         let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
87                                  FORMAT_MESSAGE_IGNORE_INSERTS,
88                                  ptr::null_mut(),
89                                  errnum as DWORD,
90                                  langId,
91                                  buf.as_mut_ptr(),
92                                  buf.len() as DWORD,
93                                  ptr::null());
94         if res == 0 {
95             // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
96             let fm_err = errno();
97             return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
98         }
99
100         let msg = String::from_utf16(truncate_utf16_at_nul(&buf));
101         match msg {
102             Ok(msg) => format!("OS Error {}: {}", errnum, msg),
103             Err(..) => format!("OS Error {} (FormatMessageW() returned \
104                                 invalid UTF-16)", errnum),
105         }
106     }
107 }
108
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) {
118         0 => {
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)))
122         }
123         _ => Err(IoError::last_error()),
124     }
125 }
126
127 pub fn fill_utf16_buf_and_decode<F>(mut f: F) -> Option<String> where
128     F: FnMut(*mut u16, DWORD) -> DWORD,
129 {
130     unsafe {
131         let mut n = TMPBUF_SZ as DWORD;
132         let mut res = None;
133         let mut done = false;
134         while !done {
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) {
138                 done = true;
139             } else if k == n &&
140                       libc::GetLastError() ==
141                       libc::ERROR_INSUFFICIENT_BUFFER as DWORD {
142                 n *= 2 as DWORD;
143             } else if k >= n {
144                 n = k;
145             } else {
146                 done = true;
147             }
148             if k != 0 && done {
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");
155                 res = Some(s)
156             }
157         }
158         return res;
159     }
160 }
161
162 pub fn getcwd() -> IoResult<Path> {
163     use libc::DWORD;
164     use libc::GetCurrentDirectoryW;
165     use io::OtherIoError;
166
167     let mut buf = [0 as u16; BUF_BYTES];
168     unsafe {
169         if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD {
170             return Err(IoError::last_error());
171         }
172     }
173
174     match String::from_utf16(truncate_utf16_at_nul(&buf)) {
175         Ok(ref cwd) => Ok(Path::new(cwd)),
176         Err(..) => Err(IoError {
177             kind: OtherIoError,
178             desc: "GetCurrentDirectoryW returned invalid UTF-16",
179             detail: None,
180         }),
181     }
182 }
183
184 pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
185     use libc::funcs::extra::kernel32::{
186         GetEnvironmentStringsW,
187         FreeEnvironmentStringsW
188     };
189     let ch = GetEnvironmentStringsW();
190     if ch as uint == 0 {
191         panic!("os::env() failure getting env string from OS: {}",
192                os::last_os_error());
193     }
194     // Here, we lossily decode the string as UTF16.
195     //
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.
203     //
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
207     // marks.
208     let mut result = Vec::new();
209     let mut i = 0;
210     while *ch.offset(i) != 0 {
211         let p = &*ch.offset(i);
212         let mut len = 0;
213         while *(p as *const _).offset(len) != 0 {
214             len += 1;
215         }
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());
219         i += len as int + 1;
220     }
221     FreeEnvironmentStringsW(ch);
222     result
223 }
224
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
231     //
232     //   c:\foo;c:\som"e;di"r;c:\bar
233     //
234     // Should parse as [c:\foo, c:\some;dir, c:\bar].
235     //
236     // (The above is based on testing; there is no clear reference available
237     // for the grammar.)
238
239     let mut parsed = Vec::new();
240     let mut in_progress = Vec::new();
241     let mut in_quote = false;
242
243     for b in unparsed.iter() {
244         match *b {
245             b';' if !in_quote => {
246                 parsed.push(Path::new(in_progress.as_slice()));
247                 in_progress.truncate(0)
248             }
249             b'"' => {
250                 in_quote = !in_quote;
251             }
252             _  => {
253                 in_progress.push(*b);
254             }
255         }
256     }
257     parsed.push(Path::new(in_progress));
258     parsed
259 }
260
261 pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
262     let mut joined = Vec::new();
263     let sep = b';';
264
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) {
270             joined.push(b'"');
271             joined.push_all(path);
272             joined.push(b'"');
273         } else {
274             joined.push_all(path);
275         }
276     }
277
278     Ok(joined)
279 }
280
281 pub fn load_self() -> Option<Vec<u8>> {
282     unsafe {
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())
286     }
287 }
288
289 pub fn chdir(p: &Path) -> IoResult<()> {
290     let mut p = p.as_str().unwrap().utf16_units().collect::<Vec<u16>>();
291     p.push(0);
292
293     unsafe {
294         match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
295             true => Ok(()),
296             false => Err(IoError::last_error()),
297         }
298     }
299 }
300
301 pub fn page_size() -> uint {
302     use mem;
303     unsafe {
304         let mut info = mem::zeroed();
305         libc::GetSystemInfo(&mut info);
306
307         return info.dwPageSize as uint;
308     }
309 }
310
311 #[cfg(test)]
312 mod tests {
313     use super::truncate_utf16_at_nul;
314
315     #[test]
316     fn test_truncate_utf16_at_nul() {
317         let v = [];
318         let b: &[u16] = &[];
319         assert_eq!(truncate_utf16_at_nul(&v), b);
320
321         let v = [0, 2, 3];
322         assert_eq!(truncate_utf16_at_nul(&v), b);
323
324         let v = [1, 0, 3];
325         let b: &[u16] = &[1];
326         assert_eq!(truncate_utf16_at_nul(&v), b);
327
328         let v = [1, 2, 0];
329         let b: &[u16] = &[1, 2];
330         assert_eq!(truncate_utf16_at_nul(&v), b);
331
332         let v = [1, 2, 3];
333         let b: &[u16] = &[1, 2, 3];
334         assert_eq!(truncate_utf16_at_nul(&v), b);
335     }
336 }