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