@@ -20510,6 +20510,8 @@ function Minimatch (pattern, options) {
2051020510 }
2051120511
2051220512 this.options = options
20513+ this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined
20514+ ? options.maxGlobstarRecursion : 200
2051320515 this.set = []
2051420516 this.pattern = pattern
2051520517 this.regexp = null
@@ -20758,6 +20760,9 @@ function parse (pattern, isSub) {
2075820760 continue
2075920761 }
2076020762
20763+ // coalesce consecutive non-globstar * characters
20764+ if (c === '*' && stateChar === '*') continue
20765+
2076120766 // if we already have a stateChar, then it means
2076220767 // that there was something like ** or +? in there.
2076320768 // Handle the stateChar, then proceed with this one.
@@ -21152,109 +21157,173 @@ Minimatch.prototype.match = function match (f, partial) {
2115221157// out of pattern, then that's fine, as long as all
2115321158// the parts match.
2115421159Minimatch.prototype.matchOne = function (file, pattern, partial) {
21155- var options = this.options
21160+ if (pattern.indexOf(GLOBSTAR) !== -1) {
21161+ return this._matchGlobstar(file, pattern, partial, 0, 0)
21162+ }
21163+ return this._matchOne(file, pattern, partial, 0, 0)
21164+ }
2115621165
21157- this.debug('matchOne',
21158- { 'this': this, file: file, pattern: pattern })
21166+ Minimatch.prototype._matchGlobstar = function (file, pattern, partial, fileIndex, patternIndex) {
21167+ var i
2115921168
21160- this.debug('matchOne', file.length, pattern.length)
21169+ // find first globstar from patternIndex
21170+ var firstgs = -1
21171+ for (i = patternIndex; i < pattern.length; i++) {
21172+ if (pattern[i] === GLOBSTAR) { firstgs = i; break }
21173+ }
2116121174
21162- for (var fi = 0,
21163- pi = 0,
21164- fl = file.length,
21165- pl = pattern.length
21166- ; (fi < fl) && (pi < pl)
21167- ; fi++, pi++) {
21168- this.debug('matchOne loop')
21169- var p = pattern[pi]
21170- var f = file[fi]
21175+ // find last globstar
21176+ var lastgs = -1
21177+ for (i = pattern.length - 1; i >= 0; i--) {
21178+ if (pattern[i] === GLOBSTAR) { lastgs = i; break }
21179+ }
2117121180
21172- this.debug(pattern, p, f)
21181+ var head = pattern.slice(patternIndex, firstgs)
21182+ var body = partial ? pattern.slice(firstgs + 1) : pattern.slice(firstgs + 1, lastgs)
21183+ var tail = partial ? [] : pattern.slice(lastgs + 1)
2117321184
21174- // should be impossible.
21175- // some invalid regexp stuff in the set.
21176- /* istanbul ignore if */
21177- if (p === false) return false
21178-
21179- if (p === GLOBSTAR) {
21180- this.debug('GLOBSTAR', [pattern, p, f])
21181-
21182- // "**"
21183- // a/**/b/**/c would match the following:
21184- // a/b/x/y/z/c
21185- // a/x/y/z/b/c
21186- // a/b/x/b/x/c
21187- // a/b/c
21188- // To do this, take the rest of the pattern after
21189- // the **, and see if it would match the file remainder.
21190- // If so, return success.
21191- // If not, the ** "swallows" a segment, and try again.
21192- // This is recursively awful.
21193- //
21194- // a/**/b/**/c matching a/b/x/y/z/c
21195- // - a matches a
21196- // - doublestar
21197- // - matchOne(b/x/y/z/c, b/**/c)
21198- // - b matches b
21199- // - doublestar
21200- // - matchOne(x/y/z/c, c) -> no
21201- // - matchOne(y/z/c, c) -> no
21202- // - matchOne(z/c, c) -> no
21203- // - matchOne(c, c) yes, hit
21204- var fr = fi
21205- var pr = pi + 1
21206- if (pr === pl) {
21207- this.debug('** at the end')
21208- // a ** at the end will just swallow the rest.
21209- // We have found a match.
21210- // however, it will not swallow /.x, unless
21211- // options.dot is set.
21212- // . and .. are *never* matched by **, for explosively
21213- // exponential reasons.
21214- for (; fi < fl; fi++) {
21215- if (file[fi] === '.' || file[fi] === '..' ||
21216- (!options.dot && file[fi].charAt(0) === '.')) return false
21217- }
21218- return true
21185+ // check the head
21186+ if (head.length) {
21187+ var fileHead = file.slice(fileIndex, fileIndex + head.length)
21188+ if (!this._matchOne(fileHead, head, partial, 0, 0)) {
21189+ return false
21190+ }
21191+ fileIndex += head.length
21192+ }
21193+
21194+ // check the tail
21195+ var fileTailMatch = 0
21196+ if (tail.length) {
21197+ if (tail.length + fileIndex > file.length) return false
21198+
21199+ var tailStart = file.length - tail.length
21200+ if (this._matchOne(file, tail, partial, tailStart, 0)) {
21201+ fileTailMatch = tail.length
21202+ } else {
21203+ // affordance for stuff like a/**/* matching a/b/
21204+ if (file[file.length - 1] !== '' ||
21205+ fileIndex + tail.length === file.length) {
21206+ return false
21207+ }
21208+ tailStart--
21209+ if (!this._matchOne(file, tail, partial, tailStart, 0)) {
21210+ return false
2121921211 }
21212+ fileTailMatch = tail.length + 1
21213+ }
21214+ }
2122021215
21221- // ok, let's see if we can swallow whatever we can.
21222- while (fr < fl) {
21223- var swallowee = file[fr]
21216+ // if body is empty (single ** between head and tail)
21217+ if (!body.length) {
21218+ var sawSome = !!fileTailMatch
21219+ for (i = fileIndex; i < file.length - fileTailMatch; i++) {
21220+ var f = String(file[i])
21221+ sawSome = true
21222+ if (f === '.' || f === '..' ||
21223+ (!this.options.dot && f.charAt(0) === '.')) {
21224+ return false
21225+ }
21226+ }
21227+ return partial || sawSome
21228+ }
2122421229
21225- this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
21230+ // split body into segments at each GLOBSTAR
21231+ var bodySegments = [[[], 0]]
21232+ var currentBody = bodySegments[0]
21233+ var nonGsParts = 0
21234+ var nonGsPartsSums = [0]
21235+ for (var bi = 0; bi < body.length; bi++) {
21236+ var b = body[bi]
21237+ if (b === GLOBSTAR) {
21238+ nonGsPartsSums.push(nonGsParts)
21239+ currentBody = [[], 0]
21240+ bodySegments.push(currentBody)
21241+ } else {
21242+ currentBody[0].push(b)
21243+ nonGsParts++
21244+ }
21245+ }
2122621246
21227- // XXX remove this slice. Just pass the start index.
21228- if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
21229- this.debug('globstar found match!', fr, fl, swallowee)
21230- // found a match.
21231- return true
21232- } else {
21233- // can't swallow "." or ".." ever.
21234- // can only swallow ".foo" when explicitly asked.
21235- if (swallowee === '.' || swallowee === '..' ||
21236- (!options.dot && swallowee.charAt(0) === '.')) {
21237- this.debug('dot detected!', file, fr, pattern, pr)
21238- break
21239- }
21247+ var idx = bodySegments.length - 1
21248+ var fileLength = file.length - fileTailMatch
21249+ for (var si = 0; si < bodySegments.length; si++) {
21250+ bodySegments[si][1] = fileLength -
21251+ (nonGsPartsSums[idx--] + bodySegments[si][0].length)
21252+ }
2124021253
21241- // ** swallows a segment, and continue.
21242- this.debug('globstar swallow a segment, and continue')
21243- fr++
21244- }
21254+ return !!this._matchGlobStarBodySections(
21255+ file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch
21256+ )
21257+ }
21258+
21259+ // return false for "nope, not matching"
21260+ // return null for "not matching, cannot keep trying"
21261+ Minimatch.prototype._matchGlobStarBodySections = function (
21262+ file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail
21263+ ) {
21264+ var bs = bodySegments[bodyIndex]
21265+ if (!bs) {
21266+ // just make sure there are no bad dots
21267+ for (var i = fileIndex; i < file.length; i++) {
21268+ sawTail = true
21269+ var f = file[i]
21270+ if (f === '.' || f === '..' ||
21271+ (!this.options.dot && f.charAt(0) === '.')) {
21272+ return false
2124521273 }
21274+ }
21275+ return sawTail
21276+ }
2124621277
21247- // no match was found.
21248- // However, in partial mode, we can't say this is necessarily over.
21249- // If there's more *pattern* left, then
21250- /* istanbul ignore if */
21251- if (partial) {
21252- // ran out of file
21253- this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
21254- if (fr === fl) return true
21278+ var body = bs[0]
21279+ var after = bs[1]
21280+ while (fileIndex <= after) {
21281+ var m = this._matchOne(
21282+ file.slice(0, fileIndex + body.length),
21283+ body,
21284+ partial,
21285+ fileIndex,
21286+ 0
21287+ )
21288+ // if limit exceeded, no match. intentional false negative,
21289+ // acceptable break in correctness for security.
21290+ if (m && globStarDepth < this.maxGlobstarRecursion) {
21291+ var sub = this._matchGlobStarBodySections(
21292+ file, bodySegments,
21293+ fileIndex + body.length, bodyIndex + 1,
21294+ partial, globStarDepth + 1, sawTail
21295+ )
21296+ if (sub !== false) {
21297+ return sub
2125521298 }
21299+ }
21300+ var f = file[fileIndex]
21301+ if (f === '.' || f === '..' ||
21302+ (!this.options.dot && f.charAt(0) === '.')) {
2125621303 return false
2125721304 }
21305+ fileIndex++
21306+ }
21307+ return partial || null
21308+ }
21309+
21310+ Minimatch.prototype._matchOne = function (file, pattern, partial, fileIndex, patternIndex) {
21311+ var fi, pi, fl, pl
21312+ for (
21313+ fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length
21314+ ; (fi < fl) && (pi < pl)
21315+ ; fi++, pi++
21316+ ) {
21317+ this.debug('matchOne loop')
21318+ var p = pattern[pi]
21319+ var f = file[fi]
21320+
21321+ this.debug(pattern, p, f)
21322+
21323+ // should be impossible.
21324+ // some invalid regexp stuff in the set.
21325+ /* istanbul ignore if */
21326+ if (p === false || p === GLOBSTAR) return false
2125821327
2125921328 // something other than **
2126021329 // non-magic patterns just have to match exactly
@@ -21271,17 +21340,6 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) {
2127121340 if (!hit) return false
2127221341 }
2127321342
21274- // Note: ending in / means that we'll get a final ""
21275- // at the end of the pattern. This can only match a
21276- // corresponding "" at the end of the file.
21277- // If the file ends in /, then it can only match a
21278- // a pattern that ends in /, unless the pattern just
21279- // doesn't have any more for it. But, a/b/ should *not*
21280- // match "a/b/*", even though "" matches against the
21281- // [^/]*? pattern, except in partial mode, where it might
21282- // simply not be reached yet.
21283- // However, a/b/ should still satisfy a/*
21284-
2128521343 // now either we fell off the end of the pattern, or we're done.
2128621344 if (fi === fl && pi === pl) {
2128721345 // ran out of pattern and filename at the same time.
0 commit comments