diff --git a/src/header/fnmatch/mod.rs b/src/header/fnmatch/mod.rs index 4d267d00a7..429b1d15a9 100644 --- a/src/header/fnmatch/mod.rs +++ b/src/header/fnmatch/mod.rs @@ -28,6 +28,11 @@ pub const FNM_PERIOD: c_int = 4; pub const FNM_CASEFOLD: c_int = 8; /// Equivalent to `FNM_CASEFOLD`. pub const FNM_IGNORECASE: c_int = FNM_CASEFOLD; +/// Equivalent to `FNM_PATHNAME`. GNU extension. +pub const FNM_FILE_NAME: c_int = FNM_PATHNAME; +/// The pattern may match a leading directory of the input string, i.e. +/// `foo` matches `foo/bar`. GNU extension. +pub const FNM_LEADING_DIR: c_int = 16; // TODO: FNM_EXTMATCH (Non-POSIX) unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Tree { @@ -160,10 +165,21 @@ pub unsafe extern "C" fn fnmatch( let tokens = unsafe { tokenize(pattern.cast::(), flags) }; - if PosixRegex::new(Cow::Owned(tokens)) - .case_insensitive(flags & FNM_CASEFOLD == FNM_CASEFOLD) - .matches_exact(input) - .is_some() + let case_insensitive = flags & FNM_CASEFOLD == FNM_CASEFOLD; + let regex = PosixRegex::new(Cow::Owned(tokens)).case_insensitive(case_insensitive); + + // FNM_LEADING_DIR: the pattern may match a leading directory of the input + // rather than the whole of it, so `foo` matches `foo/bar`. Try the exact + // match first, then each prefix that ends immediately before a '/'. + let matched = regex.clone().matches_exact(input).is_some() + || (flags & FNM_LEADING_DIR == FNM_LEADING_DIR + && input + .iter() + .enumerate() + .filter(|&(_, b)| *b == b'/') + .any(|(i, _)| regex.clone().matches_exact(&input[..i]).is_some())); + + if matched { 0 } else {