use std::path::{Path, PathBuf};
use std::{fs, io};
use crate::symlink::resolve_simple_symlink_chain;
#[inline]
pub(crate) fn compute_existing_prefix(
root_prefix: &Path,
components: &[std::ffi::OsString],
) -> io::Result<(PathBuf, usize, bool)> {
let mut path = root_prefix.to_path_buf();
let mut count = 0usize;
let mut symlink_seen = false;
if let Some(first) = components.first() {
if first == std::ffi::OsStr::new(".") {
return Ok((root_prefix.to_path_buf(), 1, false));
}
if first == std::ffi::OsStr::new("..") {
let parent_path = if let Some(parent) = root_prefix.parent() {
if !parent.as_os_str().is_empty() {
parent.to_path_buf()
} else {
root_prefix.to_path_buf()
}
} else {
root_prefix.to_path_buf()
};
return Ok((parent_path, 1, false));
}
path.push(first);
match fs::symlink_metadata(&path) {
Ok(meta) => {
if meta.file_type().is_symlink() {
symlink_seen = true;
path = resolve_simple_symlink_chain(&path)?;
}
count = 1;
}
Err(_) => {
return Ok((root_prefix.to_path_buf(), 0, false));
}
}
}
for c in components.iter().skip(count) {
if c == std::ffi::OsStr::new(".") {
count += 1;
continue;
}
if c == std::ffi::OsStr::new("..") {
#[cfg(all(target_os = "linux", feature = "proc-canonicalize"))]
{
use crate::symlink::is_proc_magic_link;
if is_proc_magic_link(&path) {
count += 1;
continue;
}
}
if let Some(parent) = path.parent() {
if !parent.as_os_str().is_empty() {
path = parent.to_path_buf();
}
}
count += 1;
continue;
}
path.push(c);
match fs::symlink_metadata(&path) {
Ok(meta) => {
if meta.file_type().is_symlink() {
symlink_seen = true;
let resolved = resolve_simple_symlink_chain(&path)?;
let adopt =
resolved.exists() || resolved.parent().map(|p| p.exists()).unwrap_or(false);
if adopt {
path = resolved;
} else {
}
}
count += 1;
}
Err(_) => {
let _ = path.pop();
break;
}
}
}
Ok((path, count, symlink_seen))
}