1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#![doc = include_str!("../README.md")]
#![doc(test(attr(
warn(unused),
deny(warnings),
// W/o this, we seem to get some bogus warning about `extern crate ..`.
allow(unused_extern_crates),
)))]
use std::path::PathBuf;
/// Get the path of the current user's home directory.
///
/// See the library documentation for more information.
pub fn home_dir() -> Option<PathBuf> {
match std::env::var("HOME") {
Ok(home) => Some(home.into()),
Err(_) => {
#[cfg(unix)]
{
unix::home_dir()
}
#[cfg(windows)]
{
win32::home_dir()
}
}
}
}
#[cfg(unix)]
mod unix {
use std::ffi::{CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
pub(super) fn home_dir() -> Option<PathBuf> {
let uid = unsafe { libc::geteuid() };
let passwd = unsafe { libc::getpwuid(uid) };
// getpwnam(3):
// The getpwnam() and getpwuid() functions return a pointer to a passwd structure, or NULL
// if the matching entry is not found or an error occurs. If an error occurs, errno is set
// to indicate the error. If one wants to check errno after the call, it should be set to
// zero before the call. The return value may point to a static area, and may be overwritten
// by subsequent calls to getpwent(3), getpwnam(), or getpwuid().
if passwd.is_null() {
return None;
}
// SAFETY: `getpwuid()` returns either NULL or a valid pointer to a `passwd` structure.
let passwd = unsafe { &*passwd };
if passwd.pw_dir.is_null() {
return None;
}
// SAFETY: `getpwuid()->pw_dir` is a valid pointer to a c-string.
let home_dir = unsafe { CStr::from_ptr(passwd.pw_dir) };
Some(PathBuf::from(OsStr::from_bytes(home_dir.to_bytes())))
}
}
#[cfg(windows)]
mod win32 {
use std::{path::PathBuf, ptr};
use winapi::{
shared::winerror::S_OK,
um::{
combaseapi::CoTaskMemFree, knownfolders::FOLDERID_Profile, shlobj::SHGetKnownFolderPath,
},
};
pub(super) fn home_dir() -> Option<PathBuf> {
let mut psz_path = ptr::null_mut();
let res = unsafe {
SHGetKnownFolderPath(
&FOLDERID_Profile,
0,
ptr::null_mut(),
&mut psz_path as *mut _,
)
};
if res != S_OK {
return None;
}
// Determine the length of the UTF-16 string.
let mut len = 0;
// SAFETY: `psz_path` guaranteed to be a valid pointer to a null-terminated UTF-16 string.
while unsafe { *(psz_path as *const u16).offset(len) } != 0 {
len += 1;
}
let slice = unsafe { std::slice::from_raw_parts(psz_path, len as usize) };
let path = String::from_utf16(slice).ok()?;
unsafe {
CoTaskMemFree(psz_path as *mut _);
}
Some(PathBuf::from(path))
}
}