#![cfg(windows)]
use std::env;
use std::ffi::OsString;
use std::io;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::ptr;
use winapi::shared::minwindef::DWORD;
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
use winapi::um::errhandlingapi::{GetLastError, SetLastError};
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
use winapi::um::userenv::GetUserProfileDirectoryW;
use winapi::um::winnt::TOKEN_READ;
pub fn home_dir_inner() -> Option<PathBuf> {
env::var_os("USERPROFILE")
.map(PathBuf::from)
.or_else(|| home_dir_crt())
}
#[cfg(not(target_vendor = "uwp"))]
fn home_dir_crt() -> Option<PathBuf> {
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(target_vendor = "uwp")]
fn home_dir_crt() -> Option<PathBuf> {
None
}
fn os2path(s: &[u16]) -> PathBuf {
PathBuf::from(OsString::from_wide(s))
}
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,
{
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]));
}
}
}
}