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