Projekt

Obecné

Profil

Stáhnout (24.8 KB) Statistiky
| Větev: | Revize:
1 3a515b92 cagy
module.exports = minimatch
2
minimatch.Minimatch = Minimatch
3
4
var path = { sep: '/' }
5
try {
6
  path = require('path')
7
} catch (er) {}
8
9
var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
10
var expand = require('brace-expansion')
11
12
var 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()
22
var qmark = '[^/]'
23
24
// * => any number of characters
25
var 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.
30
var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
31
32
// not a ^ or / followed by a dot,
33
// followed by anything, any number of times.
34
var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
35
36
// characters that need to be escaped in RegExp.
37
var reSpecials = charSet('().*{}+?[]^$\\!')
38
39
// "abc" -> { a:true, b:true, c:true }
40
function charSet (s) {
41
  return s.split('').reduce(function (set, c) {
42
    set[c] = true
43
    return set
44
  }, {})
45
}
46
47
// normalizes slashes.
48
var slashSplit = /\/+/
49
50
minimatch.filter = filter
51
function filter (pattern, options) {
52
  options = options || {}
53
  return function (p, i, list) {
54
    return minimatch(p, pattern, options)
55
  }
56
}
57
58
function 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
71
minimatch.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
87
Minimatch.defaults = function (def) {
88
  if (!def || !Object.keys(def).length) return Minimatch
89
  return minimatch.defaults(def).Minimatch
90
}
91
92
function 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
110
function 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
139
Minimatch.prototype.debug = function () {}
140
141
Minimatch.prototype.make = make
142
function 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
197
Minimatch.prototype.parseNegate = parseNegate
198
function 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
227
minimatch.braceExpand = function (pattern, options) {
228
  return braceExpand(pattern, options)
229
}
230
231
Minimatch.prototype.braceExpand = braceExpand
232
233
function 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.
269
Minimatch.prototype.parse = parse
270
var SUBPARSE = {}
271
function 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
637
minimatch.makeRe = function (pattern, options) {
638
  return new Minimatch(pattern, options || {}).makeRe()
639
}
640
641
Minimatch.prototype.makeRe = makeRe
642
function 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
687
minimatch.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
699
Minimatch.prototype.match = match
700
function 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.
760
Minimatch.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 *
917
function globUnescape (s) {
918
  return s.replace(/\\(.)/g, '$1')
919
}
920
921
function regExpEscape (s) {
922
  return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
923
}