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