| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 | // A thing that emits "entry" events with Reader objects// Pausing it causes it to stop emitting entry events, and also// pauses the current entry if there is one.module.exports = DirReadervar fs = require('graceful-fs')var inherits = require('inherits')var path = require('path')var Reader = require('./reader.js')var assert = require('assert').okinherits(DirReader, Reader)function DirReader (props) {  var self = this  if (!(self instanceof DirReader)) {    throw new Error('DirReader must be called as constructor.')  }  // should already be established as a Directory type  if (props.type !== 'Directory' || !props.Directory) {    throw new Error('Non-directory type ' + props.type)  }  self.entries = null  self._index = -1  self._paused = false  self._length = -1  if (props.sort) {    this.sort = props.sort  }  Reader.call(this, props)}DirReader.prototype._getEntries = function () {  var self = this  // race condition.  might pause() before calling _getEntries,  // and then resume, and try to get them a second time.  if (self._gotEntries) return  self._gotEntries = true  fs.readdir(self._path, function (er, entries) {    if (er) return self.error(er)    self.entries = entries    self.emit('entries', entries)    if (self._paused) self.once('resume', processEntries)    else processEntries()    function processEntries () {      self._length = self.entries.length      if (typeof self.sort === 'function') {        self.entries = self.entries.sort(self.sort.bind(self))      }      self._read()    }  })}// start walking the dir, and emit an "entry" event for each one.DirReader.prototype._read = function () {  var self = this  if (!self.entries) return self._getEntries()  if (self._paused || self._currentEntry || self._aborted) {    // console.error('DR paused=%j, current=%j, aborted=%j', self._paused, !!self._currentEntry, self._aborted)    return  }  self._index++  if (self._index >= self.entries.length) {    if (!self._ended) {      self._ended = true      self.emit('end')      self.emit('close')    }    return  }  // ok, handle this one, then.  // save creating a proxy, by stat'ing the thing now.  var p = path.resolve(self._path, self.entries[self._index])  assert(p !== self._path)  assert(self.entries[self._index])  // set this to prevent trying to _read() again in the stat time.  self._currentEntry = p  fs[ self.props.follow ? 'stat' : 'lstat' ](p, function (er, stat) {    if (er) return self.error(er)    var who = self._proxy || self    stat.path = p    stat.basename = path.basename(p)    stat.dirname = path.dirname(p)    var childProps = self.getChildProps.call(who, stat)    childProps.path = p    childProps.basename = path.basename(p)    childProps.dirname = path.dirname(p)    var entry = Reader(childProps, stat)    // console.error("DR Entry", p, stat.size)    self._currentEntry = entry    // "entry" events are for direct entries in a specific dir.    // "child" events are for any and all children at all levels.    // This nomenclature is not completely final.    entry.on('pause', function (who) {      if (!self._paused && !entry._disowned) {        self.pause(who)      }    })    entry.on('resume', function (who) {      if (self._paused && !entry._disowned) {        self.resume(who)      }    })    entry.on('stat', function (props) {      self.emit('_entryStat', entry, props)      if (entry._aborted) return      if (entry._paused) {        entry.once('resume', function () {          self.emit('entryStat', entry, props)        })      } else self.emit('entryStat', entry, props)    })    entry.on('ready', function EMITCHILD () {      // console.error("DR emit child", entry._path)      if (self._paused) {        // console.error("  DR emit child - try again later")        // pause the child, and emit the "entry" event once we drain.        // console.error("DR pausing child entry")        entry.pause(self)        return self.once('resume', EMITCHILD)      }      // skip over sockets.  they can't be piped around properly,      // so there's really no sense even acknowledging them.      // if someone really wants to see them, they can listen to      // the "socket" events.      if (entry.type === 'Socket') {        self.emit('socket', entry)      } else {        self.emitEntry(entry)      }    })    var ended = false    entry.on('close', onend)    entry.on('disown', onend)    function onend () {      if (ended) return      ended = true      self.emit('childEnd', entry)      self.emit('entryEnd', entry)      self._currentEntry = null      if (!self._paused) {        self._read()      }    }    // XXX Remove this.  Works in node as of 0.6.2 or so.    // Long filenames should not break stuff.    entry.on('error', function (er) {      if (entry._swallowErrors) {        self.warn(er)        entry.emit('end')        entry.emit('close')      } else {        self.emit('error', er)      }    })    // proxy up some events.    ;[      'child',      'childEnd',      'warn'    ].forEach(function (ev) {      entry.on(ev, self.emit.bind(self, ev))    })  })}DirReader.prototype.disown = function (entry) {  entry.emit('beforeDisown')  entry._disowned = true  entry.parent = entry.root = null  if (entry === this._currentEntry) {    this._currentEntry = null  }  entry.emit('disown')}DirReader.prototype.getChildProps = function () {  return {    depth: this.depth + 1,    root: this.root || this,    parent: this,    follow: this.follow,    filter: this.filter,    sort: this.props.sort,    hardlinks: this.props.hardlinks  }}DirReader.prototype.pause = function (who) {  var self = this  if (self._paused) return  who = who || self  self._paused = true  if (self._currentEntry && self._currentEntry.pause) {    self._currentEntry.pause(who)  }  self.emit('pause', who)}DirReader.prototype.resume = function (who) {  var self = this  if (!self._paused) return  who = who || self  self._paused = false  // console.error('DR Emit Resume', self._path)  self.emit('resume', who)  if (self._paused) {    // console.error('DR Re-paused', self._path)    return  }  if (self._currentEntry) {    if (self._currentEntry.resume) self._currentEntry.resume(who)  } else self._read()}DirReader.prototype.emitEntry = function (entry) {  this.emit('entry', entry)  this.emit('child', entry)}
 |