mirror of
https://git.proxmox.com/git/pathpatterns
synced 2025-06-12 16:40:56 +00:00
Fix /**/ special case for beginning-of-text matching
Git actually skipped the next slash in the pattern. Since we don't have a Component::Slash currently we'll just add a flag to `do_match` instead. Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
f9fbdc3b94
commit
17b7bd9f3c
@ -396,14 +396,19 @@ impl Pattern {
|
|||||||
|
|
||||||
/// Check whether this pattern matches a text.
|
/// Check whether this pattern matches a text.
|
||||||
pub fn matches<T: AsRef<[u8]>>(&self, text: T) -> bool {
|
pub fn matches<T: AsRef<[u8]>>(&self, text: T) -> bool {
|
||||||
match self.do_matches(0, text.as_ref()) {
|
match self.do_matches(0, text.as_ref(), false) {
|
||||||
MatchResult::Match => true,
|
MatchResult::Match => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The algorithm is ported from git's wildmatch.c.
|
// The algorithm is ported from git's wildmatch.c.
|
||||||
fn do_matches(&self, mut ci: usize, mut text: &[u8]) -> MatchResult {
|
fn do_matches(
|
||||||
|
&self,
|
||||||
|
mut ci: usize,
|
||||||
|
mut text: &[u8],
|
||||||
|
mut skip_slash_in_literal: bool,
|
||||||
|
) -> MatchResult {
|
||||||
let components = &self.components[..];
|
let components = &self.components[..];
|
||||||
|
|
||||||
if self.flags.intersects(PatternFlag::PATH_NAME) {
|
if self.flags.intersects(PatternFlag::PATH_NAME) {
|
||||||
@ -411,6 +416,7 @@ impl Pattern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while ci != components.len() {
|
while ci != components.len() {
|
||||||
|
let skip_slash_in_literal = mem::replace(&mut skip_slash_in_literal, false);
|
||||||
//eprintln!("Matching: {:?} at text: {:?}", components[ci], unsafe {
|
//eprintln!("Matching: {:?} at text: {:?}", components[ci], unsafe {
|
||||||
// std::str::from_utf8_unchecked(text)
|
// std::str::from_utf8_unchecked(text)
|
||||||
//},);
|
//},);
|
||||||
@ -423,6 +429,12 @@ impl Pattern {
|
|||||||
return MatchResult::AbortAll;
|
return MatchResult::AbortAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let literal = if skip_slash_in_literal {
|
||||||
|
&literal[1..]
|
||||||
|
} else {
|
||||||
|
literal
|
||||||
|
};
|
||||||
|
|
||||||
if !starts_with(text, &literal, self.flags) {
|
if !starts_with(text, &literal, self.flags) {
|
||||||
return MatchResult::NoMatch;
|
return MatchResult::NoMatch;
|
||||||
}
|
}
|
||||||
@ -474,7 +486,7 @@ impl Pattern {
|
|||||||
// FIXME: Optimization: Add the "try to advance faster" optimization from
|
// FIXME: Optimization: Add the "try to advance faster" optimization from
|
||||||
// git here.
|
// git here.
|
||||||
|
|
||||||
match self.do_matches(ci + 1, text) {
|
match self.do_matches(ci + 1, text, false) {
|
||||||
MatchResult::NoMatch => {
|
MatchResult::NoMatch => {
|
||||||
if text[0] == b'/' {
|
if text[0] == b'/' {
|
||||||
return MatchResult::AbortToStarStar;
|
return MatchResult::AbortToStarStar;
|
||||||
@ -498,15 +510,8 @@ impl Pattern {
|
|||||||
{
|
{
|
||||||
// Assuming we matched `foo/` and are at `/` `**` `/`, see if we can let
|
// Assuming we matched `foo/` and are at `/` `**` `/`, see if we can let
|
||||||
// it match nothing, so that `foo/` `**` `/bar` can match `foo/bar`.
|
// it match nothing, so that `foo/` `**` `/bar` can match `foo/bar`.
|
||||||
//
|
|
||||||
// Under the condition that the previous component ended with a slash
|
|
||||||
// (`components[ci - 1].ends_with_slash()`) we can safely move back by
|
|
||||||
// a byte in `text`.
|
|
||||||
let text = unsafe {
|
|
||||||
std::slice::from_raw_parts(text.as_ptr().offset(-1), text.len() + 1)
|
|
||||||
};
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match self.do_matches(ci + 1, text) {
|
match self.do_matches(ci + 1, text, true) {
|
||||||
MatchResult::Match => return MatchResult::Match,
|
MatchResult::Match => return MatchResult::Match,
|
||||||
_ => (), // or just continue regularly
|
_ => (), // or just continue regularly
|
||||||
}
|
}
|
||||||
@ -519,7 +524,7 @@ impl Pattern {
|
|||||||
return MatchResult::AbortAll;
|
return MatchResult::AbortAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.do_matches(ci + 1, text) {
|
match self.do_matches(ci + 1, text, false) {
|
||||||
MatchResult::NoMatch => (),
|
MatchResult::NoMatch => (),
|
||||||
MatchResult::AbortToStarStar => (), // continue from here
|
MatchResult::AbortToStarStar => (), // continue from here
|
||||||
other => return other,
|
other => return other,
|
||||||
@ -713,4 +718,15 @@ fn test() {
|
|||||||
let pattern = Pattern::new("a/b**/c", PatternFlag::PATH_NAME).unwrap();
|
let pattern = Pattern::new("a/b**/c", PatternFlag::PATH_NAME).unwrap();
|
||||||
assert!(pattern.matches("a/bxx/c"));
|
assert!(pattern.matches("a/bxx/c"));
|
||||||
assert!(!pattern.matches("a/bxx/yy/c"));
|
assert!(!pattern.matches("a/bxx/yy/c"));
|
||||||
|
|
||||||
|
let pattern = Pattern::new("**/lost+found", PatternFlag::PATH_NAME).unwrap();
|
||||||
|
assert!(pattern.matches("/foo/lost+found"));
|
||||||
|
assert!(pattern.matches("foo/lost+found"));
|
||||||
|
assert!(pattern.matches("/lost+found"));
|
||||||
|
assert!(pattern.matches("///lost+found"));
|
||||||
|
assert!(pattern.matches("lost+found"));
|
||||||
|
assert!(!pattern.matches("lost+found2"));
|
||||||
|
assert!(!pattern.matches("lost+found/"));
|
||||||
|
assert!(!pattern.matches("xlost+found"));
|
||||||
|
assert!(!pattern.matches("xlost+found/"));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user