UNPKG

25.3 kBJavaScriptView Raw
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}