diff --git a/src/display.rs b/src/display.rs index 86667f5..3eb725d 100644 --- a/src/display.rs +++ b/src/display.rs @@ -159,7 +159,10 @@ pub fn format_string( ) -> String { let printable_name = { if display_data.short_paths { - dir_name.split('/').last().unwrap_or(dir_name) + dir_name + .split(std::path::is_separator) + .last() + .unwrap_or(dir_name) } else { dir_name } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index c8a5b34..864db6a 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -38,8 +38,9 @@ impl PartialEq for Node { } pub fn is_a_parent_of(parent: &str, child: &str) -> bool { - (child.starts_with(parent) && child.chars().nth(parent.chars().count()) == Some('/')) - || parent == "/" + let path_parent = std::path::Path::new(parent); + let path_child = std::path::Path::new(child); + (path_child.starts_with(path_parent) && !path_parent.starts_with(path_child)) } pub fn simplify_dir_names(filenames: Vec<&str>) -> HashSet { @@ -47,7 +48,7 @@ pub fn simplify_dir_names(filenames: Vec<&str>) -> HashSet { let mut to_remove: Vec = Vec::with_capacity(filenames.len()); for t in filenames { - let top_level_name = strip_end_slash(t); + let top_level_name = strip_trailing_curdirs(t); let mut can_add = true; for tt in top_level_names.iter() { @@ -61,7 +62,7 @@ pub fn simplify_dir_names(filenames: Vec<&str>) -> HashSet { top_level_names.retain(|tr| to_remove.binary_search(tr).is_err()); to_remove.clear(); if can_add { - top_level_names.insert(strip_end_slash(t).to_owned()); + top_level_names.insert(strip_trailing_curdirs(t).to_owned()); } } @@ -107,11 +108,13 @@ fn get_allowed_filesystems(top_level_names: &HashSet) -> Option &str { - while (new_name.ends_with('/') || new_name.ends_with("/.")) && new_name.len() > 1 { - new_name = &new_name[..new_name.len() - 1]; - } - new_name +pub fn strip_trailing_curdirs>(path: P) -> std::string::String { + // `Path.components()` normalizes the path (repeated separators, interior '.', and trailing slashes are removed); ref: + path.as_ref() + .components() + .collect::() + .to_string_lossy() + .to_string() } fn examine_dir( @@ -235,11 +238,12 @@ pub fn trim_deep_ones( let mut result: Vec<(String, u64)> = Vec::with_capacity(input.len() * top_level_names.len()); for name in top_level_names { - let my_max_depth = name.matches('/').count() + max_depth as usize; + let my_max_depth = name.matches(std::path::is_separator).count() + max_depth as usize; let name_ref: &str = name.as_ref(); for &(ref k, ref v) in input.iter() { - if k.starts_with(name_ref) && k.matches('/').count() <= my_max_depth { + if k.starts_with(name_ref) && k.matches(std::path::is_separator).count() <= my_max_depth + { result.push((k.clone(), *v)); } } @@ -300,6 +304,10 @@ mod tests { fn test_is_a_parent_of() { assert!(is_a_parent_of("/usr", "/usr/andy")); assert!(is_a_parent_of("/usr", "/usr/andy/i/am/descendant")); + assert!(!is_a_parent_of("/usr", "/usr/.")); + assert!(!is_a_parent_of("/usr", "/usr/")); + assert!(!is_a_parent_of("/usr", "/usr")); + assert!(!is_a_parent_of("/usr/", "/usr")); assert!(!is_a_parent_of("/usr/andy", "/usr")); assert!(!is_a_parent_of("/usr/andy", "/usr/sibling")); } @@ -308,5 +316,6 @@ mod tests { fn test_is_a_parent_of_root() { assert!(is_a_parent_of("/", "/usr/andy")); assert!(is_a_parent_of("/", "/usr")); + assert!(!is_a_parent_of("/", "/")); } }