2626#![ cfg_attr( all( test, windows) , feature( std_misc) ) ]
2727
2828use std:: ascii:: AsciiExt ;
29- use std:: cell:: Cell ;
3029use std:: cmp;
3130use std:: fmt;
3231use std:: fs;
@@ -614,7 +613,7 @@ impl Pattern {
614613 /// Return if the given `str` matches this `Pattern` using the specified
615614 /// match options.
616615 pub fn matches_with ( & self , str : & str , options : & MatchOptions ) -> bool {
617- self . matches_from ( None , str, 0 , options) == Match
616+ self . matches_from ( true , str. chars ( ) , 0 , options) == Match
618617 }
619618
620619 /// Return if the given `Path`, when converted to a `str`, matches this
@@ -630,81 +629,67 @@ impl Pattern {
630629 pub fn as_str < ' a > ( & ' a self ) -> & ' a str { & self . original }
631630
632631 fn matches_from ( & self ,
633- prev_char : Option < char > ,
634- mut file : & str ,
632+ mut follows_separator : bool ,
633+ mut file : std :: str:: Chars ,
635634 i : usize ,
636635 options : & MatchOptions ) -> MatchResult {
637636
638- let prev_char = Cell :: new ( prev_char) ;
639-
640- let require_literal = |c| {
641- ( options. require_literal_separator && path:: is_separator ( c) ) ||
642- ( options. require_literal_leading_dot && c == '.'
643- && path:: is_separator ( prev_char. get ( ) . unwrap_or ( '/' ) ) )
644- } ;
645-
646637 for ( ti, token) in self . tokens [ i..] . iter ( ) . enumerate ( ) {
647638 match * token {
648- AnySequence | AnyRecursiveSequence => {
649- loop {
650- match self . matches_from ( prev_char. get ( ) , file,
651- i + ti + 1 , options) {
639+ AnySequence |AnyRecursiveSequence => {
640+ // ** must be at the start.
641+ debug_assert ! ( match * token { AnyRecursiveSequence => follows_separator, _ => true } ) ;
642+
643+ // Empty match
644+ match self . matches_from ( follows_separator, file. clone ( ) , i + ti + 1 , options) {
645+ SubPatternDoesntMatch => ( ) , // keep trying
646+ m => return m,
647+ } ;
648+
649+ while let Some ( c) = file. next ( ) {
650+ if follows_separator && options. require_literal_leading_dot && c == '.' {
651+ return SubPatternDoesntMatch ;
652+ }
653+ follows_separator = path:: is_separator ( c) ;
654+ match * token {
655+ AnyRecursiveSequence if !follows_separator => continue ,
656+ AnySequence if options. require_literal_separator && follows_separator => return SubPatternDoesntMatch ,
657+ _ => ( ) ,
658+ }
659+ match self . matches_from ( follows_separator, file. clone ( ) , i + ti + 1 , options) {
652660 SubPatternDoesntMatch => ( ) , // keep trying
653661 m => return m,
654662 }
655-
656- if file. len ( ) == 0 { return EntirePatternDoesntMatch }
657- let c = file. chars ( ) . next ( ) . unwrap ( ) ;
658- let next = & file[ c. len_utf8 ( ) ..] ;
659-
660- if let AnySequence = * token {
661- if require_literal ( c) {
662- return SubPatternDoesntMatch ;
663- }
664- }
665-
666- prev_char. set ( Some ( c) ) ;
667- file = next;
668663 }
669- }
664+ } ,
670665 _ => {
671- if file. len ( ) == 0 { return EntirePatternDoesntMatch }
672- let c = file. chars ( ) . next ( ) . unwrap ( ) ;
673- let next = & file[ c. len_utf8 ( ) ..] ;
674-
675- let matches = match * token {
676- AnyChar => {
677- !require_literal ( c)
678- }
679- AnyWithin ( ref specifiers) => {
680- !require_literal ( c) &&
681- in_char_specifiers ( & specifiers,
682- c,
683- options)
684- }
685- AnyExcept ( ref specifiers) => {
686- !require_literal ( c) &&
687- !in_char_specifiers ( & specifiers,
688- c,
689- options)
690- }
691- Char ( c2) => {
692- chars_eq ( c, c2, options. case_sensitive )
693- }
694- AnySequence | AnyRecursiveSequence => {
695- unreachable ! ( )
696- }
666+ let c = match file. next ( ) {
667+ Some ( c) => c,
668+ None => return EntirePatternDoesntMatch ,
697669 } ;
698- if !matches {
670+
671+ let is_sep = path:: is_separator ( c) ;
672+
673+ if !match * token {
674+ AnyChar |AnyWithin ( ..) |AnyExcept ( ..)
675+ if ( options. require_literal_separator && is_sep)
676+ || ( follows_separator && options. require_literal_leading_dot && c == '.' )
677+ => false ,
678+ AnyChar => true ,
679+ AnyWithin ( ref specifiers) => in_char_specifiers ( & specifiers, c, options) ,
680+ AnyExcept ( ref specifiers) => !in_char_specifiers ( & specifiers, c, options) ,
681+ Char ( c2) => chars_eq ( c, c2, options. case_sensitive ) ,
682+ AnySequence | AnyRecursiveSequence => unreachable ! ( ) ,
683+ } {
699684 return SubPatternDoesntMatch ;
700685 }
701- prev_char. set ( Some ( c) ) ;
702- file = next;
686+ follows_separator = is_sep;
703687 }
704688 }
705689 }
706690
707- if file. is_empty ( ) {
691+ // Iter is fused.
692+ if file. next ( ) . is_none ( ) {
708693 Match
709694 } else {
710695 SubPatternDoesntMatch
@@ -885,7 +870,7 @@ pub struct MatchOptions {
885870 pub require_literal_separator : bool ,
886871
887872 /// If this is true then paths that contain components that start with a `.`
888- /// will not match unless the `.` appears literally in the pattern: `*`, `?`
873+ /// will not match unless the `.` appears literally in the pattern: `*`, `?`, `**`,
889874 /// or `[...]` will not match. This is useful because such files are
890875 /// conventionally considered hidden on Unix systems and it might be
891876 /// desirable to skip them when listing files.
@@ -1022,7 +1007,14 @@ mod test {
10221007 assert ! ( !pat. matches( "some/other/notthis.txt" ) ) ;
10231008
10241009 // a single ** should be valid, for globs
1025- assert ! ( Pattern :: new( "**" ) . unwrap( ) . is_recursive) ;
1010+ // Should accept anything
1011+ let pat = Pattern :: new ( "**" ) . unwrap ( ) ;
1012+ assert ! ( pat. is_recursive) ;
1013+ assert ! ( pat. matches( "abcde" ) ) ;
1014+ assert ! ( pat. matches( "" ) ) ;
1015+ assert ! ( pat. matches( ".asdf" ) ) ;
1016+ assert ! ( pat. matches( "/x/.asdf" ) ) ;
1017+
10261018
10271019 // collapse consecutive wildcards
10281020 let pat = Pattern :: new ( "some/**/**/needle.txt" ) . unwrap ( ) ;
@@ -1045,6 +1037,13 @@ mod test {
10451037 assert ! ( pat. matches( "/test" ) ) ;
10461038 assert ! ( !pat. matches( "/one/notthis" ) ) ;
10471039 assert ! ( !pat. matches( "/notthis" ) ) ;
1040+
1041+ // Only start sub-patterns on start of path segment.
1042+ let pat = Pattern :: new ( "**/.*" ) . unwrap ( ) ;
1043+ assert ! ( pat. matches( ".abc" ) ) ;
1044+ assert ! ( pat. matches( "abc/.abc" ) ) ;
1045+ assert ! ( !pat. matches( "ab.c" ) ) ;
1046+ assert ! ( !pat. matches( "abc/ab.c" ) ) ;
10481047 }
10491048
10501049 #[ test]
@@ -1234,6 +1233,10 @@ mod test {
12341233 let f = |options| Pattern :: new ( "aaa/[.]bbb" ) . unwrap ( ) . matches_with ( "aaa/.bbb" , options) ;
12351234 assert ! ( f( & options_not_require_literal_leading_dot) ) ;
12361235 assert ! ( !f( & options_require_literal_leading_dot) ) ;
1236+
1237+ let f = |options| Pattern :: new ( "**/*" ) . unwrap ( ) . matches_with ( ".bbb" , options) ;
1238+ assert ! ( f( & options_not_require_literal_leading_dot) ) ;
1239+ assert ! ( !f( & options_require_literal_leading_dot) ) ;
12371240 }
12381241
12391242 #[ test]
0 commit comments