| | |
| | | } |
| | | |
| | | this.options = options |
| | | this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined |
| | | ? options.maxGlobstarRecursion : 200 |
| | | this.set = [] |
| | | this.pattern = pattern |
| | | this.regexp = null |
| | |
| | | re += c |
| | | continue |
| | | } |
| | | |
| | | // coalesce consecutive non-globstar * characters |
| | | if (c === '*' && stateChar === '*') continue |
| | | |
| | | // if we already have a stateChar, then it means |
| | | // that there was something like ** or +? in there. |
| | |
| | | // out of pattern, then that's fine, as long as all |
| | | // the parts match. |
| | | Minimatch.prototype.matchOne = function (file, pattern, partial) { |
| | | var options = this.options |
| | | if (pattern.indexOf(GLOBSTAR) !== -1) { |
| | | return this._matchGlobstar(file, pattern, partial, 0, 0) |
| | | } |
| | | return this._matchOne(file, pattern, partial, 0, 0) |
| | | } |
| | | |
| | | this.debug('matchOne', |
| | | { 'this': this, file: file, pattern: pattern }) |
| | | Minimatch.prototype._matchGlobstar = function (file, pattern, partial, fileIndex, patternIndex) { |
| | | var i |
| | | |
| | | this.debug('matchOne', file.length, pattern.length) |
| | | // find first globstar from patternIndex |
| | | var firstgs = -1 |
| | | for (i = patternIndex; i < pattern.length; i++) { |
| | | if (pattern[i] === GLOBSTAR) { firstgs = i; break } |
| | | } |
| | | |
| | | for (var fi = 0, |
| | | pi = 0, |
| | | fl = file.length, |
| | | pl = pattern.length |
| | | ; (fi < fl) && (pi < pl) |
| | | ; fi++, pi++) { |
| | | // find last globstar |
| | | var lastgs = -1 |
| | | for (i = pattern.length - 1; i >= 0; i--) { |
| | | if (pattern[i] === GLOBSTAR) { lastgs = i; break } |
| | | } |
| | | |
| | | var head = pattern.slice(patternIndex, firstgs) |
| | | var body = partial ? pattern.slice(firstgs + 1) : pattern.slice(firstgs + 1, lastgs) |
| | | var tail = partial ? [] : pattern.slice(lastgs + 1) |
| | | |
| | | // check the head |
| | | if (head.length) { |
| | | var fileHead = file.slice(fileIndex, fileIndex + head.length) |
| | | if (!this._matchOne(fileHead, head, partial, 0, 0)) { |
| | | return false |
| | | } |
| | | fileIndex += head.length |
| | | } |
| | | |
| | | // check the tail |
| | | var fileTailMatch = 0 |
| | | if (tail.length) { |
| | | if (tail.length + fileIndex > file.length) return false |
| | | |
| | | var tailStart = file.length - tail.length |
| | | if (this._matchOne(file, tail, partial, tailStart, 0)) { |
| | | fileTailMatch = tail.length |
| | | } else { |
| | | // affordance for stuff like a/**/* matching a/b/ |
| | | if (file[file.length - 1] !== '' || |
| | | fileIndex + tail.length === file.length) { |
| | | return false |
| | | } |
| | | tailStart-- |
| | | if (!this._matchOne(file, tail, partial, tailStart, 0)) { |
| | | return false |
| | | } |
| | | fileTailMatch = tail.length + 1 |
| | | } |
| | | } |
| | | |
| | | // if body is empty (single ** between head and tail) |
| | | if (!body.length) { |
| | | var sawSome = !!fileTailMatch |
| | | for (i = fileIndex; i < file.length - fileTailMatch; i++) { |
| | | var f = String(file[i]) |
| | | sawSome = true |
| | | if (f === '.' || f === '..' || |
| | | (!this.options.dot && f.charAt(0) === '.')) { |
| | | return false |
| | | } |
| | | } |
| | | return partial || sawSome |
| | | } |
| | | |
| | | // split body into segments at each GLOBSTAR |
| | | var bodySegments = [[[], 0]] |
| | | var currentBody = bodySegments[0] |
| | | var nonGsParts = 0 |
| | | var nonGsPartsSums = [0] |
| | | for (var bi = 0; bi < body.length; bi++) { |
| | | var b = body[bi] |
| | | if (b === GLOBSTAR) { |
| | | nonGsPartsSums.push(nonGsParts) |
| | | currentBody = [[], 0] |
| | | bodySegments.push(currentBody) |
| | | } else { |
| | | currentBody[0].push(b) |
| | | nonGsParts++ |
| | | } |
| | | } |
| | | |
| | | var idx = bodySegments.length - 1 |
| | | var fileLength = file.length - fileTailMatch |
| | | for (var si = 0; si < bodySegments.length; si++) { |
| | | bodySegments[si][1] = fileLength - |
| | | (nonGsPartsSums[idx--] + bodySegments[si][0].length) |
| | | } |
| | | |
| | | return !!this._matchGlobStarBodySections( |
| | | file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch |
| | | ) |
| | | } |
| | | |
| | | // return false for "nope, not matching" |
| | | // return null for "not matching, cannot keep trying" |
| | | Minimatch.prototype._matchGlobStarBodySections = function ( |
| | | file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail |
| | | ) { |
| | | var bs = bodySegments[bodyIndex] |
| | | if (!bs) { |
| | | // just make sure there are no bad dots |
| | | for (var i = fileIndex; i < file.length; i++) { |
| | | sawTail = true |
| | | var f = file[i] |
| | | if (f === '.' || f === '..' || |
| | | (!this.options.dot && f.charAt(0) === '.')) { |
| | | return false |
| | | } |
| | | } |
| | | return sawTail |
| | | } |
| | | |
| | | var body = bs[0] |
| | | var after = bs[1] |
| | | while (fileIndex <= after) { |
| | | var m = this._matchOne( |
| | | file.slice(0, fileIndex + body.length), |
| | | body, |
| | | partial, |
| | | fileIndex, |
| | | 0 |
| | | ) |
| | | // if limit exceeded, no match. intentional false negative, |
| | | // acceptable break in correctness for security. |
| | | if (m && globStarDepth < this.maxGlobstarRecursion) { |
| | | var sub = this._matchGlobStarBodySections( |
| | | file, bodySegments, |
| | | fileIndex + body.length, bodyIndex + 1, |
| | | partial, globStarDepth + 1, sawTail |
| | | ) |
| | | if (sub !== false) { |
| | | return sub |
| | | } |
| | | } |
| | | var f = file[fileIndex] |
| | | if (f === '.' || f === '..' || |
| | | (!this.options.dot && f.charAt(0) === '.')) { |
| | | return false |
| | | } |
| | | fileIndex++ |
| | | } |
| | | return partial || null |
| | | } |
| | | |
| | | Minimatch.prototype._matchOne = function (file, pattern, partial, fileIndex, patternIndex) { |
| | | var fi, pi, fl, pl |
| | | for ( |
| | | fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length |
| | | ; (fi < fl) && (pi < pl) |
| | | ; fi++, pi++ |
| | | ) { |
| | | this.debug('matchOne loop') |
| | | var p = pattern[pi] |
| | | var f = file[fi] |
| | |
| | | // should be impossible. |
| | | // some invalid regexp stuff in the set. |
| | | /* istanbul ignore if */ |
| | | if (p === false) return false |
| | | |
| | | if (p === GLOBSTAR) { |
| | | this.debug('GLOBSTAR', [pattern, p, f]) |
| | | |
| | | // "**" |
| | | // a/**/b/**/c would match the following: |
| | | // a/b/x/y/z/c |
| | | // a/x/y/z/b/c |
| | | // a/b/x/b/x/c |
| | | // a/b/c |
| | | // To do this, take the rest of the pattern after |
| | | // the **, and see if it would match the file remainder. |
| | | // If so, return success. |
| | | // If not, the ** "swallows" a segment, and try again. |
| | | // This is recursively awful. |
| | | // |
| | | // a/**/b/**/c matching a/b/x/y/z/c |
| | | // - a matches a |
| | | // - doublestar |
| | | // - matchOne(b/x/y/z/c, b/**/c) |
| | | // - b matches b |
| | | // - doublestar |
| | | // - matchOne(x/y/z/c, c) -> no |
| | | // - matchOne(y/z/c, c) -> no |
| | | // - matchOne(z/c, c) -> no |
| | | // - matchOne(c, c) yes, hit |
| | | var fr = fi |
| | | var pr = pi + 1 |
| | | if (pr === pl) { |
| | | this.debug('** at the end') |
| | | // a ** at the end will just swallow the rest. |
| | | // We have found a match. |
| | | // however, it will not swallow /.x, unless |
| | | // options.dot is set. |
| | | // . and .. are *never* matched by **, for explosively |
| | | // exponential reasons. |
| | | for (; fi < fl; fi++) { |
| | | if (file[fi] === '.' || file[fi] === '..' || |
| | | (!options.dot && file[fi].charAt(0) === '.')) return false |
| | | } |
| | | return true |
| | | } |
| | | |
| | | // ok, let's see if we can swallow whatever we can. |
| | | while (fr < fl) { |
| | | var swallowee = file[fr] |
| | | |
| | | this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) |
| | | |
| | | // XXX remove this slice. Just pass the start index. |
| | | if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { |
| | | this.debug('globstar found match!', fr, fl, swallowee) |
| | | // found a match. |
| | | return true |
| | | } else { |
| | | // can't swallow "." or ".." ever. |
| | | // can only swallow ".foo" when explicitly asked. |
| | | if (swallowee === '.' || swallowee === '..' || |
| | | (!options.dot && swallowee.charAt(0) === '.')) { |
| | | this.debug('dot detected!', file, fr, pattern, pr) |
| | | break |
| | | } |
| | | |
| | | // ** swallows a segment, and continue. |
| | | this.debug('globstar swallow a segment, and continue') |
| | | fr++ |
| | | } |
| | | } |
| | | |
| | | // no match was found. |
| | | // However, in partial mode, we can't say this is necessarily over. |
| | | // If there's more *pattern* left, then |
| | | /* istanbul ignore if */ |
| | | if (partial) { |
| | | // ran out of file |
| | | this.debug('\n>>> no match, partial?', file, fr, pattern, pr) |
| | | if (fr === fl) return true |
| | | } |
| | | return false |
| | | } |
| | | if (p === false || p === GLOBSTAR) return false |
| | | |
| | | // something other than ** |
| | | // non-magic patterns just have to match exactly |
| | |
| | | |
| | | if (!hit) return false |
| | | } |
| | | |
| | | // Note: ending in / means that we'll get a final "" |
| | | // at the end of the pattern. This can only match a |
| | | // corresponding "" at the end of the file. |
| | | // If the file ends in /, then it can only match a |
| | | // a pattern that ends in /, unless the pattern just |
| | | // doesn't have any more for it. But, a/b/ should *not* |
| | | // match "a/b/*", even though "" matches against the |
| | | // [^/]*? pattern, except in partial mode, where it might |
| | | // simply not be reached yet. |
| | | // However, a/b/ should still satisfy a/* |
| | | |
| | | // now either we fell off the end of the pattern, or we're done. |
| | | if (fi === fl && pi === pl) { |