l0bsterssg

node.js static responsive blog post generator
Log | Files | Refs | README

minimatch.js (25347B)


      1 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 }