Version 1
[yaffs-website] / node_modules / glob / sync.js
1 module.exports = globSync
2 globSync.GlobSync = GlobSync
3
4 var fs = require('fs')
5 var rp = require('fs.realpath')
6 var minimatch = require('minimatch')
7 var Minimatch = minimatch.Minimatch
8 var Glob = require('./glob.js').Glob
9 var util = require('util')
10 var path = require('path')
11 var assert = require('assert')
12 var isAbsolute = require('path-is-absolute')
13 var common = require('./common.js')
14 var alphasort = common.alphasort
15 var alphasorti = common.alphasorti
16 var setopts = common.setopts
17 var ownProp = common.ownProp
18 var childrenIgnored = common.childrenIgnored
19
20 function globSync (pattern, options) {
21   if (typeof options === 'function' || arguments.length === 3)
22     throw new TypeError('callback provided to sync glob\n'+
23                         'See: https://github.com/isaacs/node-glob/issues/167')
24
25   return new GlobSync(pattern, options).found
26 }
27
28 function GlobSync (pattern, options) {
29   if (!pattern)
30     throw new Error('must provide pattern')
31
32   if (typeof options === 'function' || arguments.length === 3)
33     throw new TypeError('callback provided to sync glob\n'+
34                         'See: https://github.com/isaacs/node-glob/issues/167')
35
36   if (!(this instanceof GlobSync))
37     return new GlobSync(pattern, options)
38
39   setopts(this, pattern, options)
40
41   if (this.noprocess)
42     return this
43
44   var n = this.minimatch.set.length
45   this.matches = new Array(n)
46   for (var i = 0; i < n; i ++) {
47     this._process(this.minimatch.set[i], i, false)
48   }
49   this._finish()
50 }
51
52 GlobSync.prototype._finish = function () {
53   assert(this instanceof GlobSync)
54   if (this.realpath) {
55     var self = this
56     this.matches.forEach(function (matchset, index) {
57       var set = self.matches[index] = Object.create(null)
58       for (var p in matchset) {
59         try {
60           p = self._makeAbs(p)
61           var real = rp.realpathSync(p, self.realpathCache)
62           set[real] = true
63         } catch (er) {
64           if (er.syscall === 'stat')
65             set[self._makeAbs(p)] = true
66           else
67             throw er
68         }
69       }
70     })
71   }
72   common.finish(this)
73 }
74
75
76 GlobSync.prototype._process = function (pattern, index, inGlobStar) {
77   assert(this instanceof GlobSync)
78
79   // Get the first [n] parts of pattern that are all strings.
80   var n = 0
81   while (typeof pattern[n] === 'string') {
82     n ++
83   }
84   // now n is the index of the first one that is *not* a string.
85
86   // See if there's anything else
87   var prefix
88   switch (n) {
89     // if not, then this is rather simple
90     case pattern.length:
91       this._processSimple(pattern.join('/'), index)
92       return
93
94     case 0:
95       // pattern *starts* with some non-trivial item.
96       // going to readdir(cwd), but not include the prefix in matches.
97       prefix = null
98       break
99
100     default:
101       // pattern has some string bits in the front.
102       // whatever it starts with, whether that's 'absolute' like /foo/bar,
103       // or 'relative' like '../baz'
104       prefix = pattern.slice(0, n).join('/')
105       break
106   }
107
108   var remain = pattern.slice(n)
109
110   // get the list of entries.
111   var read
112   if (prefix === null)
113     read = '.'
114   else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
115     if (!prefix || !isAbsolute(prefix))
116       prefix = '/' + prefix
117     read = prefix
118   } else
119     read = prefix
120
121   var abs = this._makeAbs(read)
122
123   //if ignored, skip processing
124   if (childrenIgnored(this, read))
125     return
126
127   var isGlobStar = remain[0] === minimatch.GLOBSTAR
128   if (isGlobStar)
129     this._processGlobStar(prefix, read, abs, remain, index, inGlobStar)
130   else
131     this._processReaddir(prefix, read, abs, remain, index, inGlobStar)
132 }
133
134
135 GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) {
136   var entries = this._readdir(abs, inGlobStar)
137
138   // if the abs isn't a dir, then nothing can match!
139   if (!entries)
140     return
141
142   // It will only match dot entries if it starts with a dot, or if
143   // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
144   var pn = remain[0]
145   var negate = !!this.minimatch.negate
146   var rawGlob = pn._glob
147   var dotOk = this.dot || rawGlob.charAt(0) === '.'
148
149   var matchedEntries = []
150   for (var i = 0; i < entries.length; i++) {
151     var e = entries[i]
152     if (e.charAt(0) !== '.' || dotOk) {
153       var m
154       if (negate && !prefix) {
155         m = !e.match(pn)
156       } else {
157         m = e.match(pn)
158       }
159       if (m)
160         matchedEntries.push(e)
161     }
162   }
163
164   var len = matchedEntries.length
165   // If there are no matched entries, then nothing matches.
166   if (len === 0)
167     return
168
169   // if this is the last remaining pattern bit, then no need for
170   // an additional stat *unless* the user has specified mark or
171   // stat explicitly.  We know they exist, since readdir returned
172   // them.
173
174   if (remain.length === 1 && !this.mark && !this.stat) {
175     if (!this.matches[index])
176       this.matches[index] = Object.create(null)
177
178     for (var i = 0; i < len; i ++) {
179       var e = matchedEntries[i]
180       if (prefix) {
181         if (prefix.slice(-1) !== '/')
182           e = prefix + '/' + e
183         else
184           e = prefix + e
185       }
186
187       if (e.charAt(0) === '/' && !this.nomount) {
188         e = path.join(this.root, e)
189       }
190       this.matches[index][e] = true
191     }
192     // This was the last one, and no stats were needed
193     return
194   }
195
196   // now test all matched entries as stand-ins for that part
197   // of the pattern.
198   remain.shift()
199   for (var i = 0; i < len; i ++) {
200     var e = matchedEntries[i]
201     var newPattern
202     if (prefix)
203       newPattern = [prefix, e]
204     else
205       newPattern = [e]
206     this._process(newPattern.concat(remain), index, inGlobStar)
207   }
208 }
209
210
211 GlobSync.prototype._emitMatch = function (index, e) {
212   var abs = this._makeAbs(e)
213   if (this.mark)
214     e = this._mark(e)
215
216   if (this.matches[index][e])
217     return
218
219   if (this.nodir) {
220     var c = this.cache[this._makeAbs(e)]
221     if (c === 'DIR' || Array.isArray(c))
222       return
223   }
224
225   this.matches[index][e] = true
226   if (this.stat)
227     this._stat(e)
228 }
229
230
231 GlobSync.prototype._readdirInGlobStar = function (abs) {
232   // follow all symlinked directories forever
233   // just proceed as if this is a non-globstar situation
234   if (this.follow)
235     return this._readdir(abs, false)
236
237   var entries
238   var lstat
239   var stat
240   try {
241     lstat = fs.lstatSync(abs)
242   } catch (er) {
243     // lstat failed, doesn't exist
244     return null
245   }
246
247   var isSym = lstat.isSymbolicLink()
248   this.symlinks[abs] = isSym
249
250   // If it's not a symlink or a dir, then it's definitely a regular file.
251   // don't bother doing a readdir in that case.
252   if (!isSym && !lstat.isDirectory())
253     this.cache[abs] = 'FILE'
254   else
255     entries = this._readdir(abs, false)
256
257   return entries
258 }
259
260 GlobSync.prototype._readdir = function (abs, inGlobStar) {
261   var entries
262
263   if (inGlobStar && !ownProp(this.symlinks, abs))
264     return this._readdirInGlobStar(abs)
265
266   if (ownProp(this.cache, abs)) {
267     var c = this.cache[abs]
268     if (!c || c === 'FILE')
269       return null
270
271     if (Array.isArray(c))
272       return c
273   }
274
275   try {
276     return this._readdirEntries(abs, fs.readdirSync(abs))
277   } catch (er) {
278     this._readdirError(abs, er)
279     return null
280   }
281 }
282
283 GlobSync.prototype._readdirEntries = function (abs, entries) {
284   // if we haven't asked to stat everything, then just
285   // assume that everything in there exists, so we can avoid
286   // having to stat it a second time.
287   if (!this.mark && !this.stat) {
288     for (var i = 0; i < entries.length; i ++) {
289       var e = entries[i]
290       if (abs === '/')
291         e = abs + e
292       else
293         e = abs + '/' + e
294       this.cache[e] = true
295     }
296   }
297
298   this.cache[abs] = entries
299
300   // mark and cache dir-ness
301   return entries
302 }
303
304 GlobSync.prototype._readdirError = function (f, er) {
305   // handle errors, and cache the information
306   switch (er.code) {
307     case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
308     case 'ENOTDIR': // totally normal. means it *does* exist.
309       var abs = this._makeAbs(f)
310       this.cache[abs] = 'FILE'
311       if (abs === this.cwdAbs) {
312         var error = new Error(er.code + ' invalid cwd ' + this.cwd)
313         error.path = this.cwd
314         error.code = er.code
315         throw error
316       }
317       break
318
319     case 'ENOENT': // not terribly unusual
320     case 'ELOOP':
321     case 'ENAMETOOLONG':
322     case 'UNKNOWN':
323       this.cache[this._makeAbs(f)] = false
324       break
325
326     default: // some unusual error.  Treat as failure.
327       this.cache[this._makeAbs(f)] = false
328       if (this.strict)
329         throw er
330       if (!this.silent)
331         console.error('glob error', er)
332       break
333   }
334 }
335
336 GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
337
338   var entries = this._readdir(abs, inGlobStar)
339
340   // no entries means not a dir, so it can never have matches
341   // foo.txt/** doesn't match foo.txt
342   if (!entries)
343     return
344
345   // test without the globstar, and with every child both below
346   // and replacing the globstar.
347   var remainWithoutGlobStar = remain.slice(1)
348   var gspref = prefix ? [ prefix ] : []
349   var noGlobStar = gspref.concat(remainWithoutGlobStar)
350
351   // the noGlobStar pattern exits the inGlobStar state
352   this._process(noGlobStar, index, false)
353
354   var len = entries.length
355   var isSym = this.symlinks[abs]
356
357   // If it's a symlink, and we're in a globstar, then stop
358   if (isSym && inGlobStar)
359     return
360
361   for (var i = 0; i < len; i++) {
362     var e = entries[i]
363     if (e.charAt(0) === '.' && !this.dot)
364       continue
365
366     // these two cases enter the inGlobStar state
367     var instead = gspref.concat(entries[i], remainWithoutGlobStar)
368     this._process(instead, index, true)
369
370     var below = gspref.concat(entries[i], remain)
371     this._process(below, index, true)
372   }
373 }
374
375 GlobSync.prototype._processSimple = function (prefix, index) {
376   // XXX review this.  Shouldn't it be doing the mounting etc
377   // before doing stat?  kinda weird?
378   var exists = this._stat(prefix)
379
380   if (!this.matches[index])
381     this.matches[index] = Object.create(null)
382
383   // If it doesn't exist, then just mark the lack of results
384   if (!exists)
385     return
386
387   if (prefix && isAbsolute(prefix) && !this.nomount) {
388     var trail = /[\/\\]$/.test(prefix)
389     if (prefix.charAt(0) === '/') {
390       prefix = path.join(this.root, prefix)
391     } else {
392       prefix = path.resolve(this.root, prefix)
393       if (trail)
394         prefix += '/'
395     }
396   }
397
398   if (process.platform === 'win32')
399     prefix = prefix.replace(/\\/g, '/')
400
401   // Mark this as a match
402   this.matches[index][prefix] = true
403 }
404
405 // Returns either 'DIR', 'FILE', or false
406 GlobSync.prototype._stat = function (f) {
407   var abs = this._makeAbs(f)
408   var needDir = f.slice(-1) === '/'
409
410   if (f.length > this.maxLength)
411     return false
412
413   if (!this.stat && ownProp(this.cache, abs)) {
414     var c = this.cache[abs]
415
416     if (Array.isArray(c))
417       c = 'DIR'
418
419     // It exists, but maybe not how we need it
420     if (!needDir || c === 'DIR')
421       return c
422
423     if (needDir && c === 'FILE')
424       return false
425
426     // otherwise we have to stat, because maybe c=true
427     // if we know it exists, but not what it is.
428   }
429
430   var exists
431   var stat = this.statCache[abs]
432   if (!stat) {
433     var lstat
434     try {
435       lstat = fs.lstatSync(abs)
436     } catch (er) {
437       return false
438     }
439
440     if (lstat.isSymbolicLink()) {
441       try {
442         stat = fs.statSync(abs)
443       } catch (er) {
444         stat = lstat
445       }
446     } else {
447       stat = lstat
448     }
449   }
450
451   this.statCache[abs] = stat
452
453   var c = stat.isDirectory() ? 'DIR' : 'FILE'
454   this.cache[abs] = this.cache[abs] || c
455
456   if (needDir && c !== 'DIR')
457     return false
458
459   return c
460 }
461
462 GlobSync.prototype._mark = function (p) {
463   return common.mark(this, p)
464 }
465
466 GlobSync.prototype._makeAbs = function (f) {
467   return common.makeAbs(this, f)
468 }