• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1module.exports = minimatch
2minimatch.Minimatch = Minimatch
3
4var path = { sep: '/' }
5try {
6  path = require('path')
7} catch (er) {}
8
9var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
10var expand = require('brace-expansion')
11
12var plTypes = {
13  '!': { open: '(?:(?!(?:', close: '))[^/]*?)'},
14  '?': { open: '(?:', close: ')?' },
15  '+': { open: '(?:', close: ')+' },
16  '*': { open: '(?:', close: ')*' },
17  '@': { open: '(?:', close: ')' }
18}
19
20// any single thing other than /
21// don't need to escape / when using new RegExp()
22var qmark = '[^/]'
23
24// * => any number of characters
25var star = qmark + '*?'
26
27// ** when dots are allowed.  Anything goes, except .. and .
28// not (^ or / followed by one or two dots followed by $ or /),
29// followed by anything, any number of times.
30var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
31
32// not a ^ or / followed by a dot,
33// followed by anything, any number of times.
34var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
35
36// characters that need to be escaped in RegExp.
37var reSpecials = charSet('().*{}+?[]^$\\!')
38
39// "abc" -> { a:true, b:true, c:true }
40function charSet (s) {
41  return s.split('').reduce(function (set, c) {
42    set[c] = true
43    return set
44  }, {})
45}
46
47// normalizes slashes.
48var slashSplit = /\/+/
49
50minimatch.filter = filter
51function filter (pattern, options) {
52  options = options || {}
53  return function (p, i, list) {
54    return minimatch(p, pattern, options)
55  }
56}
57
58function ext (a, b) {
59  a = a || {}
60  b = b || {}
61  var t = {}
62  Object.keys(b).forEach(function (k) {
63    t[k] = b[k]
64  })
65  Object.keys(a).forEach(function (k) {
66    t[k] = a[k]
67  })
68  return t
69}
70
71minimatch.defaults = function (def) {
72  if (!def || !Object.keys(def).length) return minimatch
73
74  var orig = minimatch
75
76  var m = function minimatch (p, pattern, options) {
77    return orig.minimatch(p, pattern, ext(def, options))
78  }
79
80  m.Minimatch = function Minimatch (pattern, options) {
81    return new orig.Minimatch(pattern, ext(def, options))
82  }
83
84  return m
85}
86
87Minimatch.defaults = function (def) {
88  if (!def || !Object.keys(def).length) return Minimatch
89  return minimatch.defaults(def).Minimatch
90}
91
92function minimatch (p, pattern, options) {
93  if (typeof pattern !== 'string') {
94    throw new TypeError('glob pattern string required')
95  }
96
97  if (!options) options = {}
98
99  // shortcut: comments match nothing.
100  if (!options.nocomment && pattern.charAt(0) === '#') {
101    return false
102  }
103
104  // "" only matches ""
105  if (pattern.trim() === '') return p === ''
106
107  return new Minimatch(pattern, options).match(p)
108}
109
110function Minimatch (pattern, options) {
111  if (!(this instanceof Minimatch)) {
112    return new Minimatch(pattern, options)
113  }
114
115  if (typeof pattern !== 'string') {
116    throw new TypeError('glob pattern string required')
117  }
118
119  if (!options) options = {}
120  pattern = pattern.trim()
121
122  // windows support: need to use /, not \
123  if (path.sep !== '/') {
124    pattern = pattern.split(path.sep).join('/')
125  }
126
127  this.options = options
128  this.set = []
129  this.pattern = pattern
130  this.regexp = null
131  this.negate = false
132  this.comment = false
133  this.empty = false
134
135  // make the set of regexps etc.
136  this.make()
137}
138
139Minimatch.prototype.debug = function () {}
140
141Minimatch.prototype.make = make
142function make () {
143  // don't do it more than once.
144  if (this._made) return
145
146  var pattern = this.pattern
147  var options = this.options
148
149  // empty patterns and comments match nothing.
150  if (!options.nocomment && pattern.charAt(0) === '#') {
151    this.comment = true
152    return
153  }
154  if (!pattern) {
155    this.empty = true
156    return
157  }
158
159  // step 1: figure out negation, etc.
160  this.parseNegate()
161
162  // step 2: expand braces
163  var set = this.globSet = this.braceExpand()
164
165  if (options.debug) this.debug = console.error
166
167  this.debug(this.pattern, set)
168
169  // step 3: now we have a set, so turn each one into a series of path-portion
170  // matching patterns.
171  // These will be regexps, except in the case of "**", which is
172  // set to the GLOBSTAR object for globstar behavior,
173  // and will not contain any / characters
174  set = this.globParts = set.map(function (s) {
175    return s.split(slashSplit)
176  })
177
178  this.debug(this.pattern, set)
179
180  // glob --> regexps
181  set = set.map(function (s, si, set) {
182    return s.map(this.parse, this)
183  }, this)
184
185  this.debug(this.pattern, set)
186
187  // filter out everything that didn't compile properly.
188  set = set.filter(function (s) {
189    return s.indexOf(false) === -1
190  })
191
192  this.debug(this.pattern, set)
193
194  this.set = set
195}
196
197Minimatch.prototype.parseNegate = parseNegate
198function parseNegate () {
199  var pattern = this.pattern
200  var negate = false
201  var options = this.options
202  var negateOffset = 0
203
204  if (options.nonegate) return
205
206  for (var i = 0, l = pattern.length
207    ; i < l && pattern.charAt(i) === '!'
208    ; i++) {
209    negate = !negate
210    negateOffset++
211  }
212
213  if (negateOffset) this.pattern = pattern.substr(negateOffset)
214  this.negate = negate
215}
216
217// Brace expansion:
218// a{b,c}d -> abd acd
219// a{b,}c -> abc ac
220// a{0..3}d -> a0d a1d a2d a3d
221// a{b,c{d,e}f}g -> abg acdfg acefg
222// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
223//
224// Invalid sets are not expanded.
225// a{2..}b -> a{2..}b
226// a{b}c -> a{b}c
227minimatch.braceExpand = function (pattern, options) {
228  return braceExpand(pattern, options)
229}
230
231Minimatch.prototype.braceExpand = braceExpand
232
233function braceExpand (pattern, options) {
234  if (!options) {
235    if (this instanceof Minimatch) {
236      options = this.options
237    } else {
238      options = {}
239    }
240  }
241
242  pattern = typeof pattern === 'undefined'
243    ? this.pattern : pattern
244
245  if (typeof pattern === 'undefined') {
246    throw new TypeError('undefined pattern')
247  }
248
249  if (options.nobrace ||
250    !pattern.match(/\{.*\}/)) {
251    // shortcut. no need to expand.
252    return [pattern]
253  }
254
255  return expand(pattern)
256}
257
258// parse a component of the expanded set.
259// At this point, no pattern may contain "/" in it
260// so we're going to return a 2d array, where each entry is the full
261// pattern, split on '/', and then turned into a regular expression.
262// A regexp is made at the end which joins each array with an
263// escaped /, and another full one which joins each regexp with |.
264//
265// Following the lead of Bash 4.1, note that "**" only has special meaning
266// when it is the *only* thing in a path portion.  Otherwise, any series
267// of * is equivalent to a single *.  Globstar behavior is enabled by
268// default, and can be disabled by setting options.noglobstar.
269Minimatch.prototype.parse = parse
270var SUBPARSE = {}
271function parse (pattern, isSub) {
272  if (pattern.length > 1024 * 64) {
273    throw new TypeError('pattern is too long')
274  }
275
276  var options = this.options
277
278  // shortcuts
279  if (!options.noglobstar && pattern === '**') return GLOBSTAR
280  if (pattern === '') return ''
281
282  var re = ''
283  var hasMagic = !!options.nocase
284  var escaping = false
285  // ? => one single character
286  var patternListStack = []
287  var negativeLists = []
288  var stateChar
289  var inClass = false
290  var reClassStart = -1
291  var classStart = -1
292  // . and .. never match anything that doesn't start with .,
293  // even when options.dot is set.
294  var patternStart = pattern.charAt(0) === '.' ? '' // anything
295  // not (start or / followed by . or .. followed by / or end)
296  : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
297  : '(?!\\.)'
298  var self = this
299
300  function clearStateChar () {
301    if (stateChar) {
302      // we had some state-tracking character
303      // that wasn't consumed by this pass.
304      switch (stateChar) {
305        case '*':
306          re += star
307          hasMagic = true
308        break
309        case '?':
310          re += qmark
311          hasMagic = true
312        break
313        default:
314          re += '\\' + stateChar
315        break
316      }
317      self.debug('clearStateChar %j %j', stateChar, re)
318      stateChar = false
319    }
320  }
321
322  for (var i = 0, len = pattern.length, c
323    ; (i < len) && (c = pattern.charAt(i))
324    ; i++) {
325    this.debug('%s\t%s %s %j', pattern, i, re, c)
326
327    // skip over any that are escaped.
328    if (escaping && reSpecials[c]) {
329      re += '\\' + c
330      escaping = false
331      continue
332    }
333
334    switch (c) {
335      case '/':
336        // completely not allowed, even escaped.
337        // Should already be path-split by now.
338        return false
339
340      case '\\':
341        clearStateChar()
342        escaping = true
343      continue
344
345      // the various stateChar values
346      // for the "extglob" stuff.
347      case '?':
348      case '*':
349      case '+':
350      case '@':
351      case '!':
352        this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
353
354        // all of those are literals inside a class, except that
355        // the glob [!a] means [^a] in regexp
356        if (inClass) {
357          this.debug('  in class')
358          if (c === '!' && i === classStart + 1) c = '^'
359          re += c
360          continue
361        }
362
363        // if we already have a stateChar, then it means
364        // that there was something like ** or +? in there.
365        // Handle the stateChar, then proceed with this one.
366        self.debug('call clearStateChar %j', stateChar)
367        clearStateChar()
368        stateChar = c
369        // if extglob is disabled, then +(asdf|foo) isn't a thing.
370        // just clear the statechar *now*, rather than even diving into
371        // the patternList stuff.
372        if (options.noext) clearStateChar()
373      continue
374
375      case '(':
376        if (inClass) {
377          re += '('
378          continue
379        }
380
381        if (!stateChar) {
382          re += '\\('
383          continue
384        }
385
386        patternListStack.push({
387          type: stateChar,
388          start: i - 1,
389          reStart: re.length,
390          open: plTypes[stateChar].open,
391          close: plTypes[stateChar].close
392        })
393        // negation is (?:(?!js)[^/]*)
394        re += stateChar === '!' ? '(?:(?!(?:' : '(?:'
395        this.debug('plType %j %j', stateChar, re)
396        stateChar = false
397      continue
398
399      case ')':
400        if (inClass || !patternListStack.length) {
401          re += '\\)'
402          continue
403        }
404
405        clearStateChar()
406        hasMagic = true
407        var pl = patternListStack.pop()
408        // negation is (?:(?!js)[^/]*)
409        // The others are (?:<pattern>)<type>
410        re += pl.close
411        if (pl.type === '!') {
412          negativeLists.push(pl)
413        }
414        pl.reEnd = re.length
415      continue
416
417      case '|':
418        if (inClass || !patternListStack.length || escaping) {
419          re += '\\|'
420          escaping = false
421          continue
422        }
423
424        clearStateChar()
425        re += '|'
426      continue
427
428      // these are mostly the same in regexp and glob
429      case '[':
430        // swallow any state-tracking char before the [
431        clearStateChar()
432
433        if (inClass) {
434          re += '\\' + c
435          continue
436        }
437
438        inClass = true
439        classStart = i
440        reClassStart = re.length
441        re += c
442      continue
443
444      case ']':
445        //  a right bracket shall lose its special
446        //  meaning and represent itself in
447        //  a bracket expression if it occurs
448        //  first in the list.  -- POSIX.2 2.8.3.2
449        if (i === classStart + 1 || !inClass) {
450          re += '\\' + c
451          escaping = false
452          continue
453        }
454
455        // handle the case where we left a class open.
456        // "[z-a]" is valid, equivalent to "\[z-a\]"
457        if (inClass) {
458          // split where the last [ was, make sure we don't have
459          // an invalid re. if so, re-walk the contents of the
460          // would-be class to re-translate any characters that
461          // were passed through as-is
462          // TODO: It would probably be faster to determine this
463          // without a try/catch and a new RegExp, but it's tricky
464          // to do safely.  For now, this is safe and works.
465          var cs = pattern.substring(classStart + 1, i)
466          try {
467            RegExp('[' + cs + ']')
468          } catch (er) {
469            // not a valid class!
470            var sp = this.parse(cs, SUBPARSE)
471            re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
472            hasMagic = hasMagic || sp[1]
473            inClass = false
474            continue
475          }
476        }
477
478        // finish up the class.
479        hasMagic = true
480        inClass = false
481        re += c
482      continue
483
484      default:
485        // swallow any state char that wasn't consumed
486        clearStateChar()
487
488        if (escaping) {
489          // no need
490          escaping = false
491        } else if (reSpecials[c]
492          && !(c === '^' && inClass)) {
493          re += '\\'
494        }
495
496        re += c
497
498    } // switch
499  } // for
500
501  // handle the case where we left a class open.
502  // "[abc" is valid, equivalent to "\[abc"
503  if (inClass) {
504    // split where the last [ was, and escape it
505    // this is a huge pita.  We now have to re-walk
506    // the contents of the would-be class to re-translate
507    // any characters that were passed through as-is
508    cs = pattern.substr(classStart + 1)
509    sp = this.parse(cs, SUBPARSE)
510    re = re.substr(0, reClassStart) + '\\[' + sp[0]
511    hasMagic = hasMagic || sp[1]
512  }
513
514  // handle the case where we had a +( thing at the *end*
515  // of the pattern.
516  // each pattern list stack adds 3 chars, and we need to go through
517  // and escape any | chars that were passed through as-is for the regexp.
518  // Go through and escape them, taking care not to double-escape any
519  // | chars that were already escaped.
520  for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
521    var tail = re.slice(pl.reStart + pl.open.length)
522    this.debug('setting tail', re, pl)
523    // maybe some even number of \, then maybe 1 \, followed by a |
524    tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) {
525      if (!$2) {
526        // the | isn't already escaped, so escape it.
527        $2 = '\\'
528      }
529
530      // need to escape all those slashes *again*, without escaping the
531      // one that we need for escaping the | character.  As it works out,
532      // escaping an even number of slashes can be done by simply repeating
533      // it exactly after itself.  That's why this trick works.
534      //
535      // I am sorry that you have to see this.
536      return $1 + $1 + $2 + '|'
537    })
538
539    this.debug('tail=%j\n   %s', tail, tail, pl, re)
540    var t = pl.type === '*' ? star
541      : pl.type === '?' ? qmark
542      : '\\' + pl.type
543
544    hasMagic = true
545    re = re.slice(0, pl.reStart) + t + '\\(' + tail
546  }
547
548  // handle trailing things that only matter at the very end.
549  clearStateChar()
550  if (escaping) {
551    // trailing \\
552    re += '\\\\'
553  }
554
555  // only need to apply the nodot start if the re starts with
556  // something that could conceivably capture a dot
557  var addPatternStart = false
558  switch (re.charAt(0)) {
559    case '.':
560    case '[':
561    case '(': addPatternStart = true
562  }
563
564  // Hack to work around lack of negative lookbehind in JS
565  // A pattern like: *.!(x).!(y|z) needs to ensure that a name
566  // like 'a.xyz.yz' doesn't match.  So, the first negative
567  // lookahead, has to look ALL the way ahead, to the end of
568  // the pattern.
569  for (var n = negativeLists.length - 1; n > -1; n--) {
570    var nl = negativeLists[n]
571
572    var nlBefore = re.slice(0, nl.reStart)
573    var nlFirst = re.slice(nl.reStart, nl.reEnd - 8)
574    var nlLast = re.slice(nl.reEnd - 8, nl.reEnd)
575    var nlAfter = re.slice(nl.reEnd)
576
577    nlLast += nlAfter
578
579    // Handle nested stuff like *(*.js|!(*.json)), where open parens
580    // mean that we should *not* include the ) in the bit that is considered
581    // "after" the negated section.
582    var openParensBefore = nlBefore.split('(').length - 1
583    var cleanAfter = nlAfter
584    for (i = 0; i < openParensBefore; i++) {
585      cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')
586    }
587    nlAfter = cleanAfter
588
589    var dollar = ''
590    if (nlAfter === '' && isSub !== SUBPARSE) {
591      dollar = '$'
592    }
593    var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast
594    re = newRe
595  }
596
597  // if the re is not "" at this point, then we need to make sure
598  // it doesn't match against an empty path part.
599  // Otherwise a/* will match a/, which it should not.
600  if (re !== '' && hasMagic) {
601    re = '(?=.)' + re
602  }
603
604  if (addPatternStart) {
605    re = patternStart + re
606  }
607
608  // parsing just a piece of a larger pattern.
609  if (isSub === SUBPARSE) {
610    return [re, hasMagic]
611  }
612
613  // skip the regexp for non-magical patterns
614  // unescape anything in it, though, so that it'll be
615  // an exact match against a file etc.
616  if (!hasMagic) {
617    return globUnescape(pattern)
618  }
619
620  var flags = options.nocase ? 'i' : ''
621  try {
622    var regExp = new RegExp('^' + re + '$', flags)
623  } catch (er) {
624    // If it was an invalid regular expression, then it can't match
625    // anything.  This trick looks for a character after the end of
626    // the string, which is of course impossible, except in multi-line
627    // mode, but it's not a /m regex.
628    return new RegExp('$.')
629  }
630
631  regExp._glob = pattern
632  regExp._src = re
633
634  return regExp
635}
636
637minimatch.makeRe = function (pattern, options) {
638  return new Minimatch(pattern, options || {}).makeRe()
639}
640
641Minimatch.prototype.makeRe = makeRe
642function makeRe () {
643  if (this.regexp || this.regexp === false) return this.regexp
644
645  // at this point, this.set is a 2d array of partial
646  // pattern strings, or "**".
647  //
648  // It's better to use .match().  This function shouldn't
649  // be used, really, but it's pretty convenient sometimes,
650  // when you just want to work with a regex.
651  var set = this.set
652
653  if (!set.length) {
654    this.regexp = false
655    return this.regexp
656  }
657  var options = this.options
658
659  var twoStar = options.noglobstar ? star
660    : options.dot ? twoStarDot
661    : twoStarNoDot
662  var flags = options.nocase ? 'i' : ''
663
664  var re = set.map(function (pattern) {
665    return pattern.map(function (p) {
666      return (p === GLOBSTAR) ? twoStar
667      : (typeof p === 'string') ? regExpEscape(p)
668      : p._src
669    }).join('\\\/')
670  }).join('|')
671
672  // must match entire pattern
673  // ending in a * or ** will make it less strict.
674  re = '^(?:' + re + ')$'
675
676  // can match anything, as long as it's not this.
677  if (this.negate) re = '^(?!' + re + ').*$'
678
679  try {
680    this.regexp = new RegExp(re, flags)
681  } catch (ex) {
682    this.regexp = false
683  }
684  return this.regexp
685}
686
687minimatch.match = function (list, pattern, options) {
688  options = options || {}
689  var mm = new Minimatch(pattern, options)
690  list = list.filter(function (f) {
691    return mm.match(f)
692  })
693  if (mm.options.nonull && !list.length) {
694    list.push(pattern)
695  }
696  return list
697}
698
699Minimatch.prototype.match = match
700function match (f, partial) {
701  this.debug('match', f, this.pattern)
702  // short-circuit in the case of busted things.
703  // comments, etc.
704  if (this.comment) return false
705  if (this.empty) return f === ''
706
707  if (f === '/' && partial) return true
708
709  var options = this.options
710
711  // windows: need to use /, not \
712  if (path.sep !== '/') {
713    f = f.split(path.sep).join('/')
714  }
715
716  // treat the test path as a set of pathparts.
717  f = f.split(slashSplit)
718  this.debug(this.pattern, 'split', f)
719
720  // just ONE of the pattern sets in this.set needs to match
721  // in order for it to be valid.  If negating, then just one
722  // match means that we have failed.
723  // Either way, return on the first hit.
724
725  var set = this.set
726  this.debug(this.pattern, 'set', set)
727
728  // Find the basename of the path by looking for the last non-empty segment
729  var filename
730  var i
731  for (i = f.length - 1; i >= 0; i--) {
732    filename = f[i]
733    if (filename) break
734  }
735
736  for (i = 0; i < set.length; i++) {
737    var pattern = set[i]
738    var file = f
739    if (options.matchBase && pattern.length === 1) {
740      file = [filename]
741    }
742    var hit = this.matchOne(file, pattern, partial)
743    if (hit) {
744      if (options.flipNegate) return true
745      return !this.negate
746    }
747  }
748
749  // didn't get any hits.  this is success if it's a negative
750  // pattern, failure otherwise.
751  if (options.flipNegate) return false
752  return this.negate
753}
754
755// set partial to true to test if, for example,
756// "/a/b" matches the start of "/*/b/*/d"
757// Partial means, if you run out of file before you run
758// out of pattern, then that's fine, as long as all
759// the parts match.
760Minimatch.prototype.matchOne = function (file, pattern, partial) {
761  var options = this.options
762
763  this.debug('matchOne',
764    { 'this': this, file: file, pattern: pattern })
765
766  this.debug('matchOne', file.length, pattern.length)
767
768  for (var fi = 0,
769      pi = 0,
770      fl = file.length,
771      pl = pattern.length
772      ; (fi < fl) && (pi < pl)
773      ; fi++, pi++) {
774    this.debug('matchOne loop')
775    var p = pattern[pi]
776    var f = file[fi]
777
778    this.debug(pattern, p, f)
779
780    // should be impossible.
781    // some invalid regexp stuff in the set.
782    if (p === false) return false
783
784    if (p === GLOBSTAR) {
785      this.debug('GLOBSTAR', [pattern, p, f])
786
787      // "**"
788      // a/**/b/**/c would match the following:
789      // a/b/x/y/z/c
790      // a/x/y/z/b/c
791      // a/b/x/b/x/c
792      // a/b/c
793      // To do this, take the rest of the pattern after
794      // the **, and see if it would match the file remainder.
795      // If so, return success.
796      // If not, the ** "swallows" a segment, and try again.
797      // This is recursively awful.
798      //
799      // a/**/b/**/c matching a/b/x/y/z/c
800      // - a matches a
801      // - doublestar
802      //   - matchOne(b/x/y/z/c, b/**/c)
803      //     - b matches b
804      //     - doublestar
805      //       - matchOne(x/y/z/c, c) -> no
806      //       - matchOne(y/z/c, c) -> no
807      //       - matchOne(z/c, c) -> no
808      //       - matchOne(c, c) yes, hit
809      var fr = fi
810      var pr = pi + 1
811      if (pr === pl) {
812        this.debug('** at the end')
813        // a ** at the end will just swallow the rest.
814        // We have found a match.
815        // however, it will not swallow /.x, unless
816        // options.dot is set.
817        // . and .. are *never* matched by **, for explosively
818        // exponential reasons.
819        for (; fi < fl; fi++) {
820          if (file[fi] === '.' || file[fi] === '..' ||
821            (!options.dot && file[fi].charAt(0) === '.')) return false
822        }
823        return true
824      }
825
826      // ok, let's see if we can swallow whatever we can.
827      while (fr < fl) {
828        var swallowee = file[fr]
829
830        this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
831
832        // XXX remove this slice.  Just pass the start index.
833        if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
834          this.debug('globstar found match!', fr, fl, swallowee)
835          // found a match.
836          return true
837        } else {
838          // can't swallow "." or ".." ever.
839          // can only swallow ".foo" when explicitly asked.
840          if (swallowee === '.' || swallowee === '..' ||
841            (!options.dot && swallowee.charAt(0) === '.')) {
842            this.debug('dot detected!', file, fr, pattern, pr)
843            break
844          }
845
846          // ** swallows a segment, and continue.
847          this.debug('globstar swallow a segment, and continue')
848          fr++
849        }
850      }
851
852      // no match was found.
853      // However, in partial mode, we can't say this is necessarily over.
854      // If there's more *pattern* left, then
855      if (partial) {
856        // ran out of file
857        this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
858        if (fr === fl) return true
859      }
860      return false
861    }
862
863    // something other than **
864    // non-magic patterns just have to match exactly
865    // patterns with magic have been turned into regexps.
866    var hit
867    if (typeof p === 'string') {
868      if (options.nocase) {
869        hit = f.toLowerCase() === p.toLowerCase()
870      } else {
871        hit = f === p
872      }
873      this.debug('string match', p, f, hit)
874    } else {
875      hit = f.match(p)
876      this.debug('pattern match', p, f, hit)
877    }
878
879    if (!hit) return false
880  }
881
882  // Note: ending in / means that we'll get a final ""
883  // at the end of the pattern.  This can only match a
884  // corresponding "" at the end of the file.
885  // If the file ends in /, then it can only match a
886  // a pattern that ends in /, unless the pattern just
887  // doesn't have any more for it. But, a/b/ should *not*
888  // match "a/b/*", even though "" matches against the
889  // [^/]*? pattern, except in partial mode, where it might
890  // simply not be reached yet.
891  // However, a/b/ should still satisfy a/*
892
893  // now either we fell off the end of the pattern, or we're done.
894  if (fi === fl && pi === pl) {
895    // ran out of pattern and filename at the same time.
896    // an exact hit!
897    return true
898  } else if (fi === fl) {
899    // ran out of file, but still had pattern left.
900    // this is ok if we're doing the match as part of
901    // a glob fs traversal.
902    return partial
903  } else if (pi === pl) {
904    // ran out of pattern, still have file left.
905    // this is only acceptable if we're on the very last
906    // empty segment of a file with a trailing slash.
907    // a/* should match a/b/
908    var emptyFileEnd = (fi === fl - 1) && (file[fi] === '')
909    return emptyFileEnd
910  }
911
912  // should be unreachable.
913  throw new Error('wtf?')
914}
915
916// replace stuff like \* with *
917function globUnescape (s) {
918  return s.replace(/\\(.)/g, '$1')
919}
920
921function regExpEscape (s) {
922  return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
923}
924