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 }