26
26
#![ cfg_attr( all( test, windows) , feature( std_misc) ) ]
27
27
28
28
use std:: ascii:: AsciiExt ;
29
- use std:: cell:: Cell ;
30
29
use std:: cmp;
31
30
use std:: fmt;
32
31
use std:: fs;
@@ -614,7 +613,7 @@ impl Pattern {
614
613
/// Return if the given `str` matches this `Pattern` using the specified
615
614
/// match options.
616
615
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
618
617
}
619
618
620
619
/// Return if the given `Path`, when converted to a `str`, matches this
@@ -630,81 +629,67 @@ impl Pattern {
630
629
pub fn as_str < ' a > ( & ' a self ) -> & ' a str { & self . original }
631
630
632
631
fn matches_from ( & self ,
633
- prev_char : Option < char > ,
634
- mut file : & str ,
632
+ mut follows_separator : bool ,
633
+ mut file : std :: str:: Chars ,
635
634
i : usize ,
636
635
options : & MatchOptions ) -> MatchResult {
637
636
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
-
646
637
for ( ti, token) in self . tokens [ i..] . iter ( ) . enumerate ( ) {
647
638
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) {
652
660
SubPatternDoesntMatch => ( ) , // keep trying
653
661
m => return m,
654
662
}
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;
668
663
}
669
- }
664
+ } ,
670
665
_ => {
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 ,
697
669
} ;
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
+ } {
699
684
return SubPatternDoesntMatch ;
700
685
}
701
- prev_char. set ( Some ( c) ) ;
702
- file = next;
686
+ follows_separator = is_sep;
703
687
}
704
688
}
705
689
}
706
690
707
- if file. is_empty ( ) {
691
+ // Iter is fused.
692
+ if file. next ( ) . is_none ( ) {
708
693
Match
709
694
} else {
710
695
SubPatternDoesntMatch
@@ -885,7 +870,7 @@ pub struct MatchOptions {
885
870
pub require_literal_separator : bool ,
886
871
887
872
/// 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: `*`, `?`, `**`,
889
874
/// or `[...]` will not match. This is useful because such files are
890
875
/// conventionally considered hidden on Unix systems and it might be
891
876
/// desirable to skip them when listing files.
@@ -1022,7 +1007,14 @@ mod test {
1022
1007
assert ! ( !pat. matches( "some/other/notthis.txt" ) ) ;
1023
1008
1024
1009
// 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
+
1026
1018
1027
1019
// collapse consecutive wildcards
1028
1020
let pat = Pattern :: new ( "some/**/**/needle.txt" ) . unwrap ( ) ;
@@ -1045,6 +1037,13 @@ mod test {
1045
1037
assert ! ( pat. matches( "/test" ) ) ;
1046
1038
assert ! ( !pat. matches( "/one/notthis" ) ) ;
1047
1039
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" ) ) ;
1048
1047
}
1049
1048
1050
1049
#[ test]
@@ -1234,6 +1233,10 @@ mod test {
1234
1233
let f = |options| Pattern :: new ( "aaa/[.]bbb" ) . unwrap ( ) . matches_with ( "aaa/.bbb" , options) ;
1235
1234
assert ! ( f( & options_not_require_literal_leading_dot) ) ;
1236
1235
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) ) ;
1237
1240
}
1238
1241
1239
1242
#[ test]
0 commit comments