-- File created: 2008-10-10 13:29:03moduleSystem.FilePath.Glob.Match(match)whereimportControl.Exception(assert)importData.Char(isDigit)importData.Monoid(mappend)importSystem.FilePath(isPathSeparator,isExtSeparator)importSystem.FilePath.Glob.BaseimportSystem.FilePath.Glob.Utils(dropLeadingZeroes,inRange,pathParts)-- |Matches the given 'Pattern' against the given 'FilePath', returning 'True'-- if the pattern matches and 'False' otherwise.match::Pattern->FilePath->Boolmatch=begMatch.unPattern-- begMatch takes care of some things at the beginning of a pattern or after /:-- - . needs to be matched explicitly-- - ./foo is equivalent to foobegMatch,match'::[Token]->FilePath->BoolbegMatch_"."=FalsebegMatch_".."=FalsebegMatch(ExtSeparator:PathSeparator:pat)s=begMatchpatsbegMatchpat(x:y:s)|isExtSeparatorx&&isPathSeparatory=begMatchpatsbegMatchpats=ifnot(nulls)&&isExtSeparator(heads)thencasepatofExtSeparator:pat'->match'pat'(tails)_->Falseelsematch'patsmatch'[]s=nullsmatch'(AnyNonPathSeparator:s)""=nullsmatch'_""=Falsematch'(Literall:xs)(c:cs)=l==c&&match'xscsmatch'(ExtSeparator:xs)(c:cs)=isExtSeparatorc&&match'xscsmatch'(PathSeparator:xs)(c:cs)=isPathSeparatorc&&begMatchxscsmatch'(NonPathSeparator:xs)(c:cs)=not(isPathSeparatorc)&&match'xscsmatch'(CharRangebrng:xs)(c:cs)=not(isPathSeparatorc)&&any(either(==c)(`inRange`c))rng==b&&match'xscsmatch'(OpenRangelohi:xs)path=let(lzNum,cs)=spanisDigitpathnum=dropLeadingZeroeslzNumnumChoices=tail.takeWhile(not.null.snd).map(flipsplitAtnum)$[0..]inifnulllzNumthenFalse-- no digitselse-- So, given the path "123foo" what we've got is:-- cs = "foo"-- num = "123"-- numChoices = [("1","23"),("12","3")]---- We want to try matching x against each of 123, 12, and 1.-- 12 and 1 are in numChoices already, but we need to add (num,"")-- manually.any(\(n,rest)->inOpenRangelohin&&match'xs(rest++cs))((num,""):numChoices)match'again@(AnyNonPathSeparator:xs)path@(c:cs)=match'xspath||(ifisPathSeparatorcthenFalseelsematch'againcs)match'again@(AnyDirectory:xs)path=letparts=pathPartspathmatches=any(match'xs)parts||any(match'again)(tailparts)inifnullxs-- **/ shouldn't match foo/.bar, so check that remaining bits don't-- start with .thenall(not.isExtSeparator.head)(initparts)&&matcheselsematchesmatch'(LongLiterallens:xs)path=let(pre,cs)=splitAtlenpathinpre==s&&match'xscs-- Does the actual open range matching: finds whether the third parameter-- is between the first two or not.---- It does this by keeping track of the Ordering so far (e.g. having-- looked at "12" and "34" the Ordering of the two would be LT: 12 < 34)-- and aborting if a String "runs out": a longer string is automatically-- greater.---- Assumes that the input strings contain only digits, and no leading zeroes.inOpenRange::MaybeString->MaybeString->String->BoolinOpenRangel_h_s_=assert(allisDigits_)$gol_h_s_EQEQwheregoNothingNothing___=True-- no boundsgo(Just[])_[]LT_=False-- lesser than lower boundgo_(Just[])__GT=False-- greater than upper boundgo_(Just[])(_:_)__=False-- longer than upper boundgo(Just(_:_))_[]__=False-- shorter than lower boundgo__[]__=Truego(Just(l:ls))(Just(h:hs))(c:cs)ordlordh=letordl'=ordl`mappend`compareclordh'=ordh`mappend`comparechingo(Justls)(Jusths)csordl'ordh'goNothing(Just(h:hs))(c:cs)_ordh=letordh'=ordh`mappend`comparechingoNothing(Jusths)csGTordh'go(Just(l:ls))Nothing(c:cs)ordl_=letordl'=ordl`mappend`compareclingo(Justls)Nothingcsordl'LT-- lower bound is shorter: s is greatergo(Just[])his_ordh=goNothinghisGTordh