]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/os.rs
7e28dd1e259c8c1551e7f73fc97d2daaecc09501
[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 os::windows::prelude::*;
16
17 use error::Error as StdError;
18 use ffi::{OsString, OsStr};
19 use fmt;
20 use io;
21 use os::windows::ffi::EncodeWide;
22 use path::{self, PathBuf};
23 use ptr;
24 use slice;
25 use sys::{c, cvt};
26 use sys::handle::Handle;
27
28 use super::to_u16s;
29
30 pub fn errno() -> i32 {
31     unsafe { c::GetLastError() as i32 }
32 }
33
34 /// Gets a detailed string description for the given error number.
35 pub fn error_string(errnum: i32) -> String {
36     // This value is calculated from the macro
37     // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
38     let langId = 0x0800 as c::DWORD;
39
40     let mut buf = [0 as c::WCHAR; 2048];
41
42     unsafe {
43         let res = c::FormatMessageW(c::FORMAT_MESSAGE_FROM_SYSTEM |
44                                         c::FORMAT_MESSAGE_IGNORE_INSERTS,
45                                     ptr::null_mut(),
46                                     errnum as c::DWORD,
47                                     langId,
48                                     buf.as_mut_ptr(),
49                                     buf.len() as c::DWORD,
50                                     ptr::null()) as usize;
51         if res == 0 {
52             // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
53             let fm_err = errno();
54             return format!("OS Error {} (FormatMessageW() returned error {})",
55                            errnum, fm_err);
56         }
57
58         match String::from_utf16(&buf[..res]) {
59             Ok(mut msg) => {
60                 // Trim trailing CRLF inserted by FormatMessageW
61                 let len = msg.trim_right().len();
62                 msg.truncate(len);
63                 msg
64             },
65             Err(..) => format!("OS Error {} (FormatMessageW() returned \
66                                 invalid UTF-16)", errnum),
67         }
68     }
69 }
70
71 pub struct Env {
72     base: c::LPWCH,
73     cur: c::LPWCH,
74 }
75
76 impl Iterator for Env {
77     type Item = (OsString, OsString);
78
79     fn next(&mut self) -> Option<(OsString, OsString)> {
80         loop {
81             unsafe {
82                 if *self.cur == 0 { return None }
83                 let p = &*self.cur as *const u16;
84                 let mut len = 0;
85                 while *p.offset(len) != 0 {
86                     len += 1;
87                 }
88                 let s = slice::from_raw_parts(p, len as usize);
89                 self.cur = self.cur.offset(len + 1);
90
91                 // Windows allows environment variables to start with an equals
92                 // symbol (in any other position, this is the separator between
93                 // variable name and value). Since`s` has at least length 1 at
94                 // this point (because the empty string terminates the array of
95                 // environment variables), we can safely slice.
96                 let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
97                     Some(p) => p,
98                     None => continue,
99                 };
100                 return Some((
101                     OsStringExt::from_wide(&s[..pos]),
102                     OsStringExt::from_wide(&s[pos+1..]),
103                 ))
104             }
105         }
106     }
107 }
108
109 impl Drop for Env {
110     fn drop(&mut self) {
111         unsafe { c::FreeEnvironmentStringsW(self.base); }
112     }
113 }
114
115 pub fn env() -> Env {
116     unsafe {
117         let ch = c::GetEnvironmentStringsW();
118         if ch as usize == 0 {
119             panic!("failure getting env string from OS: {}",
120                    io::Error::last_os_error());
121         }
122         Env { base: ch, cur: ch }
123     }
124 }
125
126 pub struct SplitPaths<'a> {
127     data: EncodeWide<'a>,
128     must_yield: bool,
129 }
130
131 pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
132     SplitPaths {
133         data: unparsed.encode_wide(),
134         must_yield: true,
135     }
136 }
137
138 impl<'a> Iterator for SplitPaths<'a> {
139     type Item = PathBuf;
140     fn next(&mut self) -> Option<PathBuf> {
141         // On Windows, the PATH environment variable is semicolon separated.
142         // Double quotes are used as a way of introducing literal semicolons
143         // (since c:\some;dir is a valid Windows path). Double quotes are not
144         // themselves permitted in path names, so there is no way to escape a
145         // double quote.  Quoted regions can appear in arbitrary locations, so
146         //
147         //   c:\foo;c:\som"e;di"r;c:\bar
148         //
149         // Should parse as [c:\foo, c:\some;dir, c:\bar].
150         //
151         // (The above is based on testing; there is no clear reference available
152         // for the grammar.)
153
154
155         let must_yield = self.must_yield;
156         self.must_yield = false;
157
158         let mut in_progress = Vec::new();
159         let mut in_quote = false;
160         for b in self.data.by_ref() {
161             if b == '"' as u16 {
162                 in_quote = !in_quote;
163             } else if b == ';' as u16 && !in_quote {
164                 self.must_yield = true;
165                 break
166             } else {
167                 in_progress.push(b)
168             }
169         }
170
171         if !must_yield && in_progress.is_empty() {
172             None
173         } else {
174             Some(super::os2path(&in_progress))
175         }
176     }
177 }
178
179 #[derive(Debug)]
180 pub struct JoinPathsError;
181
182 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
183     where I: Iterator<Item=T>, T: AsRef<OsStr>
184 {
185     let mut joined = Vec::new();
186     let sep = b';' as u16;
187
188     for (i, path) in paths.enumerate() {
189         let path = path.as_ref();
190         if i > 0 { joined.push(sep) }
191         let v = path.encode_wide().collect::<Vec<u16>>();
192         if v.contains(&(b'"' as u16)) {
193             return Err(JoinPathsError)
194         } else if v.contains(&sep) {
195             joined.push(b'"' as u16);
196             joined.extend_from_slice(&v[..]);
197             joined.push(b'"' as u16);
198         } else {
199             joined.extend_from_slice(&v[..]);
200         }
201     }
202
203     Ok(OsStringExt::from_wide(&joined[..]))
204 }
205
206 impl fmt::Display for JoinPathsError {
207     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208         "path segment contains `\"`".fmt(f)
209     }
210 }
211
212 impl StdError for JoinPathsError {
213     fn description(&self) -> &str { "failed to join paths" }
214 }
215
216 pub fn current_exe() -> io::Result<PathBuf> {
217     super::fill_utf16_buf(|buf, sz| unsafe {
218         c::GetModuleFileNameW(ptr::null_mut(), buf, sz)
219     }, super::os2path)
220 }
221
222 pub fn getcwd() -> io::Result<PathBuf> {
223     super::fill_utf16_buf(|buf, sz| unsafe {
224         c::GetCurrentDirectoryW(sz, buf)
225     }, super::os2path)
226 }
227
228 pub fn chdir(p: &path::Path) -> io::Result<()> {
229     let p: &OsStr = p.as_ref();
230     let mut p = p.encode_wide().collect::<Vec<_>>();
231     p.push(0);
232
233     cvt(unsafe {
234         c::SetCurrentDirectoryW(p.as_ptr())
235     }).map(|_| ())
236 }
237
238 pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
239     let k = to_u16s(k)?;
240     let res = super::fill_utf16_buf(|buf, sz| unsafe {
241         c::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
242     }, |buf| {
243         OsStringExt::from_wide(buf)
244     });
245     match res {
246         Ok(value) => Ok(Some(value)),
247         Err(e) => {
248             if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) {
249                 Ok(None)
250             } else {
251                 Err(e)
252             }
253         }
254     }
255 }
256
257 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
258     let k = to_u16s(k)?;
259     let v = to_u16s(v)?;
260
261     cvt(unsafe {
262         c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())
263     }).map(|_| ())
264 }
265
266 pub fn unsetenv(n: &OsStr) -> io::Result<()> {
267     let v = to_u16s(n)?;
268     cvt(unsafe {
269         c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())
270     }).map(|_| ())
271 }
272
273 pub fn temp_dir() -> PathBuf {
274     super::fill_utf16_buf(|buf, sz| unsafe {
275         c::GetTempPathW(sz, buf)
276     }, super::os2path).unwrap()
277 }
278
279 pub fn home_dir() -> Option<PathBuf> {
280     ::env::var_os("HOME").or_else(|| {
281         ::env::var_os("USERPROFILE")
282     }).map(PathBuf::from).or_else(|| unsafe {
283         let me = c::GetCurrentProcess();
284         let mut token = ptr::null_mut();
285         if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
286             return None
287         }
288         let _handle = Handle::new(token);
289         super::fill_utf16_buf(|buf, mut sz| {
290             match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
291                 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0,
292                 0 => sz,
293                 _ => sz - 1, // sz includes the null terminator
294             }
295         }, super::os2path).ok()
296     })
297 }
298
299 pub fn exit(code: i32) -> ! {
300     unsafe { c::ExitProcess(code as c::UINT) }
301 }