@@ -52659,6 +52659,8 @@ function Minimatch (pattern, options) {
5265952659 }
5266052660
5266152661 this.options = options
52662+ this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined
52663+ ? options.maxGlobstarRecursion : 200
5266252664 this.set = []
5266352665 this.pattern = pattern
5266452666 this.regexp = null
@@ -52907,6 +52909,9 @@ function parse (pattern, isSub) {
5290752909 continue
5290852910 }
5290952911
52912+ // coalesce consecutive non-globstar * characters
52913+ if (c === '*' && stateChar === '*') continue
52914+
5291052915 // if we already have a stateChar, then it means
5291152916 // that there was something like ** or +? in there.
5291252917 // Handle the stateChar, then proceed with this one.
@@ -53301,109 +53306,173 @@ Minimatch.prototype.match = function match (f, partial) {
5330153306// out of pattern, then that's fine, as long as all
5330253307// the parts match.
5330353308Minimatch.prototype.matchOne = function (file, pattern, partial) {
53304- var options = this.options
53309+ if (pattern.indexOf(GLOBSTAR) !== -1) {
53310+ return this._matchGlobstar(file, pattern, partial, 0, 0)
53311+ }
53312+ return this._matchOne(file, pattern, partial, 0, 0)
53313+ }
5330553314
53306- this.debug('matchOne',
53307- { 'this': this, file: file, pattern: pattern })
53315+ Minimatch.prototype._matchGlobstar = function (file, pattern, partial, fileIndex, patternIndex) {
53316+ var i
5330853317
53309- this.debug('matchOne', file.length, pattern.length)
53318+ // find first globstar from patternIndex
53319+ var firstgs = -1
53320+ for (i = patternIndex; i < pattern.length; i++) {
53321+ if (pattern[i] === GLOBSTAR) { firstgs = i; break }
53322+ }
5331053323
53311- for (var fi = 0,
53312- pi = 0,
53313- fl = file.length,
53314- pl = pattern.length
53315- ; (fi < fl) && (pi < pl)
53316- ; fi++, pi++) {
53317- this.debug('matchOne loop')
53318- var p = pattern[pi]
53319- var f = file[fi]
53324+ // find last globstar
53325+ var lastgs = -1
53326+ for (i = pattern.length - 1; i >= 0; i--) {
53327+ if (pattern[i] === GLOBSTAR) { lastgs = i; break }
53328+ }
5332053329
53321- this.debug(pattern, p, f)
53330+ var head = pattern.slice(patternIndex, firstgs)
53331+ var body = partial ? pattern.slice(firstgs + 1) : pattern.slice(firstgs + 1, lastgs)
53332+ var tail = partial ? [] : pattern.slice(lastgs + 1)
5332253333
53323- // should be impossible.
53324- // some invalid regexp stuff in the set.
53325- /* istanbul ignore if */
53326- if (p === false) return false
53327-
53328- if (p === GLOBSTAR) {
53329- this.debug('GLOBSTAR', [pattern, p, f])
53330-
53331- // "**"
53332- // a/**/b/**/c would match the following:
53333- // a/b/x/y/z/c
53334- // a/x/y/z/b/c
53335- // a/b/x/b/x/c
53336- // a/b/c
53337- // To do this, take the rest of the pattern after
53338- // the **, and see if it would match the file remainder.
53339- // If so, return success.
53340- // If not, the ** "swallows" a segment, and try again.
53341- // This is recursively awful.
53342- //
53343- // a/**/b/**/c matching a/b/x/y/z/c
53344- // - a matches a
53345- // - doublestar
53346- // - matchOne(b/x/y/z/c, b/**/c)
53347- // - b matches b
53348- // - doublestar
53349- // - matchOne(x/y/z/c, c) -> no
53350- // - matchOne(y/z/c, c) -> no
53351- // - matchOne(z/c, c) -> no
53352- // - matchOne(c, c) yes, hit
53353- var fr = fi
53354- var pr = pi + 1
53355- if (pr === pl) {
53356- this.debug('** at the end')
53357- // a ** at the end will just swallow the rest.
53358- // We have found a match.
53359- // however, it will not swallow /.x, unless
53360- // options.dot is set.
53361- // . and .. are *never* matched by **, for explosively
53362- // exponential reasons.
53363- for (; fi < fl; fi++) {
53364- if (file[fi] === '.' || file[fi] === '..' ||
53365- (!options.dot && file[fi].charAt(0) === '.')) return false
53366- }
53367- return true
53334+ // check the head
53335+ if (head.length) {
53336+ var fileHead = file.slice(fileIndex, fileIndex + head.length)
53337+ if (!this._matchOne(fileHead, head, partial, 0, 0)) {
53338+ return false
53339+ }
53340+ fileIndex += head.length
53341+ }
53342+
53343+ // check the tail
53344+ var fileTailMatch = 0
53345+ if (tail.length) {
53346+ if (tail.length + fileIndex > file.length) return false
53347+
53348+ var tailStart = file.length - tail.length
53349+ if (this._matchOne(file, tail, partial, tailStart, 0)) {
53350+ fileTailMatch = tail.length
53351+ } else {
53352+ // affordance for stuff like a/**/* matching a/b/
53353+ if (file[file.length - 1] !== '' ||
53354+ fileIndex + tail.length === file.length) {
53355+ return false
53356+ }
53357+ tailStart--
53358+ if (!this._matchOne(file, tail, partial, tailStart, 0)) {
53359+ return false
53360+ }
53361+ fileTailMatch = tail.length + 1
53362+ }
53363+ }
53364+
53365+ // if body is empty (single ** between head and tail)
53366+ if (!body.length) {
53367+ var sawSome = !!fileTailMatch
53368+ for (i = fileIndex; i < file.length - fileTailMatch; i++) {
53369+ var f = String(file[i])
53370+ sawSome = true
53371+ if (f === '.' || f === '..' ||
53372+ (!this.options.dot && f.charAt(0) === '.')) {
53373+ return false
5336853374 }
53375+ }
53376+ return partial || sawSome
53377+ }
5336953378
53370- // ok, let's see if we can swallow whatever we can.
53371- while (fr < fl) {
53372- var swallowee = file[fr]
53379+ // split body into segments at each GLOBSTAR
53380+ var bodySegments = [[[], 0]]
53381+ var currentBody = bodySegments[0]
53382+ var nonGsParts = 0
53383+ var nonGsPartsSums = [0]
53384+ for (var bi = 0; bi < body.length; bi++) {
53385+ var b = body[bi]
53386+ if (b === GLOBSTAR) {
53387+ nonGsPartsSums.push(nonGsParts)
53388+ currentBody = [[], 0]
53389+ bodySegments.push(currentBody)
53390+ } else {
53391+ currentBody[0].push(b)
53392+ nonGsParts++
53393+ }
53394+ }
5337353395
53374- this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
53396+ var idx = bodySegments.length - 1
53397+ var fileLength = file.length - fileTailMatch
53398+ for (var si = 0; si < bodySegments.length; si++) {
53399+ bodySegments[si][1] = fileLength -
53400+ (nonGsPartsSums[idx--] + bodySegments[si][0].length)
53401+ }
5337553402
53376- // XXX remove this slice. Just pass the start index.
53377- if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
53378- this.debug('globstar found match!', fr, fl, swallowee)
53379- // found a match.
53380- return true
53381- } else {
53382- // can't swallow "." or ".." ever.
53383- // can only swallow ".foo" when explicitly asked.
53384- if (swallowee === '.' || swallowee === '..' ||
53385- (!options.dot && swallowee.charAt(0) === '.')) {
53386- this.debug('dot detected!', file, fr, pattern, pr)
53387- break
53388- }
53403+ return !!this._matchGlobStarBodySections(
53404+ file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch
53405+ )
53406+ }
5338953407
53390- // ** swallows a segment, and continue.
53391- this.debug('globstar swallow a segment, and continue')
53392- fr++
53393- }
53408+ // return false for "nope, not matching"
53409+ // return null for "not matching, cannot keep trying"
53410+ Minimatch.prototype._matchGlobStarBodySections = function (
53411+ file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail
53412+ ) {
53413+ var bs = bodySegments[bodyIndex]
53414+ if (!bs) {
53415+ // just make sure there are no bad dots
53416+ for (var i = fileIndex; i < file.length; i++) {
53417+ sawTail = true
53418+ var f = file[i]
53419+ if (f === '.' || f === '..' ||
53420+ (!this.options.dot && f.charAt(0) === '.')) {
53421+ return false
5339453422 }
53423+ }
53424+ return sawTail
53425+ }
5339553426
53396- // no match was found.
53397- // However, in partial mode, we can't say this is necessarily over.
53398- // If there's more *pattern* left, then
53399- /* istanbul ignore if */
53400- if (partial) {
53401- // ran out of file
53402- this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
53403- if (fr === fl) return true
53427+ var body = bs[0]
53428+ var after = bs[1]
53429+ while (fileIndex <= after) {
53430+ var m = this._matchOne(
53431+ file.slice(0, fileIndex + body.length),
53432+ body,
53433+ partial,
53434+ fileIndex,
53435+ 0
53436+ )
53437+ // if limit exceeded, no match. intentional false negative,
53438+ // acceptable break in correctness for security.
53439+ if (m && globStarDepth < this.maxGlobstarRecursion) {
53440+ var sub = this._matchGlobStarBodySections(
53441+ file, bodySegments,
53442+ fileIndex + body.length, bodyIndex + 1,
53443+ partial, globStarDepth + 1, sawTail
53444+ )
53445+ if (sub !== false) {
53446+ return sub
5340453447 }
53448+ }
53449+ var f = file[fileIndex]
53450+ if (f === '.' || f === '..' ||
53451+ (!this.options.dot && f.charAt(0) === '.')) {
5340553452 return false
5340653453 }
53454+ fileIndex++
53455+ }
53456+ return partial || null
53457+ }
53458+
53459+ Minimatch.prototype._matchOne = function (file, pattern, partial, fileIndex, patternIndex) {
53460+ var fi, pi, fl, pl
53461+ for (
53462+ fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length
53463+ ; (fi < fl) && (pi < pl)
53464+ ; fi++, pi++
53465+ ) {
53466+ this.debug('matchOne loop')
53467+ var p = pattern[pi]
53468+ var f = file[fi]
53469+
53470+ this.debug(pattern, p, f)
53471+
53472+ // should be impossible.
53473+ // some invalid regexp stuff in the set.
53474+ /* istanbul ignore if */
53475+ if (p === false || p === GLOBSTAR) return false
5340753476
5340853477 // something other than **
5340953478 // non-magic patterns just have to match exactly
@@ -53420,17 +53489,6 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) {
5342053489 if (!hit) return false
5342153490 }
5342253491
53423- // Note: ending in / means that we'll get a final ""
53424- // at the end of the pattern. This can only match a
53425- // corresponding "" at the end of the file.
53426- // If the file ends in /, then it can only match a
53427- // a pattern that ends in /, unless the pattern just
53428- // doesn't have any more for it. But, a/b/ should *not*
53429- // match "a/b/*", even though "" matches against the
53430- // [^/]*? pattern, except in partial mode, where it might
53431- // simply not be reached yet.
53432- // However, a/b/ should still satisfy a/*
53433-
5343453492 // now either we fell off the end of the pattern, or we're done.
5343553493 if (fi === fl && pi === pl) {
5343653494 // ran out of pattern and filename at the same time.
0 commit comments