1
- // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
1
+ // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2
2
// file at the top-level directory of this distribution and at
3
3
// http://rust-lang.org/COPYRIGHT.
4
4
//
@@ -43,6 +43,7 @@ use std::path::is_sep;
43
43
pub struct Paths {
44
44
root : Path ,
45
45
dir_patterns : Vec < Pattern > ,
46
+ require_dir : bool ,
46
47
options : MatchOptions ,
47
48
todo : Vec < ( Path , uint ) > ,
48
49
}
@@ -51,7 +52,7 @@ pub struct Paths {
51
52
/// Return an iterator that produces all the Paths that match the given pattern,
52
53
/// which may be absolute or relative to the current working directory.
53
54
///
54
- /// is method uses the default match options and is equivalent to calling
55
+ /// This method uses the default match options and is equivalent to calling
55
56
/// `glob_with(pattern, MatchOptions::new())`. Use `glob_with` directly if you
56
57
/// want to use non-default match options.
57
58
///
@@ -106,6 +107,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Paths {
106
107
return Paths {
107
108
root : root,
108
109
dir_patterns : Vec :: new ( ) ,
110
+ require_dir : false ,
109
111
options : options,
110
112
todo : Vec :: new ( ) ,
111
113
} ;
@@ -117,13 +119,21 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Paths {
117
119
let dir_patterns = pattern. slice_from ( cmp:: min ( root_len, pattern. len ( ) ) )
118
120
. split_terminator ( is_sep)
119
121
. map ( |s| Pattern :: new ( s) )
120
- . collect ( ) ;
122
+ . collect :: < Vec < Pattern > > ( ) ;
123
+ let require_dir = pattern. chars ( ) . next_back ( ) . map ( is_sep) == Some ( true ) ;
121
124
122
- let todo = list_dir_sorted ( & root) . move_iter ( ) . map ( |x|( x, 0 u) ) . collect ( ) ;
125
+ let mut todo = Vec :: new ( ) ;
126
+ if dir_patterns. len ( ) > 0 {
127
+ // Shouldn't happen, but we're using -1 as a special index.
128
+ assert ! ( dir_patterns. len( ) < -1 as uint) ;
129
+
130
+ fill_todo ( & mut todo, dir_patterns. as_slice ( ) , 0 , & root, options) ;
131
+ }
123
132
124
133
Paths {
125
134
root : root,
126
135
dir_patterns : dir_patterns,
136
+ require_dir : require_dir,
127
137
options : options,
128
138
todo : todo,
129
139
}
@@ -138,6 +148,12 @@ impl Iterator<Path> for Paths {
138
148
}
139
149
140
150
let ( path, idx) = self . todo . pop ( ) . unwrap ( ) ;
151
+ // idx -1: was already checked by fill_todo, maybe path was '.' or
152
+ // '..' that we can't match here because of normalization.
153
+ if idx == -1 as uint {
154
+ if self . require_dir && !path. is_dir ( ) { continue ; }
155
+ return Some ( path) ;
156
+ }
141
157
let ref pattern = * self . dir_patterns . get ( idx) ;
142
158
143
159
if pattern. matches_with ( match path. filename_str ( ) {
@@ -152,23 +168,27 @@ impl Iterator<Path> for Paths {
152
168
if idx == self . dir_patterns . len ( ) - 1 {
153
169
// it is not possible for a pattern to match a directory *AND* its children
154
170
// so we don't need to check the children
155
- return Some ( path) ;
171
+
172
+ if !self . require_dir || path. is_dir ( ) {
173
+ return Some ( path) ;
174
+ }
156
175
} else {
157
- self . todo . extend ( list_dir_sorted ( & path) . move_iter ( ) . map ( |x|( x, idx+1 ) ) ) ;
176
+ fill_todo ( & mut self . todo , self . dir_patterns . as_slice ( ) ,
177
+ idx + 1 , & path, self . options ) ;
158
178
}
159
179
}
160
180
}
161
181
}
162
182
163
183
}
164
184
165
- fn list_dir_sorted ( path : & Path ) -> Vec < Path > {
185
+ fn list_dir_sorted ( path : & Path ) -> Option < Vec < Path > > {
166
186
match fs:: readdir ( path) {
167
187
Ok ( mut children) => {
168
188
children. sort_by ( |p1, p2| p2. filename ( ) . cmp ( & p1. filename ( ) ) ) ;
169
- children. move_iter ( ) . collect ( )
189
+ Some ( children. move_iter ( ) . collect ( ) )
170
190
}
171
- Err ( ..) => Vec :: new ( )
191
+ Err ( ..) => None
172
192
}
173
193
}
174
194
@@ -435,6 +455,72 @@ impl Pattern {
435
455
436
456
}
437
457
458
+ // Fills `todo` with paths under `path` to be matched by `patterns[idx]`,
459
+ // special-casing patterns to match `.` and `..`, and avoiding `readdir()`
460
+ // calls when there are no metacharacters in the pattern.
461
+ fn fill_todo ( todo : & mut Vec < ( Path , uint ) > , patterns : & [ Pattern ] , idx : uint , path : & Path ,
462
+ options : MatchOptions ) {
463
+ // convert a pattern that's just many Char(_) to a string
464
+ fn pattern_as_str ( pattern : & Pattern ) -> Option < ~str > {
465
+ let mut s = ~"";
466
+ for token in pattern. tokens . iter ( ) {
467
+ match * token {
468
+ Char ( c) => s. push_char ( c) ,
469
+ _ => return None
470
+ }
471
+ }
472
+ return Some ( s) ;
473
+ }
474
+
475
+ let add = |todo : & mut Vec < _ > , next_path : Path | {
476
+ if idx + 1 == patterns. len ( ) {
477
+ // We know it's good, so don't make the iterator match this path
478
+ // against the pattern again. In particular, it can't match
479
+ // . or .. globs since these never show up as path components.
480
+ todo. push ( ( next_path, -1 as uint ) ) ;
481
+ } else {
482
+ fill_todo ( todo, patterns, idx + 1 , & next_path, options) ;
483
+ }
484
+ } ;
485
+
486
+ let pattern = & patterns[ idx] ;
487
+
488
+ match pattern_as_str ( pattern) {
489
+ Some ( s) => {
490
+ // This pattern component doesn't have any metacharacters, so we
491
+ // don't need to read the current directory to know where to
492
+ // continue. So instead of passing control back to the iterator,
493
+ // we can just check for that one entry and potentially recurse
494
+ // right away.
495
+ let special = "." == s || ".." == s;
496
+ let next_path = path. join ( s) ;
497
+ if ( special && path. is_dir ( ) ) || ( !special && next_path. exists ( ) ) {
498
+ add ( todo, next_path) ;
499
+ }
500
+ } ,
501
+ None => {
502
+ match list_dir_sorted ( path) {
503
+ Some ( entries) => {
504
+ todo. extend ( entries. move_iter ( ) . map ( |x|( x, idx) ) ) ;
505
+
506
+ // Matching the special directory entries . and .. that refer to
507
+ // the current and parent directory respectively requires that
508
+ // the pattern has a leading dot, even if the `MatchOptions` field
509
+ // `require_literal_leading_dot` is not set.
510
+ if pattern. tokens . len ( ) > 0 && pattern. tokens . get ( 0 ) == & Char ( '.' ) {
511
+ for & special in [ "." , ".." ] . iter ( ) {
512
+ if pattern. matches_with ( special, options) {
513
+ add ( todo, path. join ( special) ) ;
514
+ }
515
+ }
516
+ }
517
+ }
518
+ None => { }
519
+ }
520
+ }
521
+ }
522
+ }
523
+
438
524
fn parse_char_specifiers ( s : & [ char ] ) -> Vec < CharSpecifier > {
439
525
let mut cs = Vec :: new ( ) ;
440
526
let mut i = 0 ;
@@ -567,7 +653,7 @@ mod test {
567
653
fn test_absolute_pattern ( ) {
568
654
// assume that the filesystem is not empty!
569
655
assert ! ( glob( "/*" ) . next( ) . is_some( ) ) ;
570
- assert ! ( glob( "//" ) . next( ) . is_none ( ) ) ;
656
+ assert ! ( glob( "//" ) . next( ) . is_some ( ) ) ;
571
657
572
658
// check windows absolute paths with host/device components
573
659
let root_with_device = os:: getcwd ( ) . root_path ( ) . unwrap ( ) . join ( "*" ) ;
0 commit comments