diff --git a/CHANGES.md b/CHANGES.md index 5f0367be..9fa84109 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,8 @@ Unreleased * Add the `[:alpha:]` character class in `Re.Perl` (#169) * Double asterisk (`**`) in `Re.Glob` (#172) Like `*` but also match `/` characters when `pathname` is set. +* Double asterisk should match 0 or more directories unless in trailing + position. (#192, fixes #185) 1.9.0 (05-Apr-2019) ------------------- diff --git a/lib/glob.ml b/lib/glob.ml index a0d95f06..7cd3894d 100644 --- a/lib/glob.ml +++ b/lib/glob.ml @@ -48,6 +48,26 @@ let of_string ~double_asterisk s : t = r in + (** + [read_ahead pattern] will attempt to read [pattern] and will return [true] if it was successful. + If it fails, it will return [false] and not increment the read index. + *) + let read_ahead pattern = + let pattern_len = String.length pattern in + (* if the pattern we are looking for exeeds the remaining length of s, return false immediately *) + if !i + pattern_len >= l then + false + else + try + for j = 0 to pattern_len - 1 do + let found = not (eos ()) && s.[!i + j] = pattern.[j] in + if not found then raise_no_trace Exit; + done; + i := !i + pattern_len; + true + with | Exit -> false + in + let char () = ignore (read '\\' : bool); if eos () then raise Parse_error; @@ -75,7 +95,9 @@ let of_string ~double_asterisk s : t = in let piece () = - if read '*' + if double_asterisk && read_ahead "/**" && not (eos ()) + then ManyMany + else if read '*' then if double_asterisk && read '*' then ManyMany else Many diff --git a/lib_test/test_glob.ml b/lib_test/test_glob.ml index cf0bb067..21aacd07 100644 --- a/lib_test/test_glob.ml +++ b/lib_test/test_glob.ml @@ -98,9 +98,20 @@ let _ = assert (re_match (glob ~anchored "foo/**/bar") "foo/far/oof/bar"); assert (re_match (glob ~anchored "foo/**bar") "foo/far/oofbar"); assert (re_match (glob ~anchored "foo/**bar") "foo/bar"); + assert (re_match (glob ~anchored "foo/**bar") "foo/foobar"); assert (re_match (glob ~anchored "/**") "//foo"); assert (re_match (glob ~anchored "**") "foo//bar"); + assert (re_match (glob ~anchored "foo/bar/**/*.ml") "foo/bar/baz/foobar.ml"); + assert (re_match (glob ~anchored "foo/bar/**/*.ml") "foo/bar/foobar.ml"); + + assert (re_match (glob ~anchored "foo/**/bar/**/baz") "foo/bar/baz"); + assert (re_match (glob ~anchored "foo/**/bar/**/baz") "foo/bar/x/y/z/baz"); + assert (re_match (glob ~anchored "foo/**/bar/**/baz") "foo/x/y/z/bar/baz"); + assert (re_match (glob ~anchored "foo/**/bar/**/baz") "foo/bar/x/bar/x/baz"); + assert (re_mismatch (glob ~anchored "foo/**/bar/**/baz") "foo/bar/../x/baz"); + assert (re_mismatch (glob ~anchored "foo/**/bar/**/baz") "foo/bar/./x/baz"); + (* Interaction with [~period] *) let period = true in assert (re_mismatch (glob ~anchored ~period "**") ".foobar");