mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-29 06:13:20 +00:00
Add tests for normalize_path and fix some bugs
This commit is contained in:
parent
33fd679f68
commit
dea18b34aa
2 changed files with 73 additions and 15 deletions
|
@ -6,6 +6,7 @@ mod wrealpath;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
use crate::wchar::{wstr, WString};
|
||||||
pub(crate) use format::printf::sprintf;
|
pub(crate) use format::printf::sprintf;
|
||||||
pub(crate) use gettext::{wgettext, wgettext_fmt};
|
pub(crate) use gettext::{wgettext, wgettext_fmt};
|
||||||
pub use normalize_path::*;
|
pub use normalize_path::*;
|
||||||
|
@ -27,3 +28,30 @@ pub fn perror(s: &str) {
|
||||||
let _ = stderr.write_all(slice);
|
let _ = stderr.write_all(slice);
|
||||||
let _ = stderr.write_all(b"\n");
|
let _ = stderr.write_all(b"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Joins strings with a separator.
|
||||||
|
pub fn join_strings(strs: &[&wstr], sep: char) -> WString {
|
||||||
|
if strs.is_empty() {
|
||||||
|
return WString::new();
|
||||||
|
}
|
||||||
|
let capacity = strs.iter().fold(0, |acc, s| acc + s.len()) + strs.len() - 1;
|
||||||
|
let mut result = WString::with_capacity(capacity);
|
||||||
|
for (i, s) in strs.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
result.push(sep);
|
||||||
|
}
|
||||||
|
result.push_utfstr(s);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_join_strings() {
|
||||||
|
use crate::wchar::L;
|
||||||
|
assert_eq!(join_strings(&[], '/'), "");
|
||||||
|
assert_eq!(join_strings(&[L!("foo")], '/'), "foo");
|
||||||
|
assert_eq!(
|
||||||
|
join_strings(&[L!("foo"), L!("bar"), L!("baz")], '/'),
|
||||||
|
"foo/bar/baz"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
use std::iter::repeat;
|
|
||||||
|
|
||||||
use crate::wchar::{wstr, WString, L};
|
use crate::wchar::{wstr, WString, L};
|
||||||
|
use crate::wutil::join_strings;
|
||||||
|
|
||||||
|
/// Given an input path, "normalize" it:
|
||||||
|
/// 1. Collapse multiple /s into a single /, except maybe at the beginning.
|
||||||
|
/// 2. .. goes up a level.
|
||||||
|
/// 3. Remove /./ in the middle.
|
||||||
pub fn normalize_path(path: &wstr, allow_leading_double_slashes: bool) -> WString {
|
pub fn normalize_path(path: &wstr, allow_leading_double_slashes: bool) -> WString {
|
||||||
// Count the leading slashes.
|
// Count the leading slashes.
|
||||||
let sep = '/';
|
let sep = '/';
|
||||||
let mut leading_slashes: usize = 0;
|
let mut leading_slashes: usize = 0;
|
||||||
for (i, &c) in path.as_char_slice().iter().enumerate() {
|
for c in path.chars() {
|
||||||
if c != sep {
|
if c != sep {
|
||||||
leading_slashes = i;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
leading_slashes += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let comps = path
|
let comps = path
|
||||||
|
@ -20,11 +23,11 @@ pub fn normalize_path(path: &wstr, allow_leading_double_slashes: bool) -> WStrin
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut new_comps = Vec::new();
|
let mut new_comps = Vec::new();
|
||||||
for comp in comps {
|
for comp in comps {
|
||||||
if comp.is_empty() || comp == L!(".") {
|
if comp.is_empty() || comp == "." {
|
||||||
continue;
|
continue;
|
||||||
} else if comp != L!("..") {
|
} else if comp != ".." {
|
||||||
new_comps.push(comp);
|
new_comps.push(comp);
|
||||||
} else if !new_comps.is_empty() && new_comps.last().map_or(L!(""), |&s| s) != L!("..") {
|
} else if !new_comps.is_empty() && new_comps.last().unwrap() != ".." {
|
||||||
// '..' with a real path component, drop that path component.
|
// '..' with a real path component, drop that path component.
|
||||||
new_comps.pop();
|
new_comps.pop();
|
||||||
} else if leading_slashes == 0 {
|
} else if leading_slashes == 0 {
|
||||||
|
@ -32,12 +35,7 @@ pub fn normalize_path(path: &wstr, allow_leading_double_slashes: bool) -> WStrin
|
||||||
new_comps.push(L!(".."));
|
new_comps.push(L!(".."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut result = new_comps.into_iter().fold(Vec::new(), |mut acc, x| {
|
let mut result = join_strings(&new_comps, sep);
|
||||||
acc.extend_from_slice(x.as_char_slice());
|
|
||||||
acc.push('/');
|
|
||||||
acc
|
|
||||||
});
|
|
||||||
result.pop();
|
|
||||||
// If we don't allow leading double slashes, collapse them to 1 if there are any.
|
// If we don't allow leading double slashes, collapse them to 1 if there are any.
|
||||||
let mut numslashes = if leading_slashes > 0 { 1 } else { 0 };
|
let mut numslashes = if leading_slashes > 0 { 1 } else { 0 };
|
||||||
// If we do, prepend one or two leading slashes.
|
// If we do, prepend one or two leading slashes.
|
||||||
|
@ -45,10 +43,42 @@ pub fn normalize_path(path: &wstr, allow_leading_double_slashes: bool) -> WStrin
|
||||||
if allow_leading_double_slashes && leading_slashes == 2 {
|
if allow_leading_double_slashes && leading_slashes == 2 {
|
||||||
numslashes = 2;
|
numslashes = 2;
|
||||||
}
|
}
|
||||||
result.splice(0..0, repeat(sep).take(numslashes));
|
for _ in 0..numslashes {
|
||||||
|
result.insert(0, sep);
|
||||||
|
}
|
||||||
// Ensure ./ normalizes to . and not empty.
|
// Ensure ./ normalizes to . and not empty.
|
||||||
if result.is_empty() {
|
if result.is_empty() {
|
||||||
result.push('.');
|
result.push('.');
|
||||||
}
|
}
|
||||||
WString::from_chars(result)
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_normalize_path() {
|
||||||
|
fn norm_path(path: &wstr) -> WString {
|
||||||
|
normalize_path(path, true)
|
||||||
|
}
|
||||||
|
assert_eq!(norm_path(L!("")), ".");
|
||||||
|
assert_eq!(norm_path(L!("..")), "..");
|
||||||
|
assert_eq!(norm_path(L!("./")), ".");
|
||||||
|
assert_eq!(norm_path(L!("./.")), ".");
|
||||||
|
assert_eq!(norm_path(L!("/")), "/");
|
||||||
|
assert_eq!(norm_path(L!("//")), "//");
|
||||||
|
assert_eq!(norm_path(L!("///")), "/");
|
||||||
|
assert_eq!(norm_path(L!("////")), "/");
|
||||||
|
assert_eq!(norm_path(L!("/.///")), "/");
|
||||||
|
assert_eq!(norm_path(L!(".//")), ".");
|
||||||
|
assert_eq!(norm_path(L!("/.//../")), "/");
|
||||||
|
assert_eq!(norm_path(L!("////abc")), "/abc");
|
||||||
|
assert_eq!(norm_path(L!("/abc")), "/abc");
|
||||||
|
assert_eq!(norm_path(L!("/abc/")), "/abc");
|
||||||
|
assert_eq!(norm_path(L!("/abc/..def/")), "/abc/..def");
|
||||||
|
assert_eq!(norm_path(L!("//abc/../def/")), "//def");
|
||||||
|
assert_eq!(norm_path(L!("abc/../abc/../abc/../abc")), "abc");
|
||||||
|
assert_eq!(norm_path(L!("../../")), "../..");
|
||||||
|
assert_eq!(norm_path(L!("foo/./bar")), "foo/bar");
|
||||||
|
assert_eq!(norm_path(L!("foo/../")), ".");
|
||||||
|
assert_eq!(norm_path(L!("foo/../foo")), "foo");
|
||||||
|
assert_eq!(norm_path(L!("foo/../foo/")), "foo");
|
||||||
|
assert_eq!(norm_path(L!("foo/././bar/.././baz")), "foo/baz");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue