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