]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 | ||
85aaf69f | 13 | #![allow(bad_style)] |
1a4d82fc | 14 | |
c34b1796 | 15 | use os::windows::prelude::*; |
1a4d82fc | 16 | |
85aaf69f | 17 | use error::Error as StdError; |
c34b1796 | 18 | use ffi::{OsString, OsStr}; |
1a4d82fc | 19 | use fmt; |
c34b1796 | 20 | use io; |
92a42be0 | 21 | use libc::{c_int, c_void}; |
c34b1796 AL |
22 | use ops::Range; |
23 | use os::windows::ffi::EncodeWide; | |
24 | use path::{self, PathBuf}; | |
1a4d82fc JJ |
25 | use ptr; |
26 | use slice; | |
92a42be0 | 27 | use sys::{c, cvt}; |
c34b1796 | 28 | use sys::handle::Handle; |
1a4d82fc | 29 | |
92a42be0 | 30 | use super::to_u16s; |
1a4d82fc | 31 | |
85aaf69f | 32 | pub fn errno() -> i32 { |
92a42be0 | 33 | unsafe { c::GetLastError() as i32 } |
1a4d82fc JJ |
34 | } |
35 | ||
9346a6ac | 36 | /// Gets a detailed string description for the given error number. |
1a4d82fc | 37 | pub fn error_string(errnum: i32) -> String { |
1a4d82fc JJ |
38 | // This value is calculated from the macro |
39 | // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) | |
92a42be0 | 40 | let langId = 0x0800 as c::DWORD; |
1a4d82fc | 41 | |
92a42be0 | 42 | let mut buf = [0 as c::WCHAR; 2048]; |
1a4d82fc JJ |
43 | |
44 | unsafe { | |
92a42be0 SL |
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; | |
1a4d82fc JJ |
53 | if res == 0 { |
54 | // Sometimes FormatMessageW can fail e.g. system doesn't like langId, | |
55 | let fm_err = errno(); | |
85aaf69f SL |
56 | return format!("OS Error {} (FormatMessageW() returned error {})", |
57 | errnum, fm_err); | |
1a4d82fc JJ |
58 | } |
59 | ||
e9174d1e SL |
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 | }, | |
1a4d82fc JJ |
67 | Err(..) => format!("OS Error {} (FormatMessageW() returned \ |
68 | invalid UTF-16)", errnum), | |
69 | } | |
70 | } | |
71 | } | |
72 | ||
85aaf69f | 73 | pub struct Env { |
92a42be0 SL |
74 | base: c::LPWCH, |
75 | cur: c::LPWCH, | |
1a4d82fc JJ |
76 | } |
77 | ||
85aaf69f SL |
78 | impl Iterator for Env { |
79 | type Item = (OsString, OsString); | |
80 | ||
81 | fn next(&mut self) -> Option<(OsString, OsString)> { | |
92a42be0 SL |
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 | )) | |
1a4d82fc JJ |
106 | } |
107 | } | |
1a4d82fc JJ |
108 | } |
109 | } | |
110 | ||
85aaf69f SL |
111 | impl Drop for Env { |
112 | fn drop(&mut self) { | |
92a42be0 | 113 | unsafe { c::FreeEnvironmentStringsW(self.base); } |
85aaf69f SL |
114 | } |
115 | } | |
1a4d82fc | 116 | |
85aaf69f | 117 | pub fn env() -> Env { |
1a4d82fc | 118 | unsafe { |
92a42be0 | 119 | let ch = c::GetEnvironmentStringsW(); |
85aaf69f SL |
120 | if ch as usize == 0 { |
121 | panic!("failure getting env string from OS: {}", | |
c34b1796 | 122 | io::Error::last_os_error()); |
1a4d82fc | 123 | } |
85aaf69f | 124 | Env { base: ch, cur: ch } |
1a4d82fc | 125 | } |
85aaf69f | 126 | } |
1a4d82fc | 127 | |
85aaf69f SL |
128 | pub struct SplitPaths<'a> { |
129 | data: EncodeWide<'a>, | |
130 | must_yield: bool, | |
1a4d82fc JJ |
131 | } |
132 | ||
85aaf69f SL |
133 | pub fn split_paths(unparsed: &OsStr) -> SplitPaths { |
134 | SplitPaths { | |
135 | data: unparsed.encode_wide(), | |
136 | must_yield: true, | |
1a4d82fc | 137 | } |
1a4d82fc JJ |
138 | } |
139 | ||
85aaf69f | 140 | impl<'a> Iterator for SplitPaths<'a> { |
c34b1796 AL |
141 | type Item = PathBuf; |
142 | fn next(&mut self) -> Option<PathBuf> { | |
85aaf69f SL |
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 { | |
1a4d82fc | 164 | in_quote = !in_quote; |
85aaf69f SL |
165 | } else if b == ';' as u16 && !in_quote { |
166 | self.must_yield = true; | |
167 | break | |
168 | } else { | |
169 | in_progress.push(b) | |
1a4d82fc | 170 | } |
85aaf69f SL |
171 | } |
172 | ||
173 | if !must_yield && in_progress.is_empty() { | |
174 | None | |
175 | } else { | |
c34b1796 | 176 | Some(super::os2path(&in_progress)) |
1a4d82fc JJ |
177 | } |
178 | } | |
1a4d82fc JJ |
179 | } |
180 | ||
85aaf69f SL |
181 | #[derive(Debug)] |
182 | pub struct JoinPathsError; | |
183 | ||
184 | pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> | |
c34b1796 | 185 | where I: Iterator<Item=T>, T: AsRef<OsStr> |
85aaf69f | 186 | { |
1a4d82fc | 187 | let mut joined = Vec::new(); |
85aaf69f | 188 | let sep = b';' as u16; |
1a4d82fc | 189 | |
85aaf69f | 190 | for (i, path) in paths.enumerate() { |
c34b1796 | 191 | let path = path.as_ref(); |
1a4d82fc | 192 | if i > 0 { joined.push(sep) } |
85aaf69f SL |
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); | |
92a42be0 | 198 | joined.extend_from_slice(&v[..]); |
85aaf69f | 199 | joined.push(b'"' as u16); |
1a4d82fc | 200 | } else { |
92a42be0 | 201 | joined.extend_from_slice(&v[..]); |
1a4d82fc JJ |
202 | } |
203 | } | |
204 | ||
85aaf69f | 205 | Ok(OsStringExt::from_wide(&joined[..])) |
1a4d82fc JJ |
206 | } |
207 | ||
85aaf69f SL |
208 | impl fmt::Display for JoinPathsError { |
209 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
210 | "path segment contains `\"`".fmt(f) | |
1a4d82fc JJ |
211 | } |
212 | } | |
213 | ||
85aaf69f SL |
214 | impl StdError for JoinPathsError { |
215 | fn description(&self) -> &str { "failed to join paths" } | |
216 | } | |
217 | ||
c34b1796 | 218 | pub fn current_exe() -> io::Result<PathBuf> { |
9346a6ac | 219 | super::fill_utf16_buf(|buf, sz| unsafe { |
92a42be0 | 220 | c::GetModuleFileNameW(ptr::null_mut(), buf, sz) |
85aaf69f SL |
221 | }, super::os2path) |
222 | } | |
223 | ||
c34b1796 | 224 | pub fn getcwd() -> io::Result<PathBuf> { |
9346a6ac | 225 | super::fill_utf16_buf(|buf, sz| unsafe { |
92a42be0 | 226 | c::GetCurrentDirectoryW(sz, buf) |
85aaf69f SL |
227 | }, super::os2path) |
228 | } | |
229 | ||
c34b1796 AL |
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<_>>(); | |
1a4d82fc JJ |
233 | p.push(0); |
234 | ||
92a42be0 SL |
235 | cvt(unsafe { |
236 | c::SetCurrentDirectoryW(p.as_ptr()) | |
237 | }).map(|_| ()) | |
1a4d82fc JJ |
238 | } |
239 | ||
92a42be0 | 240 | pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> { |
54a0048b | 241 | let k = to_u16s(k)?; |
92a42be0 SL |
242 | let res = super::fill_utf16_buf(|buf, sz| unsafe { |
243 | c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) | |
85aaf69f SL |
244 | }, |buf| { |
245 | OsStringExt::from_wide(buf) | |
92a42be0 SL |
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 | } | |
85aaf69f SL |
257 | } |
258 | ||
92a42be0 | 259 | pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { |
54a0048b SL |
260 | let k = to_u16s(k)?; |
261 | let v = to_u16s(v)?; | |
85aaf69f | 262 | |
92a42be0 SL |
263 | cvt(unsafe { |
264 | c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) | |
265 | }).map(|_| ()) | |
85aaf69f | 266 | } |
1a4d82fc | 267 | |
92a42be0 | 268 | pub fn unsetenv(n: &OsStr) -> io::Result<()> { |
54a0048b | 269 | let v = to_u16s(n)?; |
92a42be0 SL |
270 | cvt(unsafe { |
271 | c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) | |
272 | }).map(|_| ()) | |
85aaf69f SL |
273 | } |
274 | ||
275 | pub struct Args { | |
276 | range: Range<isize>, | |
277 | cur: *mut *mut u16, | |
278 | } | |
279 | ||
5bcae85e SL |
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 | ||
85aaf69f SL |
290 | impl Iterator for Args { |
291 | type Item = OsString; | |
292 | fn next(&mut self) -> Option<OsString> { | |
5bcae85e | 293 | self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) |
1a4d82fc | 294 | } |
85aaf69f | 295 | fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() } |
1a4d82fc JJ |
296 | } |
297 | ||
5bcae85e SL |
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 | ||
85aaf69f SL |
304 | impl ExactSizeIterator for Args { |
305 | fn len(&self) -> usize { self.range.len() } | |
306 | } | |
1a4d82fc | 307 | |
85aaf69f SL |
308 | impl Drop for Args { |
309 | fn drop(&mut self) { | |
c1a9b12d SL |
310 | // self.cur can be null if CommandLineToArgvW previously failed, |
311 | // but LocalFree ignores NULL pointers | |
85aaf69f SL |
312 | unsafe { c::LocalFree(self.cur as *mut c_void); } |
313 | } | |
314 | } | |
1a4d82fc | 315 | |
85aaf69f SL |
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); | |
1a4d82fc | 321 | |
c1a9b12d SL |
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 | |
85aaf69f SL |
325 | Args { cur: szArgList, range: 0..(nArgs as isize) } |
326 | } | |
327 | } | |
1a4d82fc | 328 | |
c34b1796 | 329 | pub fn temp_dir() -> PathBuf { |
9346a6ac | 330 | super::fill_utf16_buf(|buf, sz| unsafe { |
85aaf69f SL |
331 | c::GetTempPathW(sz, buf) |
332 | }, super::os2path).unwrap() | |
333 | } | |
334 | ||
c34b1796 | 335 | pub fn home_dir() -> Option<PathBuf> { |
92a42be0 SL |
336 | ::env::var_os("HOME").or_else(|| { |
337 | ::env::var_os("USERPROFILE") | |
c34b1796 | 338 | }).map(PathBuf::from).or_else(|| unsafe { |
85aaf69f SL |
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 | } | |
c34b1796 | 344 | let _handle = Handle::new(token); |
9346a6ac | 345 | super::fill_utf16_buf(|buf, mut sz| { |
85aaf69f | 346 | match c::GetUserProfileDirectoryW(token, buf, &mut sz) { |
7453a54e | 347 | 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0, |
85aaf69f | 348 | 0 => sz, |
7453a54e | 349 | _ => sz - 1, // sz includes the null terminator |
85aaf69f SL |
350 | } |
351 | }, super::os2path).ok() | |
352 | }) | |
353 | } | |
c34b1796 AL |
354 | |
355 | pub fn exit(code: i32) -> ! { | |
92a42be0 | 356 | unsafe { c::ExitProcess(code as c::UINT) } |
c34b1796 | 357 | } |