1
Fork 0
rust/src/libstd/sys/windows/os.rs
Alex Crichton ae7959d298 rollup merge of #24377: apasel422/docs
Conflicts:
	src/libstd/net/ip.rs
	src/libstd/sys/unix/fs.rs
	src/libstd/sys/unix/mod.rs
	src/libstd/sys/windows/mod.rs
2015-04-14 10:56:57 -07:00

364 lines
10 KiB
Rust

// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Implementation of `std::os` functionality for Windows
#![allow(bad_style)]
use prelude::v1::*;
use os::windows::prelude::*;
use error::Error as StdError;
use ffi::{OsString, OsStr};
use fmt;
use io;
use libc::types::os::arch::extra::LPWCH;
use libc::{self, c_int, c_void};
use mem;
use ops::Range;
use os::windows::ffi::EncodeWide;
use path::{self, PathBuf};
use ptr;
use slice;
use sys::c;
use sys::handle::Handle;
use libc::funcs::extra::kernel32::{
GetEnvironmentStringsW,
FreeEnvironmentStringsW
};
pub fn errno() -> i32 {
unsafe { libc::GetLastError() as i32 }
}
/// Gets a detailed string description for the given error number.
pub fn error_string(errnum: i32) -> String {
use libc::types::os::arch::extra::DWORD;
use libc::types::os::arch::extra::LPWSTR;
use libc::types::os::arch::extra::LPVOID;
use libc::types::os::arch::extra::WCHAR;
#[link_name = "kernel32"]
extern "system" {
fn FormatMessageW(flags: DWORD,
lpSrc: LPVOID,
msgId: DWORD,
langId: DWORD,
buf: LPWSTR,
nsize: DWORD,
args: *const c_void)
-> DWORD;
}
const FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
const FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
// This value is calculated from the macro
// MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
let langId = 0x0800 as DWORD;
let mut buf = [0 as WCHAR; 2048];
unsafe {
let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null_mut(),
errnum as DWORD,
langId,
buf.as_mut_ptr(),
buf.len() as DWORD,
ptr::null());
if res == 0 {
// Sometimes FormatMessageW can fail e.g. system doesn't like langId,
let fm_err = errno();
return format!("OS Error {} (FormatMessageW() returned error {})",
errnum, fm_err);
}
let b = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
let msg = String::from_utf16(&buf[..b]);
match msg {
Ok(msg) => msg,
Err(..) => format!("OS Error {} (FormatMessageW() returned \
invalid UTF-16)", errnum),
}
}
}
pub struct Env {
base: LPWCH,
cur: LPWCH,
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
unsafe {
if *self.cur == 0 { return None }
let p = &*self.cur;
let mut len = 0;
while *(p as *const u16).offset(len) != 0 {
len += 1;
}
let p = p as *const u16;
let s = slice::from_raw_parts(p, len as usize);
self.cur = self.cur.offset(len + 1);
let (k, v) = match s.iter().position(|&b| b == '=' as u16) {
Some(n) => (&s[..n], &s[n+1..]),
None => (s, &[][..]),
};
Some((OsStringExt::from_wide(k), OsStringExt::from_wide(v)))
}
}
}
impl Drop for Env {
fn drop(&mut self) {
unsafe { FreeEnvironmentStringsW(self.base); }
}
}
pub fn env() -> Env {
unsafe {
let ch = GetEnvironmentStringsW();
if ch as usize == 0 {
panic!("failure getting env string from OS: {}",
io::Error::last_os_error());
}
Env { base: ch, cur: ch }
}
}
pub struct SplitPaths<'a> {
data: EncodeWide<'a>,
must_yield: bool,
}
pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
SplitPaths {
data: unparsed.encode_wide(),
must_yield: true,
}
}
impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
// On Windows, the PATH environment variable is semicolon separated.
// Double quotes are used as a way of introducing literal semicolons
// (since c:\some;dir is a valid Windows path). Double quotes are not
// themselves permitted in path names, so there is no way to escape a
// double quote. Quoted regions can appear in arbitrary locations, so
//
// c:\foo;c:\som"e;di"r;c:\bar
//
// Should parse as [c:\foo, c:\some;dir, c:\bar].
//
// (The above is based on testing; there is no clear reference available
// for the grammar.)
let must_yield = self.must_yield;
self.must_yield = false;
let mut in_progress = Vec::new();
let mut in_quote = false;
for b in self.data.by_ref() {
if b == '"' as u16 {
in_quote = !in_quote;
} else if b == ';' as u16 && !in_quote {
self.must_yield = true;
break
} else {
in_progress.push(b)
}
}
if !must_yield && in_progress.is_empty() {
None
} else {
Some(super::os2path(&in_progress))
}
}
}
#[derive(Debug)]
pub struct JoinPathsError;
pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
where I: Iterator<Item=T>, T: AsRef<OsStr>
{
let mut joined = Vec::new();
let sep = b';' as u16;
for (i, path) in paths.enumerate() {
let path = path.as_ref();
if i > 0 { joined.push(sep) }
let v = path.encode_wide().collect::<Vec<u16>>();
if v.contains(&(b'"' as u16)) {
return Err(JoinPathsError)
} else if v.contains(&sep) {
joined.push(b'"' as u16);
joined.push_all(&v[..]);
joined.push(b'"' as u16);
} else {
joined.push_all(&v[..]);
}
}
Ok(OsStringExt::from_wide(&joined[..]))
}
impl fmt::Display for JoinPathsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
"path segment contains `\"`".fmt(f)
}
}
impl StdError for JoinPathsError {
fn description(&self) -> &str { "failed to join paths" }
}
pub fn current_exe() -> io::Result<PathBuf> {
super::fill_utf16_buf(|buf, sz| unsafe {
libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
}, super::os2path)
}
pub fn getcwd() -> io::Result<PathBuf> {
super::fill_utf16_buf(|buf, sz| unsafe {
libc::GetCurrentDirectoryW(sz, buf)
}, super::os2path)
}
pub fn chdir(p: &path::Path) -> io::Result<()> {
let p: &OsStr = p.as_ref();
let mut p = p.encode_wide().collect::<Vec<_>>();
p.push(0);
unsafe {
match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
true => Ok(()),
false => Err(io::Error::last_os_error()),
}
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
let k = super::to_utf16_os(k);
super::fill_utf16_buf(|buf, sz| unsafe {
libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
}, |buf| {
OsStringExt::from_wide(buf)
}).ok()
}
pub fn setenv(k: &OsStr, v: &OsStr) {
let k = super::to_utf16_os(k);
let v = super::to_utf16_os(v);
unsafe {
if libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) == 0 {
panic!("failed to set env: {}", io::Error::last_os_error());
}
}
}
pub fn unsetenv(n: &OsStr) {
let v = super::to_utf16_os(n);
unsafe {
if libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) == 0 {
panic!("failed to unset env: {}", io::Error::last_os_error());
}
}
}
pub struct Args {
range: Range<isize>,
cur: *mut *mut u16,
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.range.next().map(|i| unsafe {
let ptr = *self.cur.offset(i);
let mut len = 0;
while *ptr.offset(len) != 0 { len += 1; }
// Push it onto the list.
let ptr = ptr as *const u16;
let buf = slice::from_raw_parts(ptr, len as usize);
OsStringExt::from_wide(buf)
})
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.range.len() }
}
impl Drop for Args {
fn drop(&mut self) {
unsafe { c::LocalFree(self.cur as *mut c_void); }
}
}
pub fn args() -> Args {
unsafe {
let mut nArgs: c_int = 0;
let lpCmdLine = c::GetCommandLineW();
let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);
Args { cur: szArgList, range: 0..(nArgs as isize) }
}
}
pub fn page_size() -> usize {
unsafe {
let mut info = mem::zeroed();
libc::GetSystemInfo(&mut info);
return info.dwPageSize as usize;
}
}
pub fn temp_dir() -> PathBuf {
super::fill_utf16_buf(|buf, sz| unsafe {
c::GetTempPathW(sz, buf)
}, super::os2path).unwrap()
}
pub fn home_dir() -> Option<PathBuf> {
getenv("HOME".as_ref()).or_else(|| {
getenv("USERPROFILE".as_ref())
}).map(PathBuf::from).or_else(|| unsafe {
let me = c::GetCurrentProcess();
let mut token = ptr::null_mut();
if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
return None
}
let _handle = Handle::new(token);
super::fill_utf16_buf(|buf, mut sz| {
match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
0 if libc::GetLastError() != 0 => 0,
0 => sz,
n => n as libc::DWORD,
}
}, super::os2path).ok()
})
}
pub fn exit(code: i32) -> ! {
unsafe { c::ExitProcess(code as libc::c_uint) }
}