Skip to content

Commit 16d166a

Browse files
committed
Add an option to not follow symlinks
Add `follows_link` option in `MatchOptions` struct to signalize when to follow symlinks when the directory is traversed. The default value is `false`.
1 parent fc308b1 commit 16d166a

File tree

1 file changed

+28
-7
lines changed

1 file changed

+28
-7
lines changed

src/lib.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
//! case_sensitive: false,
4848
//! require_literal_separator: false,
4949
//! require_literal_leading_dot: false,
50+
//! follows_link: false,
5051
//! };
5152
//! for entry in glob_with("local/*a*", &options).unwrap() {
5253
//! if let Ok(path) = entry {
@@ -299,8 +300,13 @@ impl fmt::Display for GlobError {
299300
}
300301
}
301302

302-
fn is_dir(p: &Path) -> bool {
303-
fs::metadata(p).map(|m| m.is_dir()).unwrap_or(false)
303+
fn is_dir(p: &Path, follows_link: bool) -> bool {
304+
let metadata = if follows_link {
305+
fs::metadata(p)
306+
} else {
307+
fs::symlink_metadata(p)
308+
};
309+
metadata.map(|m| m.is_dir()).unwrap_or(false)
304310
}
305311

306312
/// An alias for a glob iteration result.
@@ -343,7 +349,8 @@ impl Iterator for Paths {
343349
// idx -1: was already checked by fill_todo, maybe path was '.' or
344350
// '..' that we can't match here because of normalization.
345351
if idx == !0 as usize {
346-
if self.require_dir && !is_dir(&path) {
352+
if self.require_dir && !is_dir(&path,
353+
self.options.follows_link) {
347354
continue;
348355
}
349356
return Some(Ok(path));
@@ -358,7 +365,7 @@ impl Iterator for Paths {
358365
next += 1;
359366
}
360367

361-
if is_dir(&path) {
368+
if is_dir(&path, self.options.follows_link) {
362369
// the path is a directory, so it's a match
363370

364371
// push this directory's contents
@@ -401,7 +408,8 @@ impl Iterator for Paths {
401408
// *AND* its children so we don't need to check the
402409
// children
403410

404-
if !self.require_dir || is_dir(&path) {
411+
if !self.require_dir || is_dir(&path,
412+
self.options.follows_link) {
405413
return Some(Ok(path));
406414
}
407415
} else {
@@ -804,7 +812,7 @@ fn fill_todo(todo: &mut Vec<Result<(PathBuf, usize), GlobError>>,
804812
};
805813

806814
let pattern = &patterns[idx];
807-
let is_dir = is_dir(path);
815+
let is_dir = is_dir(path, options.follows_link);
808816
let curdir = path == Path::new(".");
809817
match pattern_as_str(pattern) {
810818
Some(s) => {
@@ -957,6 +965,10 @@ pub struct MatchOptions {
957965
/// conventionally considered hidden on Unix systems and it might be
958966
/// desirable to skip them when listing files.
959967
pub require_literal_leading_dot: bool,
968+
969+
/// Whether ot not paths that are symbolic links are followed
970+
/// during the walk.
971+
pub follows_link: bool,
960972
}
961973

962974
impl MatchOptions {
@@ -970,14 +982,16 @@ impl MatchOptions {
970982
/// MatchOptions {
971983
/// case_sensitive: true,
972984
/// require_literal_separator: false,
973-
/// require_literal_leading_dot: false
985+
/// require_literal_leading_dot: false,
986+
/// follows_link: false,
974987
/// }
975988
/// ```
976989
pub fn new() -> MatchOptions {
977990
MatchOptions {
978991
case_sensitive: true,
979992
require_literal_separator: false,
980993
require_literal_leading_dot: false,
994+
follows_link: false,
981995
}
982996
}
983997
}
@@ -1219,6 +1233,7 @@ mod test {
12191233
case_sensitive: false,
12201234
require_literal_separator: false,
12211235
require_literal_leading_dot: false,
1236+
follows_link: false,
12221237
};
12231238

12241239
assert!(pat.matches_with("aBcDeFg", &options));
@@ -1237,11 +1252,13 @@ mod test {
12371252
case_sensitive: false,
12381253
require_literal_separator: false,
12391254
require_literal_leading_dot: false,
1255+
follows_link: false,
12401256
};
12411257
let options_case_sensitive = MatchOptions {
12421258
case_sensitive: true,
12431259
require_literal_separator: false,
12441260
require_literal_leading_dot: false,
1261+
follows_link: false,
12451262
};
12461263

12471264
assert!(pat_within.matches_with("a", &options_case_insensitive));
@@ -1260,11 +1277,13 @@ mod test {
12601277
case_sensitive: true,
12611278
require_literal_separator: true,
12621279
require_literal_leading_dot: false,
1280+
follows_link: false,
12631281
};
12641282
let options_not_require_literal = MatchOptions {
12651283
case_sensitive: true,
12661284
require_literal_separator: false,
12671285
require_literal_leading_dot: false,
1286+
follows_link: false,
12681287
};
12691288

12701289
assert!(Pattern::new("abc/def").unwrap().matches_with("abc/def", &options_require_literal));
@@ -1299,11 +1318,13 @@ mod test {
12991318
case_sensitive: true,
13001319
require_literal_separator: false,
13011320
require_literal_leading_dot: true,
1321+
follows_link: false,
13021322
};
13031323
let options_not_require_literal_leading_dot = MatchOptions {
13041324
case_sensitive: true,
13051325
require_literal_separator: false,
13061326
require_literal_leading_dot: false,
1327+
follows_link: false,
13071328
};
13081329

13091330
let f = |options| Pattern::new("*.txt").unwrap().matches_with(".hello.txt", options);

0 commit comments

Comments
 (0)