]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/os.rs
Auto merge of #22541 - Manishearth:rollup, r=Gankro
[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 #![allow(bad_style)]
14
15 use prelude::v1::*;
16 use os::windows::*;
17
18 use error::Error as StdError;
19 use ffi::{OsString, OsStr, AsOsStr};
20 use fmt;
21 use ops::Range;
22 use libc::types::os::arch::extra::LPWCH;
23 use libc::{self, c_int, c_void};
24 use mem;
25 use old_io::{IoError, IoResult};
26 use ptr;
27 use slice;
28 use sys::c;
29 use sys::fs::FileDesc;
30 use sys::handle::Handle as RawHandle;
31
32 use libc::funcs::extra::kernel32::{
33     GetEnvironmentStringsW,
34     FreeEnvironmentStringsW
35 };
36
37 pub fn errno() -> i32 {
38     unsafe { libc::GetLastError() as i32 }
39 }
40
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;
47
48     #[link_name = "kernel32"]
49     extern "system" {
50         fn FormatMessageW(flags: DWORD,
51                           lpSrc: LPVOID,
52                           msgId: DWORD,
53                           langId: DWORD,
54                           buf: LPWSTR,
55                           nsize: DWORD,
56                           args: *const c_void)
57                           -> DWORD;
58     }
59
60     static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
61     static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
62
63     // This value is calculated from the macro
64     // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
65     let langId = 0x0800 as DWORD;
66
67     let mut buf = [0 as WCHAR; 2048];
68
69     unsafe {
70         let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
71                                  FORMAT_MESSAGE_IGNORE_INSERTS,
72                                  ptr::null_mut(),
73                                  errnum as DWORD,
74                                  langId,
75                                  buf.as_mut_ptr(),
76                                  buf.len() as DWORD,
77                                  ptr::null());
78         if res == 0 {
79             // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
80             let fm_err = errno();
81             return format!("OS Error {} (FormatMessageW() returned error {})",
82                            errnum, fm_err);
83         }
84
85         let b = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
86         let msg = String::from_utf16(&buf[..b]);
87         match msg {
88             Ok(msg) => msg,
89             Err(..) => format!("OS Error {} (FormatMessageW() returned \
90                                 invalid UTF-16)", errnum),
91         }
92     }
93 }
94
95 pub struct Env {
96     base: LPWCH,
97     cur: LPWCH,
98 }
99
100 impl Iterator for Env {
101     type Item = (OsString, OsString);
102
103     fn next(&mut self) -> Option<(OsString, OsString)> {
104         unsafe {
105             if *self.cur == 0 { return None }
106             let p = &*self.cur;
107             let mut len = 0;
108             while *(p as *const _).offset(len) != 0 {
109                 len += 1;
110             }
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);
114
115             let (k, v) = match s.iter().position(|&b| b == '=' as u16) {
116                 Some(n) => (&s[..n], &s[n+1..]),
117                 None => (s, &[][..]),
118             };
119             Some((OsStringExt::from_wide(k), OsStringExt::from_wide(v)))
120         }
121     }
122 }
123
124 impl Drop for Env {
125     fn drop(&mut self) {
126         unsafe { FreeEnvironmentStringsW(self.base); }
127     }
128 }
129
130 pub fn env() -> Env {
131     unsafe {
132         let ch = GetEnvironmentStringsW();
133         if ch as usize == 0 {
134             panic!("failure getting env string from OS: {}",
135                    IoError::last_error());
136         }
137         Env { base: ch, cur: ch }
138     }
139 }
140
141 pub struct SplitPaths<'a> {
142     data: EncodeWide<'a>,
143     must_yield: bool,
144 }
145
146 pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
147     SplitPaths {
148         data: unparsed.encode_wide(),
149         must_yield: true,
150     }
151 }
152
153 impl<'a> Iterator for SplitPaths<'a> {
154     type Item = Path;
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
161         //
162         //   c:\foo;c:\som"e;di"r;c:\bar
163         //
164         // Should parse as [c:\foo, c:\some;dir, c:\bar].
165         //
166         // (The above is based on testing; there is no clear reference available
167         // for the grammar.)
168
169
170         let must_yield = self.must_yield;
171         self.must_yield = false;
172
173         let mut in_progress = Vec::new();
174         let mut in_quote = false;
175         for b in self.data.by_ref() {
176             if b == '"' as u16 {
177                 in_quote = !in_quote;
178             } else if b == ';' as u16 && !in_quote {
179                 self.must_yield = true;
180                 break
181             } else {
182                 in_progress.push(b)
183             }
184         }
185
186         if !must_yield && in_progress.is_empty() {
187             None
188         } else {
189             Some(super::os2path(&in_progress[..]))
190         }
191     }
192 }
193
194 #[derive(Debug)]
195 pub struct JoinPathsError;
196
197 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
198     where I: Iterator<Item=T>, T: AsOsStr
199 {
200     let mut joined = Vec::new();
201     let sep = b';' as u16;
202
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);
213         } else {
214             joined.push_all(&v[..]);
215         }
216     }
217
218     Ok(OsStringExt::from_wide(&joined[..]))
219 }
220
221 impl fmt::Display for JoinPathsError {
222     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223         "path segment contains `\"`".fmt(f)
224     }
225 }
226
227 impl StdError for JoinPathsError {
228     fn description(&self) -> &str { "failed to join paths" }
229 }
230
231 pub fn current_exe() -> IoResult<Path> {
232     super::fill_utf16_buf(|buf, sz| unsafe {
233         libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
234     }, super::os2path)
235 }
236
237 pub fn getcwd() -> IoResult<Path> {
238     super::fill_utf16_buf(|buf, sz| unsafe {
239         libc::GetCurrentDirectoryW(sz, buf)
240     }, super::os2path)
241 }
242
243 pub fn chdir(p: &Path) -> IoResult<()> {
244     let mut p = p.as_os_str().encode_wide().collect::<Vec<_>>();
245     p.push(0);
246
247     unsafe {
248         match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
249             true => Ok(()),
250             false => Err(IoError::last_error()),
251         }
252     }
253 }
254
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)
259     }, |buf| {
260         OsStringExt::from_wide(buf)
261     }).ok()
262 }
263
264 pub fn setenv(k: &OsStr, v: &OsStr) {
265     let k = super::to_utf16_os(k);
266     let v = super::to_utf16_os(v);
267
268     unsafe {
269         if libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) == 0 {
270             panic!("failed to set env: {}", IoError::last_error());
271         }
272     }
273 }
274
275 pub fn unsetenv(n: &OsStr) {
276     let v = super::to_utf16_os(n);
277     unsafe {
278         if libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) == 0 {
279             panic!("failed to unset env: {}", IoError::last_error());
280         }
281     }
282 }
283
284 pub struct Args {
285     range: Range<isize>,
286     cur: *mut *mut u16,
287 }
288
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);
294             let mut len = 0;
295             while *ptr.offset(len) != 0 { len += 1; }
296
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)
301         })
302     }
303     fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
304 }
305
306 impl ExactSizeIterator for Args {
307     fn len(&self) -> usize { self.range.len() }
308 }
309
310 impl Drop for Args {
311     fn drop(&mut self) {
312         unsafe { c::LocalFree(self.cur as *mut c_void); }
313     }
314 }
315
316 pub fn args() -> Args {
317     unsafe {
318         let mut nArgs: c_int = 0;
319         let lpCmdLine = c::GetCommandLineW();
320         let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);
321
322         Args { cur: szArgList, range: 0..(nArgs as isize) }
323     }
324 }
325
326 pub fn page_size() -> usize {
327     unsafe {
328         let mut info = mem::zeroed();
329         libc::GetSystemInfo(&mut info);
330         return info.dwPageSize as usize;
331     }
332 }
333
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) {
343         0 => {
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)))
347         }
348         _ => Err(IoError::last_error()),
349     }
350 }
351
352 pub fn temp_dir() -> Path {
353     super::fill_utf16_buf(|buf, sz| unsafe {
354         c::GetTempPathW(sz, buf)
355     }, super::os2path).unwrap()
356 }
357
358 pub fn home_dir() -> Option<Path> {
359     getenv("HOME".as_os_str()).or_else(|| {
360         getenv("USERPROFILE".as_os_str())
361     }).map(|os| {
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 {
368             return None
369         }
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,
374                 0 => sz,
375                 n => n as libc::DWORD,
376             }
377         }, super::os2path).ok()
378     })
379 }