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 | |
9 | use std::path::PathBuf; |
10 | |
11 | /// Get the path of the current user's home directory. |
12 | /// |
13 | /// See the library documentation for more information. |
14 | pub 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)] |
32 | mod 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)] |
65 | mod 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 | |