]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/windows/os.rs
New upstream version 1.13.0+dfsg1
[rustc.git] / src / libstd / sys / windows / os.rs
CommitLineData
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 15use os::windows::prelude::*;
1a4d82fc 16
85aaf69f 17use error::Error as StdError;
c34b1796 18use ffi::{OsString, OsStr};
1a4d82fc 19use fmt;
c34b1796 20use io;
92a42be0 21use libc::{c_int, c_void};
c34b1796
AL
22use ops::Range;
23use os::windows::ffi::EncodeWide;
24use path::{self, PathBuf};
1a4d82fc
JJ
25use ptr;
26use slice;
92a42be0 27use sys::{c, cvt};
c34b1796 28use sys::handle::Handle;
1a4d82fc 29
92a42be0 30use super::to_u16s;
1a4d82fc 31
85aaf69f 32pub 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 37pub 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 73pub struct Env {
92a42be0
SL
74 base: c::LPWCH,
75 cur: c::LPWCH,
1a4d82fc
JJ
76}
77
85aaf69f
SL
78impl 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
111impl Drop for Env {
112 fn drop(&mut self) {
92a42be0 113 unsafe { c::FreeEnvironmentStringsW(self.base); }
85aaf69f
SL
114 }
115}
1a4d82fc 116
85aaf69f 117pub 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
128pub struct SplitPaths<'a> {
129 data: EncodeWide<'a>,
130 must_yield: bool,
1a4d82fc
JJ
131}
132
85aaf69f
SL
133pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
134 SplitPaths {
135 data: unparsed.encode_wide(),
136 must_yield: true,
1a4d82fc 137 }
1a4d82fc
JJ
138}
139
85aaf69f 140impl<'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)]
182pub struct JoinPathsError;
183
184pub 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
208impl 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
214impl StdError for JoinPathsError {
215 fn description(&self) -> &str { "failed to join paths" }
216}
217
c34b1796 218pub 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 224pub 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
230pub 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 240pub 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 259pub 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 268pub 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
275pub struct Args {
276 range: Range<isize>,
277 cur: *mut *mut u16,
278}
279
5bcae85e
SL
280unsafe 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
290impl 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
298impl 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
304impl ExactSizeIterator for Args {
305 fn len(&self) -> usize { self.range.len() }
306}
1a4d82fc 307
85aaf69f
SL
308impl 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
316pub 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 329pub 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 335pub 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
355pub fn exit(code: i32) -> ! {
92a42be0 356 unsafe { c::ExitProcess(code as c::UINT) }
c34b1796 357}