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