#[cfg(windows)]
extern crate scopeguard;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate userenv;
#[cfg(windows)]
use winapi::shared::minwindef::DWORD;
use std::path::{PathBuf, Path};
use std::io;
use std::env;
pub fn home_dir() -> Option<PathBuf> {
home_dir_()
}
#[cfg(windows)]
fn home_dir_() -> Option<PathBuf> {
use std::ptr;
use userenv::GetUserProfileDirectoryW;
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
use winapi::um::winnt::TOKEN_READ;
use scopeguard;
::std::env::var_os("USERPROFILE").map(PathBuf::from).or_else(|| unsafe {
let me = GetCurrentProcess();
let mut token = ptr::null_mut();
if OpenProcessToken(me, TOKEN_READ, &mut token) == 0 {
return None;
}
let _g = scopeguard::guard(token, |h| { let _ = CloseHandle(*h); });
fill_utf16_buf(|buf, mut sz| {
match GetUserProfileDirectoryW(token, buf, &mut sz) {
0 if GetLastError() != ERROR_INSUFFICIENT_BUFFER => 0,
0 => sz,
_ => sz - 1, }
}, os2path).ok()
})
}
#[cfg(windows)]
fn os2path(s: &[u16]) -> PathBuf {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
PathBuf::from(OsString::from_wide(s))
}
#[cfg(windows)]
fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T>
where F1: FnMut(*mut u16, DWORD) -> DWORD,
F2: FnOnce(&[u16]) -> T
{
use winapi::um::errhandlingapi::{GetLastError, SetLastError};
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
let mut stack_buf = [0u16; 512];
let mut heap_buf = Vec::new();
unsafe {
let mut n = stack_buf.len();
loop {
let buf = if n <= stack_buf.len() {
&mut stack_buf[..]
} else {
let extra = n - heap_buf.len();
heap_buf.reserve(extra);
heap_buf.set_len(n);
&mut heap_buf[..]
};
SetLastError(0);
let k = match f1(buf.as_mut_ptr(), n as DWORD) {
0 if GetLastError() == 0 => 0,
0 => return Err(io::Error::last_os_error()),
n => n,
} as usize;
if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER {
n *= 2;
} else if k >= n {
n = k;
} else {
return Ok(f2(&buf[..k]))
}
}
}
}
#[cfg(unix)]
fn home_dir_() -> Option<PathBuf> {
::std::env::home_dir()
}
pub fn cargo_home() -> io::Result<PathBuf> {
let cwd = env::current_dir()?;
cargo_home_with_cwd(&cwd)
}
pub fn cargo_home_with_cwd(cwd: &Path) -> io::Result<PathBuf> {
let env_var = env::var_os("CARGO_HOME");
let env_var = if let Some(v) = env_var {
let vv = v.to_string_lossy().to_string();
if vv.contains(".multirust/cargo") ||
vv.contains(r".multirust\cargo") ||
vv.trim().is_empty() {
None
} else {
Some(v)
}
} else {
None
};
let env_cargo_home = env_var.map(|home| cwd.join(home));
let home_dir = home_dir()
.ok_or(io::Error::new(io::ErrorKind::Other, "couldn't find home dir"));
let user_home = home_dir.map(|p| p.join(".cargo"));
let compat_home_dir = ::std::env::home_dir();
let compat_user_home = compat_home_dir.map(|p| p.join(".cargo"));
if let Some(p) = env_cargo_home {
Ok(p)
} else {
if let Some(d) = compat_user_home {
if d.exists() {
Ok(d)
} else {
user_home
}
} else {
user_home
}
}
}
pub fn rustup_home() -> io::Result<PathBuf> {
let cwd = env::current_dir()?;
rustup_home_with_cwd(&cwd)
}
pub fn rustup_home_with_cwd(cwd: &Path) -> io::Result<PathBuf> {
let env_var = env::var_os("RUSTUP_HOME");
let env_rustup_home = env_var.map(|home| cwd.join(home));
let home_dir = home_dir()
.ok_or(io::Error::new(io::ErrorKind::Other, "couldn't find home dir"));
let user_home = if use_rustup_dir() {
home_dir.map(|d| d.join(".rustup"))
} else {
home_dir.map(|d| d.join(".multirust"))
};
if let Some(p) = env_rustup_home {
Ok(p)
} else {
user_home
}
}
fn use_rustup_dir() -> bool {
fn rustup_dir() -> Option<PathBuf> {
home_dir().map(|p| p.join(".rustup"))
}
fn multirust_dir() -> Option<PathBuf> {
home_dir().map(|p| p.join(".multirust"))
}
fn rustup_dir_exists() -> bool {
rustup_dir().map(|p| p.exists()).unwrap_or(false)
}
fn multirust_dir_exists() -> bool {
multirust_dir().map(|p| p.exists()).unwrap_or(false)
}
fn rustup_old_version_exists() -> bool {
rustup_dir()
.map(|p| p.join("rustup-version").exists())
.unwrap_or(false)
}
!rustup_old_version_exists()
&& (rustup_dir_exists() || !multirust_dir_exists())
}