]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/windows/os.rs
Imported Upstream version 1.9.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
JJ
14
15use prelude::v1::*;
c34b1796 16use os::windows::prelude::*;
1a4d82fc 17
85aaf69f 18use error::Error as StdError;
c34b1796 19use ffi::{OsString, OsStr};
1a4d82fc 20use fmt;
c34b1796 21use io;
92a42be0 22use libc::{c_int, c_void};
c34b1796
AL
23use ops::Range;
24use os::windows::ffi::EncodeWide;
25use path::{self, PathBuf};
1a4d82fc
JJ
26use ptr;
27use slice;
92a42be0 28use sys::{c, cvt};
c34b1796 29use sys::handle::Handle;
1a4d82fc 30
92a42be0 31use super::to_u16s;
1a4d82fc 32
85aaf69f 33pub 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 38pub 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 74pub struct Env {
92a42be0
SL
75 base: c::LPWCH,
76 cur: c::LPWCH,
1a4d82fc
JJ
77}
78
85aaf69f
SL
79impl 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
112impl Drop for Env {
113 fn drop(&mut self) {
92a42be0 114 unsafe { c::FreeEnvironmentStringsW(self.base); }
85aaf69f
SL
115 }
116}
1a4d82fc 117
85aaf69f 118pub 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
129pub struct SplitPaths<'a> {
130 data: EncodeWide<'a>,
131 must_yield: bool,
1a4d82fc
JJ
132}
133
85aaf69f
SL
134pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
135 SplitPaths {
136 data: unparsed.encode_wide(),
137 must_yield: true,
1a4d82fc 138 }
1a4d82fc
JJ
139}
140
85aaf69f 141impl<'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)]
183pub struct JoinPathsError;
184
185pub 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
209impl 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
215impl StdError for JoinPathsError {
216 fn description(&self) -> &str { "failed to join paths" }
217}
218
c34b1796 219pub 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 225pub 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
231pub 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 241pub 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 260pub 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 269pub 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
276pub struct Args {
277 range: Range<isize>,
278 cur: *mut *mut u16,
279}
280
281impl 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
298impl ExactSizeIterator for Args {
299 fn len(&self) -> usize { self.range.len() }
300}
1a4d82fc 301
85aaf69f
SL
302impl 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
310pub 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 323pub 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 329pub 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
349pub fn exit(code: i32) -> ! {
92a42be0 350 unsafe { c::ExitProcess(code as c::UINT) }
c34b1796 351}