mirror of
https://github.com/veeso/termscp.git
synced 2026-04-01 07:42:17 -07:00
fix: resolve . and .. in terminal cd and prevent panic in path elide
`absolutize` now lexically normalizes paths so `cd ..` navigates to the parent directory instead of appending `..` literally. Also guard against `file_name()` returning `None` in `fmt_path_elide_ex`, which caused a panic on paths containing unresolved `..` components. Closes #402
This commit is contained in:
@@ -67,8 +67,10 @@ pub fn fmt_path_elide_ex(p: &Path, width: usize, extra_len: usize) -> String {
|
||||
// If ancestors_len is bigger than 3, push '…' and parent too
|
||||
if ancestors_len > 3 {
|
||||
elided_path.push("…");
|
||||
if let Some(parent) = p.ancestors().nth(1) {
|
||||
elided_path.push(parent.file_name().unwrap());
|
||||
if let Some(parent) = p.ancestors().nth(1)
|
||||
&& let Some(name) = parent.file_name()
|
||||
{
|
||||
elided_path.push(name);
|
||||
}
|
||||
}
|
||||
// Push file_name
|
||||
|
||||
@@ -12,14 +12,34 @@ use std::path::{Component, Path, PathBuf};
|
||||
/// assert_eq!(absolutize(&Path::new("/home/omar"), &Path::new("/tmp/readme.txt")).as_path(), Path::new("/tmp/readme.txt"));
|
||||
/// ```
|
||||
pub fn absolutize(wrkdir: &Path, target: &Path) -> PathBuf {
|
||||
match target.is_absolute() {
|
||||
let raw = match target.is_absolute() {
|
||||
true => target.to_path_buf(),
|
||||
false => {
|
||||
let mut p: PathBuf = wrkdir.to_path_buf();
|
||||
p.push(target);
|
||||
p
|
||||
}
|
||||
};
|
||||
normalize(&raw)
|
||||
}
|
||||
|
||||
/// Normalize a path by resolving `.` and `..` components lexically
|
||||
/// (without touching the filesystem).
|
||||
fn normalize(path: &Path) -> PathBuf {
|
||||
let mut parts: Vec<Component> = Vec::new();
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::ParentDir => {
|
||||
// Pop the last normal component; never pop past root
|
||||
if let Some(Component::Normal(_)) = parts.last() {
|
||||
parts.pop();
|
||||
}
|
||||
}
|
||||
Component::CurDir => {} // skip `.`
|
||||
other => parts.push(other),
|
||||
}
|
||||
}
|
||||
parts.iter().collect()
|
||||
}
|
||||
|
||||
/// This function will get the difference from path `path` to `base`. Basically will remove `base` from `path`
|
||||
@@ -99,6 +119,44 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn absolutize_resolves_parent_dir() {
|
||||
assert_eq!(
|
||||
absolutize(Path::new("/home/omar"), Path::new("..")).as_path(),
|
||||
Path::new("/home")
|
||||
);
|
||||
assert_eq!(
|
||||
absolutize(Path::new("/home/omar"), Path::new("../docs")).as_path(),
|
||||
Path::new("/home/docs")
|
||||
);
|
||||
assert_eq!(
|
||||
absolutize(Path::new("/home"), Path::new("..")).as_path(),
|
||||
Path::new("/")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn absolutize_resolves_current_dir() {
|
||||
assert_eq!(
|
||||
absolutize(Path::new("/home/omar"), Path::new(".")).as_path(),
|
||||
Path::new("/home/omar")
|
||||
);
|
||||
assert_eq!(
|
||||
absolutize(Path::new("/home/omar"), Path::new("./docs")).as_path(),
|
||||
Path::new("/home/omar/docs")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_path() {
|
||||
assert_eq!(normalize(Path::new("/a/b/../c")), Path::new("/a/c"));
|
||||
assert_eq!(normalize(Path::new("/a/b/./c")), Path::new("/a/b/c"));
|
||||
assert_eq!(normalize(Path::new("/a/b/c/../..")), Path::new("/a"));
|
||||
assert_eq!(normalize(Path::new("/")), Path::new("/"));
|
||||
// Parent beyond root stays at root
|
||||
assert_eq!(normalize(Path::new("/..")), Path::new("/"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calc_diff_paths() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user