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