Mikhail Zabaluev 3ac862816f Replace usage of slice::from_raw_buf with slice::from_raw_parts
New functions, slice::from_raw_parts and slice::from_raw_parts_mut,
are added to implement the lifetime convention as agreed in RFC PR #556.
The functions slice::from_raw_buf and slice::from_raw_mut_buf are
left deprecated for the time being.
2015-02-05 13:54:26 +02:00

376 lines
11 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::*;
use error::Error as StdError;
use ffi::{OsString, OsStr, AsOsStr};
use fmt;
use iter::Range;
use libc::types::os::arch::extra::LPWCH;
use libc::{self, c_int, c_void};
use mem;
use old_io::{IoError, IoResult};
use ptr;
use slice;
use sys::c;
use sys::fs::FileDesc;
use sys::handle::Handle as RawHandle;
use libc::funcs::extra::kernel32::{
GetEnvironmentStringsW,
FreeEnvironmentStringsW
};
pub fn errno() -> i32 {
unsafe { libc::GetLastError() as i32 }
}
/// Get 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;
}
static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
static 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 _).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: {}",
IoError::last_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 = Path;
fn next(&mut self) -> Option<Path> {
// 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(Show)]
pub struct JoinPathsError;
pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
where I: Iterator<Item=T>, T: AsOsStr
{
let mut joined = Vec::new();
let sep = b';' as u16;
for (i, path) in paths.enumerate() {
let path = path.as_os_str();
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() -> IoResult<Path> {
super::fill_utf16_buf(|buf, sz| unsafe {
libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
}, super::os2path)
}
pub fn getcwd() -> IoResult<Path> {
super::fill_utf16_buf(|buf, sz| unsafe {
libc::GetCurrentDirectoryW(sz, buf)
}, super::os2path)
}
pub fn chdir(p: &Path) -> IoResult<()> {
let mut p = p.as_os_str().encode_wide().collect::<Vec<_>>();
p.push(0);
unsafe {
match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
true => Ok(()),
false => Err(IoError::last_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: {}", IoError::last_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: {}", IoError::last_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 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: 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 unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
// Windows pipes work subtly differently than unix pipes, and their
// inheritance has to be handled in a different way that I do not
// fully understand. Here we explicitly make the pipe non-inheritable,
// which means to pass it to a subprocess they need to be duplicated
// first, as in std::run.
let mut fds = [0; 2];
match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
(libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
0 => {
assert!(fds[0] != -1 && fds[0] != 0);
assert!(fds[1] != -1 && fds[1] != 0);
Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
}
_ => Err(IoError::last_error()),
}
}
pub fn temp_dir() -> Path {
super::fill_utf16_buf(|buf, sz| unsafe {
c::GetTempPathW(sz, buf)
}, super::os2path).unwrap()
}
pub fn home_dir() -> Option<Path> {
getenv("HOME".as_os_str()).or_else(|| {
getenv("USERPROFILE".as_os_str())
}).map(|os| {
// FIXME: OsString => Path
Path::new(os.to_str().unwrap())
}).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 = RawHandle::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()
})
}