1 use std::ffi::OsString;
2 use std::path::PathBuf;
3 
4 // we don't need to explicitly handle empty strings in the code above,
5 // because an empty string is not considered to be a absolute path here.
is_absolute_path(path: OsString) -> Option<PathBuf>6 pub fn is_absolute_path(path: OsString) -> Option<PathBuf> {
7     let path = PathBuf::from(path);
8     if path.is_absolute() {
9         Some(path)
10     } else {
11         None
12     }
13 }
14 
15 #[cfg(all(unix, not(target_os = "redox")))]
16 extern crate libc;
17 
18 #[cfg(all(unix, not(target_os = "redox")))]
19 mod target_unix_not_redox {
20 
21 use std::env;
22 use std::ffi::{CStr, OsString};
23 use std::mem;
24 use std::os::unix::ffi::OsStringExt;
25 use std::path::PathBuf;
26 use std::ptr;
27 
28 use super::libc;
29 
30 // https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/os.rs#L498
home_dir() -> Option<PathBuf>31 pub fn home_dir() -> Option<PathBuf> {
32     return env::var_os("HOME")
33         .and_then(|h| if h.is_empty() { None } else { Some(h) })
34         .or_else(|| unsafe { fallback() })
35         .map(PathBuf::from);
36 
37     #[cfg(any(target_os = "android", target_os = "ios", target_os = "emscripten"))]
38     unsafe fn fallback() -> Option<OsString> {
39         None
40     }
41     #[cfg(not(any(target_os = "android", target_os = "ios", target_os = "emscripten")))]
42     unsafe fn fallback() -> Option<OsString> {
43         let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
44             n if n < 0 => 512 as usize,
45             n => n as usize,
46         };
47         let mut buf = Vec::with_capacity(amt);
48         let mut passwd: libc::passwd = mem::zeroed();
49         let mut result = ptr::null_mut();
50         match libc::getpwuid_r(
51             libc::getuid(),
52             &mut passwd,
53             buf.as_mut_ptr(),
54             buf.capacity(),
55             &mut result,
56         ) {
57             0 if !result.is_null() => {
58                 let ptr = passwd.pw_dir as *const _;
59                 let bytes = CStr::from_ptr(ptr).to_bytes();
60                 if bytes.is_empty() {
61                     None
62                 } else {
63                     Some(OsStringExt::from_vec(bytes.to_vec()))
64                 }
65             }
66             _ => None,
67         }
68     }
69 }
70 
71 }
72 
73 #[cfg(all(unix, not(target_os = "redox")))]
74 pub use self::target_unix_not_redox::home_dir;
75 
76 #[cfg(target_os = "redox")]
77 extern crate redox_users;
78 
79 #[cfg(target_os = "redox")]
80 mod target_redox {
81 
82 use std::path::PathBuf;
83 
84 use super::redox_users::{All, AllUsers, Config};
85 
home_dir() -> Option<PathBuf>86 pub fn home_dir() -> Option<PathBuf> {
87     let current_uid = redox_users::get_uid().ok()?;
88     let users = AllUsers::new(Config::default()).ok()?;
89     let user = users.get_by_id(current_uid)?;
90 
91     Some(PathBuf::from(user.home.clone()))
92 }
93 
94 }
95 
96 #[cfg(target_os = "redox")]
97 pub use self::target_redox::home_dir;
98 
99 #[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
100 mod xdg_user_dirs;
101 
102 #[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
103 mod target_unix_not_mac {
104 
105 use std::collections::HashMap;
106 use std::env;
107 use std::path::{Path, PathBuf};
108 
109 use super::{home_dir, is_absolute_path};
110 use super::xdg_user_dirs;
111 
user_dir_file(home_dir: &Path) -> PathBuf112 fn user_dir_file(home_dir: &Path) -> PathBuf {
113     env::var_os("XDG_CONFIG_HOME").and_then(is_absolute_path).unwrap_or_else(|| home_dir.join(".config")).join("user-dirs.dirs")
114 }
115 
116 // this could be optimized further to not create a map and instead retrieve the requested path only
user_dir(user_dir_name: &str) -> Option<PathBuf>117 pub fn user_dir(user_dir_name: &str) -> Option<PathBuf> {
118     if let Some(home_dir) = home_dir() {
119         xdg_user_dirs::single(&home_dir, &user_dir_file(&home_dir), user_dir_name).remove(user_dir_name)
120     } else {
121         None
122     }
123 }
124 
user_dirs(home_dir_path: &Path) -> HashMap<String, PathBuf>125 pub fn user_dirs(home_dir_path: &Path) -> HashMap<String, PathBuf> {
126     xdg_user_dirs::all(home_dir_path, &user_dir_file(home_dir_path))
127 }
128 
129 }
130 
131 #[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
132 pub use self::target_unix_not_mac::{user_dir, user_dirs};
133 
134 #[cfg(target_os = "windows")]
135 extern crate winapi;
136 
137 #[cfg(target_os = "windows")]
138 mod target_windows {
139 
140 use std::ffi::OsString;
141 use std::os::windows::ffi::OsStringExt;
142 use std::path::PathBuf;
143 use std::ptr;
144 use std::slice;
145 
146 use super::winapi;
147 use super::winapi::shared::winerror;
148 use super::winapi::um::{combaseapi, knownfolders, shlobj, shtypes, winbase, winnt};
149 
known_folder(folder_id: shtypes::REFKNOWNFOLDERID) -> Option<PathBuf>150 pub fn known_folder(folder_id: shtypes::REFKNOWNFOLDERID) -> Option<PathBuf> {
151     unsafe {
152         let mut path_ptr: winnt::PWSTR = ptr::null_mut();
153         let result = shlobj::SHGetKnownFolderPath(folder_id, 0, ptr::null_mut(), &mut path_ptr);
154         if result == winerror::S_OK {
155             let len = winbase::lstrlenW(path_ptr) as usize;
156             let path = slice::from_raw_parts(path_ptr, len);
157             let ostr: OsString = OsStringExt::from_wide(path);
158             combaseapi::CoTaskMemFree(path_ptr as *mut winapi::ctypes::c_void);
159             Some(PathBuf::from(ostr))
160         } else {
161             None
162         }
163     }
164 }
165 
known_folder_profile() -> Option<PathBuf>166 pub fn known_folder_profile() -> Option<PathBuf> {
167     known_folder(&knownfolders::FOLDERID_Profile)
168 }
169 
known_folder_roaming_app_data() -> Option<PathBuf>170 pub fn known_folder_roaming_app_data() -> Option<PathBuf> {
171     known_folder(&knownfolders::FOLDERID_RoamingAppData)
172 }
173 
known_folder_local_app_data() -> Option<PathBuf>174 pub fn known_folder_local_app_data() -> Option<PathBuf> {
175     known_folder(&knownfolders::FOLDERID_LocalAppData)
176 }
177 
known_folder_music() -> Option<PathBuf>178 pub fn known_folder_music() -> Option<PathBuf> {
179     known_folder(&knownfolders::FOLDERID_Music)
180 }
181 
known_folder_desktop() -> Option<PathBuf>182 pub fn known_folder_desktop() -> Option<PathBuf> {
183     known_folder(&knownfolders::FOLDERID_Desktop)
184 }
185 
known_folder_documents() -> Option<PathBuf>186 pub fn known_folder_documents() -> Option<PathBuf> {
187     known_folder(&knownfolders::FOLDERID_Documents)
188 }
189 
known_folder_downloads() -> Option<PathBuf>190 pub fn known_folder_downloads() -> Option<PathBuf> {
191     known_folder(&knownfolders::FOLDERID_Downloads)
192 }
193 
known_folder_pictures() -> Option<PathBuf>194 pub fn known_folder_pictures() -> Option<PathBuf> {
195     known_folder(&knownfolders::FOLDERID_Pictures)
196 }
197 
known_folder_public() -> Option<PathBuf>198 pub fn known_folder_public() -> Option<PathBuf> {
199     known_folder(&knownfolders::FOLDERID_Public)
200 }
known_folder_templates() -> Option<PathBuf>201 pub fn known_folder_templates() -> Option<PathBuf> {
202     known_folder(&knownfolders::FOLDERID_Templates)
203 }
known_folder_videos() -> Option<PathBuf>204 pub fn known_folder_videos() -> Option<PathBuf> {
205     known_folder(&knownfolders::FOLDERID_Videos)
206 }
207 
208 }
209 
210 #[cfg(target_os = "windows")]
211 pub use self::target_windows::{
212     known_folder, known_folder_profile, known_folder_roaming_app_data, known_folder_local_app_data,
213     known_folder_music, known_folder_desktop, known_folder_documents, known_folder_downloads,
214     known_folder_pictures, known_folder_public, known_folder_templates, known_folder_videos
215 };
216