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_mut_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
238 let mut argv_size = 0;
240 cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_size, ptr::null_mut(), 0))?;
241
242 let argc = argv_size.div_exact(size_of::<*const libc::c_char>()).unwrap();
244 let mut argv = Vec::<*const libc::c_char>::with_capacity(argc);
245
246 cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_size, ptr::null_mut(), 0))?;
248 let argc = argv_size.div_exact(size_of::<*const libc::c_char>()).unwrap();
249 argv.set_len(argc);
250
251 if argv[0].is_null() {
252 return Err(io::const_error!(io::ErrorKind::Uncategorized, "no current exe available"));
253 }
254 let argv0 = CStr::from_ptr(argv[0]).to_bytes();
255 if argv0.iter().any(|b| *b == b'/') {
256 crate::fs::canonicalize(OsStr::from_bytes(argv0))
258 } else {
259 Ok(PathBuf::from(OsStr::from_bytes(argv0)))
263 }
264 }
265}
266
267#[cfg(any(
268 target_os = "linux",
269 target_os = "cygwin",
270 target_os = "hurd",
271 target_os = "android",
272 target_os = "nuttx",
273 target_os = "emscripten"
274))]
275pub fn current_exe() -> io::Result<PathBuf> {
276 match crate::fs::read_link("/proc/self/exe") {
277 Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(::core::hint::must_use(::core::io::Error::from_static_message(const {
&::core::io::SimpleMessage {
kind: io::ErrorKind::Uncategorized,
message: "no /proc/self/exe available. Is /proc mounted?",
}
}))io::const_error!(
278 io::ErrorKind::Uncategorized,
279 "no /proc/self/exe available. Is /proc mounted?",
280 )),
281 other => other,
282 }
283}
284
285#[cfg(any(target_os = "nto", target_os = "qnx"))]
286pub fn current_exe() -> io::Result<PathBuf> {
287 let mut e = crate::fs::read("/proc/self/exefile")?;
288 if let Some(0) = e.last() {
291 e.pop();
292 }
293 Ok(PathBuf::from(OsString::from_vec(e)))
294}
295
296#[cfg(target_vendor = "apple")]
297pub fn current_exe() -> io::Result<PathBuf> {
298 unsafe {
299 let mut sz: u32 = 0;
300 #[expect(deprecated)]
301 libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz);
302 if sz == 0 {
303 return Err(io::Error::last_os_error());
304 }
305 let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
306 #[expect(deprecated)]
307 let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
308 if err != 0 {
309 return Err(io::Error::last_os_error());
310 }
311 v.set_len(sz as usize - 1); Ok(PathBuf::from(OsString::from_vec(v)))
313 }
314}
315
316#[cfg(any(target_os = "solaris", target_os = "illumos"))]
317pub fn current_exe() -> io::Result<PathBuf> {
318 if let Ok(path) = crate::fs::read_link("/proc/self/path/a.out") {
319 Ok(path)
320 } else {
321 unsafe {
322 let path = libc::getexecname();
323 if path.is_null() {
324 Err(io::Error::last_os_error())
325 } else {
326 let filename = CStr::from_ptr(path).to_bytes();
327 let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename));
328
329 if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) }
332 }
333 }
334 }
335}
336
337#[cfg(target_os = "haiku")]
338pub fn current_exe() -> io::Result<PathBuf> {
339 let mut name = vec![0; libc::PATH_MAX as usize];
340 unsafe {
341 let result = libc::find_path(
342 crate::ptr::null_mut(),
343 libc::B_FIND_PATH_IMAGE_PATH,
344 crate::ptr::null_mut(),
345 name.as_mut_ptr(),
346 name.len(),
347 );
348 if result != libc::B_OK {
349 Err(io::const_error!(io::ErrorKind::Uncategorized, "error getting executable path"))
350 } else {
351 let name = CStr::from_ptr(name.as_ptr()).to_bytes();
353 Ok(PathBuf::from(OsStr::from_bytes(name)))
354 }
355 }
356}
357
358#[cfg(target_os = "redox")]
359pub fn current_exe() -> io::Result<PathBuf> {
360 crate::fs::read_to_string("/scheme/sys/exe").map(PathBuf::from)
361}
362
363#[cfg(target_os = "rtems")]
364pub fn current_exe() -> io::Result<PathBuf> {
365 crate::fs::read_to_string("sys:exe").map(PathBuf::from)
366}
367
368#[cfg(target_os = "l4re")]
369pub fn current_exe() -> io::Result<PathBuf> {
370 Err(io::const_error!(io::ErrorKind::Unsupported, "not yet implemented!"))
371}
372
373#[cfg(target_os = "vxworks")]
374pub fn current_exe() -> io::Result<PathBuf> {
375 #[cfg(test)]
376 use realstd::env;
377
378 #[cfg(not(test))]
379 use crate::env;
380
381 let exe_path = env::args().next().unwrap();
382 let path = path::Path::new(&exe_path);
383 path.canonicalize()
384}
385
386#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
387pub fn current_exe() -> io::Result<PathBuf> {
388 crate::sys::pal::unsupported::unsupported()
389}
390
391#[cfg(target_os = "fuchsia")]
392pub fn current_exe() -> io::Result<PathBuf> {
393 #[cfg(test)]
394 use realstd::env;
395
396 #[cfg(not(test))]
397 use crate::env;
398
399 let exe_path = env::args().next().ok_or(io::const_error!(
400 io::ErrorKind::Uncategorized,
401 "an executable path was not found because no arguments were provided through argv",
402 ))?;
403 let path = PathBuf::from(exe_path);
404
405 if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
407}
408
409#[cfg(target_vendor = "apple")]
410fn darwin_temp_dir() -> PathBuf {
411 crate::sys::pal::conf::confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64))
412 .map(PathBuf::from)
413 .unwrap_or_else(|_| {
414 PathBuf::from("/tmp")
417 })
418}
419
420pub fn temp_dir() -> PathBuf {
421 crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
422 cfg_select! {
423 target_vendor = "apple" => darwin_temp_dir(),
424 target_os = "android" => PathBuf::from("/data/local/tmp"),
425 _ => PathBuf::from("/tmp"),
426 }
427 })
428}
429
430pub fn home_dir() -> Option<PathBuf> {
431 return crate::env::var_os("HOME")
432 .filter(|s| !s.is_empty())
433 .or_else(|| unsafe { fallback() })
434 .map(PathBuf::from);
435
436 #[cfg(any(
437 target_os = "android",
438 target_os = "emscripten",
439 target_os = "redox",
440 target_os = "vxworks",
441 target_os = "espidf",
442 target_os = "horizon",
443 target_os = "vita",
444 target_os = "nuttx",
445 all(target_vendor = "apple", not(target_os = "macos")),
446 ))]
447 unsafe fn fallback() -> Option<OsString> {
448 None
449 }
450 #[cfg(not(any(
451 target_os = "android",
452 target_os = "emscripten",
453 target_os = "redox",
454 target_os = "vxworks",
455 target_os = "espidf",
456 target_os = "horizon",
457 target_os = "vita",
458 target_os = "nuttx",
459 all(target_vendor = "apple", not(target_os = "macos")),
460 )))]
461 unsafe fn fallback() -> Option<OsString> {
462 let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
463 n if n < 0 => 512 as usize,
464 n => n as usize,
465 };
466 let mut buf = Vec::with_capacity(amt);
467 let mut p = mem::MaybeUninit::<libc::passwd>::uninit();
468 let mut result = ptr::null_mut();
469 match libc::getpwuid_r(
470 libc::getuid(),
471 p.as_mut_ptr(),
472 buf.as_mut_ptr(),
473 buf.capacity(),
474 &mut result,
475 ) {
476 0 if !result.is_null() => {
477 let ptr = (*result).pw_dir as *const _;
478 let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
479 Some(OsStringExt::from_vec(bytes))
480 }
481 _ => None,
482 }
483 }
484}