1#![allow(unused_imports)] use libc::{c_char, c_int, c_void};
6
7use crate::ffi::{CStr, OsStr, OsString};
8use crate::os::unix::prelude::*;
9use crate::path::{self, PathBuf};
10use crate::sys::helpers::run_path_with_cstr;
11use crate::sys::pal::cvt;
12use crate::{fmt, io, iter, mem, ptr, slice, str};
13
14const PATH_SEPARATOR: u8 = b':';
15
16#[cfg(target_os = "espidf")]
17pub fn getcwd() -> io::Result<PathBuf> {
18 Ok(PathBuf::from("/"))
19}
20
21#[cfg(not(target_os = "espidf"))]
22pub fn getcwd() -> io::Result<PathBuf> {
23 let mut buf = Vec::with_capacity(512);
24 loop {
25 unsafe {
26 let ptr = buf.as_mut_ptr() as *mut libc::c_char;
27 if !libc::getcwd(ptr, buf.capacity()).is_null() {
28 let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
29 buf.set_len(len);
30 buf.shrink_to_fit();
31 return Ok(PathBuf::from(OsString::from_vec(buf)));
32 } else {
33 let error = io::Error::last_os_error();
34 if error.raw_os_error() != Some(libc::ERANGE) {
35 return Err(error);
36 }
37 }
38
39 let cap = buf.capacity();
42 buf.set_len(cap);
43 buf.reserve(1);
44 }
45 }
46}
47
48#[cfg(target_os = "espidf")]
49pub fn chdir(_p: &path::Path) -> io::Result<()> {
50 crate::sys::pal::unsupported::unsupported()
51}
52
53#[cfg(not(target_os = "espidf"))]
54pub fn chdir(p: &path::Path) -> io::Result<()> {
55 let result = run_path_with_cstr(p, &|p| unsafe { Ok(libc::chdir(p.as_ptr())) })?;
56 if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
57}
58
59pub type SplitPaths<'a> = iter::Map<
62 slice::Split<'a, u8, impl FnMut(&u8) -> bool + 'static>,
63 impl FnMut(&[u8]) -> PathBuf + 'static,
64>;
65
66#[define_opaque(SplitPaths)]
67pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
68 fn is_separator(&b: &u8) -> bool {
69 b == PATH_SEPARATOR
70 }
71
72 fn into_pathbuf(part: &[u8]) -> PathBuf {
73 PathBuf::from(OsStr::from_bytes(part))
74 }
75
76 unparsed.as_bytes().split(is_separator).map(into_pathbuf)
77}
78
79#[derive(#[automatically_derived]
impl ::core::fmt::Debug for JoinPathsError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "JoinPathsError")
}
}Debug)]
80pub struct JoinPathsError;
81
82pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
83where
84 I: Iterator<Item = T>,
85 T: AsRef<OsStr>,
86{
87 let mut joined = Vec::new();
88
89 for (i, path) in paths.enumerate() {
90 let path = path.as_ref().as_bytes();
91 if i > 0 {
92 joined.push(PATH_SEPARATOR)
93 }
94 if path.contains(&PATH_SEPARATOR) {
95 return Err(JoinPathsError);
96 }
97 joined.extend_from_slice(path);
98 }
99 Ok(OsStringExt::from_vec(joined))
100}
101
102impl fmt::Display for JoinPathsError {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 f.write_fmt(format_args!("path segment contains separator `{0}`",
char::from(PATH_SEPARATOR)))write!(f, "path segment contains separator `{}`", char::from(PATH_SEPARATOR))
105 }
106}
107
108impl crate::error::Error for JoinPathsError {}
109
110#[cfg(target_os = "aix")]
111pub fn current_exe() -> io::Result<PathBuf> {
112 #[cfg(test)]
113 use realstd::env;
114
115 #[cfg(not(test))]
116 use crate::env;
117 use crate::io;
118
119 let exe_path = env::args().next().ok_or(io::const_error!(
120 io::ErrorKind::NotFound,
121 "an executable path was not found because no arguments were provided through argv",
122 ))?;
123 let path = PathBuf::from(exe_path);
124 if path.is_absolute() {
125 return path.canonicalize();
126 }
127 if let Some(pstr) = path.to_str()
129 && pstr.contains("/")
130 {
131 return getcwd().map(|cwd| cwd.join(path))?.canonicalize();
132 }
133 if let Some(p) = env::var_os(OsStr::from_bytes("PATH".as_bytes())) {
135 for search_path in split_paths(&p) {
136 let pb = search_path.join(&path);
137 if pb.is_file()
138 && let Ok(metadata) = crate::fs::metadata(&pb)
139 && metadata.permissions().mode() & 0o111 != 0
140 {
141 return pb.canonicalize();
142 }
143 }
144 }
145 Err(io::const_error!(io::ErrorKind::NotFound, "an executable path was not found"))
146}
147
148#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
149pub fn current_exe() -> io::Result<PathBuf> {
150 unsafe {
151 let mut mib = [
152 libc::CTL_KERN as c_int,
153 libc::KERN_PROC as c_int,
154 libc::KERN_PROC_PATHNAME as c_int,
155 -1 as c_int,
156 ];
157 let mut sz = 0;
158 cvt(libc::sysctl(
159 mib.as_mut_ptr(),
160 mib.len() as libc::c_uint,
161 ptr::null_mut(),
162 &mut sz,
163 ptr::null_mut(),
164 0,
165 ))?;
166 if sz == 0 {
167 return Err(io::Error::last_os_error());
168 }
169 let mut v: Vec<u8> = Vec::with_capacity(sz);
170 cvt(libc::sysctl(
171 mib.as_mut_ptr(),
172 mib.len() as libc::c_uint,
173 v.as_mut_ptr() as *mut libc::c_void,
174 &mut sz,
175 ptr::null_mut(),
176 0,
177 ))?;
178 if sz == 0 {
179 return Err(io::Error::last_os_error());
180 }
181 v.set_len(sz - 1); Ok(PathBuf::from(OsString::from_vec(v)))
183 }
184}
185
186#[cfg(target_os = "netbsd")]
187pub fn current_exe() -> io::Result<PathBuf> {
188 fn sysctl() -> io::Result<PathBuf> {
189 unsafe {
190 let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME];
191 let mut path_len: usize = 0;
192 cvt(libc::sysctl(
193 mib.as_ptr(),
194 mib.len() as libc::c_uint,
195 ptr::null_mut(),
196 &mut path_len,
197 ptr::null(),
198 0,
199 ))?;
200 if path_len <= 1 {
201 return Err(io::const_error!(
202 io::ErrorKind::Uncategorized,
203 "KERN_PROC_PATHNAME sysctl returned zero-length string",
204 ));
205 }
206 let mut path: Vec<u8> = Vec::with_capacity(path_len);
207 cvt(libc::sysctl(
208 mib.as_ptr(),
209 mib.len() as libc::c_uint,
210 path.as_ptr() as *mut libc::c_void,
211 &mut path_len,
212 ptr::null(),
213 0,
214 ))?;
215 path.set_len(path_len - 1); Ok(PathBuf::from(OsString::from_vec(path)))
217 }
218 }
219 fn procfs() -> io::Result<PathBuf> {
220 let curproc_exe = path::Path::new("/proc/curproc/exe");
221 if curproc_exe.is_file() {
222 return crate::fs::read_link(curproc_exe);
223 }
224 Err(io::const_error!(
225 io::ErrorKind::Uncategorized,
226 "/proc/curproc/exe doesn't point to regular file.",
227 ))
228 }
229 sysctl().or_else(|_| procfs())
230}
231
232#[cfg(target_os = "openbsd")]
233pub fn current_exe() -> io::Result<PathBuf> {
234 unsafe {
235 let mut mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, libc::getpid(), libc::KERN_PROC_ARGV];
236 let mib = mib.as_mut_ptr();
237 let mut argv_len = 0;
238 cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len, ptr::null_mut(), 0))?;
239 let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize);
240 cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?;
241 argv.set_len(argv_len as usize);
242 if argv[0].is_null() {
243 return Err(io::const_error!(io::ErrorKind::Uncategorized, "no current exe available"));
244 }
245 let argv0 = CStr::from_ptr(argv[0]).to_bytes();
246 if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
247 crate::fs::canonicalize(OsStr::from_bytes(argv0))
248 } else {
249 Ok(PathBuf::from(OsStr::from_bytes(argv0)))
250 }
251 }
252}
253
254#[cfg(any(
255 target_os = "linux",
256 target_os = "cygwin",
257 target_os = "hurd",
258 target_os = "android",
259 target_os = "nuttx",
260 target_os = "emscripten"
261))]
262pub fn current_exe() -> io::Result<PathBuf> {
263 match crate::fs::read_link("/proc/self/exe") {
264 Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(crate::hint::must_use(crate::io::Error::from_static_message(const {
&crate::io::SimpleMessage {
kind: io::ErrorKind::Uncategorized,
message: "no /proc/self/exe available. Is /proc mounted?",
}
}))io::const_error!(
265 io::ErrorKind::Uncategorized,
266 "no /proc/self/exe available. Is /proc mounted?",
267 )),
268 other => other,
269 }
270}
271
272#[cfg(target_os = "nto")]
273pub fn current_exe() -> io::Result<PathBuf> {
274 let mut e = crate::fs::read("/proc/self/exefile")?;
275 if let Some(0) = e.last() {
278 e.pop();
279 }
280 Ok(PathBuf::from(OsString::from_vec(e)))
281}
282
283#[cfg(target_vendor = "apple")]
284pub fn current_exe() -> io::Result<PathBuf> {
285 unsafe {
286 let mut sz: u32 = 0;
287 #[expect(deprecated)]
288 libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz);
289 if sz == 0 {
290 return Err(io::Error::last_os_error());
291 }
292 let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
293 #[expect(deprecated)]
294 let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
295 if err != 0 {
296 return Err(io::Error::last_os_error());
297 }
298 v.set_len(sz as usize - 1); Ok(PathBuf::from(OsString::from_vec(v)))
300 }
301}
302
303#[cfg(any(target_os = "solaris", target_os = "illumos"))]
304pub fn current_exe() -> io::Result<PathBuf> {
305 if let Ok(path) = crate::fs::read_link("/proc/self/path/a.out") {
306 Ok(path)
307 } else {
308 unsafe {
309 let path = libc::getexecname();
310 if path.is_null() {
311 Err(io::Error::last_os_error())
312 } else {
313 let filename = CStr::from_ptr(path).to_bytes();
314 let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename));
315
316 if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) }
319 }
320 }
321 }
322}
323
324#[cfg(target_os = "haiku")]
325pub fn current_exe() -> io::Result<PathBuf> {
326 let mut name = vec![0; libc::PATH_MAX as usize];
327 unsafe {
328 let result = libc::find_path(
329 crate::ptr::null_mut(),
330 libc::B_FIND_PATH_IMAGE_PATH,
331 crate::ptr::null_mut(),
332 name.as_mut_ptr(),
333 name.len(),
334 );
335 if result != libc::B_OK {
336 Err(io::const_error!(io::ErrorKind::Uncategorized, "error getting executable path"))
337 } else {
338 let name = CStr::from_ptr(name.as_ptr()).to_bytes();
340 Ok(PathBuf::from(OsStr::from_bytes(name)))
341 }
342 }
343}
344
345#[cfg(target_os = "redox")]
346pub fn current_exe() -> io::Result<PathBuf> {
347 crate::fs::read_to_string("/scheme/sys/exe").map(PathBuf::from)
348}
349
350#[cfg(target_os = "rtems")]
351pub fn current_exe() -> io::Result<PathBuf> {
352 crate::fs::read_to_string("sys:exe").map(PathBuf::from)
353}
354
355#[cfg(target_os = "l4re")]
356pub fn current_exe() -> io::Result<PathBuf> {
357 Err(io::const_error!(io::ErrorKind::Unsupported, "not yet implemented!"))
358}
359
360#[cfg(target_os = "vxworks")]
361pub fn current_exe() -> io::Result<PathBuf> {
362 #[cfg(test)]
363 use realstd::env;
364
365 #[cfg(not(test))]
366 use crate::env;
367
368 let exe_path = env::args().next().unwrap();
369 let path = path::Path::new(&exe_path);
370 path.canonicalize()
371}
372
373#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
374pub fn current_exe() -> io::Result<PathBuf> {
375 crate::sys::pal::unsupported::unsupported()
376}
377
378#[cfg(target_os = "fuchsia")]
379pub fn current_exe() -> io::Result<PathBuf> {
380 #[cfg(test)]
381 use realstd::env;
382
383 #[cfg(not(test))]
384 use crate::env;
385
386 let exe_path = env::args().next().ok_or(io::const_error!(
387 io::ErrorKind::Uncategorized,
388 "an executable path was not found because no arguments were provided through argv",
389 ))?;
390 let path = PathBuf::from(exe_path);
391
392 if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
394}
395
396#[cfg(all(target_vendor = "apple", not(miri)))]
397fn darwin_temp_dir() -> PathBuf {
398 crate::sys::pal::conf::confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64))
399 .map(PathBuf::from)
400 .unwrap_or_else(|_| {
401 PathBuf::from("/tmp")
404 })
405}
406
407pub fn temp_dir() -> PathBuf {
408 crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
409 cfg_select! {
410 all(target_vendor = "apple", not(miri)) => darwin_temp_dir(),
411 target_os = "android" => PathBuf::from("/data/local/tmp"),
412 _ => PathBuf::from("/tmp"),
413 }
414 })
415}
416
417pub fn home_dir() -> Option<PathBuf> {
418 return crate::env::var_os("HOME")
419 .filter(|s| !s.is_empty())
420 .or_else(|| unsafe { fallback() })
421 .map(PathBuf::from);
422
423 #[cfg(any(
424 target_os = "android",
425 target_os = "emscripten",
426 target_os = "redox",
427 target_os = "vxworks",
428 target_os = "espidf",
429 target_os = "horizon",
430 target_os = "vita",
431 target_os = "nuttx",
432 all(target_vendor = "apple", not(target_os = "macos")),
433 ))]
434 unsafe fn fallback() -> Option<OsString> {
435 None
436 }
437 #[cfg(not(any(
438 target_os = "android",
439 target_os = "emscripten",
440 target_os = "redox",
441 target_os = "vxworks",
442 target_os = "espidf",
443 target_os = "horizon",
444 target_os = "vita",
445 target_os = "nuttx",
446 all(target_vendor = "apple", not(target_os = "macos")),
447 )))]
448 unsafe fn fallback() -> Option<OsString> {
449 let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
450 n if n < 0 => 512 as usize,
451 n => n as usize,
452 };
453 let mut buf = Vec::with_capacity(amt);
454 let mut p = mem::MaybeUninit::<libc::passwd>::uninit();
455 let mut result = ptr::null_mut();
456 match libc::getpwuid_r(
457 libc::getuid(),
458 p.as_mut_ptr(),
459 buf.as_mut_ptr(),
460 buf.capacity(),
461 &mut result,
462 ) {
463 0 if !result.is_null() => {
464 let ptr = (*result).pw_dir as *const _;
465 let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
466 Some(OsStringExt::from_vec(bytes))
467 }
468 _ => None,
469 }
470 }
471}