| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 | module.exports = Readervar fs = require('graceful-fs')var Stream = require('stream').Streamvar inherits = require('inherits')var path = require('path')var getType = require('./get-type.js')var hardLinks = Reader.hardLinks = {}var Abstract = require('./abstract.js')// Must do this *before* loading the child classesinherits(Reader, Abstract)var LinkReader = require('./link-reader.js')function Reader (props, currentStat) {  var self = this  if (!(self instanceof Reader)) return new Reader(props, currentStat)  if (typeof props === 'string') {    props = { path: props }  }  // polymorphism.  // call fstream.Reader(dir) to get a DirReader object, etc.  // Note that, unlike in the Writer case, ProxyReader is going  // to be the *normal* state of affairs, since we rarely know  // the type of a file prior to reading it.  var type  var ClassType  if (props.type && typeof props.type === 'function') {    type = props.type    ClassType = type  } else {    type = getType(props)    ClassType = Reader  }  if (currentStat && !type) {    type = getType(currentStat)    props[type] = true    props.type = type  }  switch (type) {    case 'Directory':      ClassType = require('./dir-reader.js')      break    case 'Link':    // XXX hard links are just files.    // However, it would be good to keep track of files' dev+inode    // and nlink values, and create a HardLinkReader that emits    // a linkpath value of the original copy, so that the tar    // writer can preserve them.    // ClassType = HardLinkReader    // break    case 'File':      ClassType = require('./file-reader.js')      break    case 'SymbolicLink':      ClassType = LinkReader      break    case 'Socket':      ClassType = require('./socket-reader.js')      break    case null:      ClassType = require('./proxy-reader.js')      break  }  if (!(self instanceof ClassType)) {    return new ClassType(props)  }  Abstract.call(self)  if (!props.path) {    self.error('Must provide a path', null, true)  }  self.readable = true  self.writable = false  self.type = type  self.props = props  self.depth = props.depth = props.depth || 0  self.parent = props.parent || null  self.root = props.root || (props.parent && props.parent.root) || self  self._path = self.path = path.resolve(props.path)  if (process.platform === 'win32') {    self.path = self._path = self.path.replace(/\?/g, '_')    if (self._path.length >= 260) {      // how DOES one create files on the moon?      // if the path has spaces in it, then UNC will fail.      self._swallowErrors = true      // if (self._path.indexOf(" ") === -1) {      self._path = '\\\\?\\' + self.path.replace(/\//g, '\\')    // }    }  }  self.basename = props.basename = path.basename(self.path)  self.dirname = props.dirname = path.dirname(self.path)  // these have served their purpose, and are now just noisy clutter  props.parent = props.root = null  // console.error("\n\n\n%s setting size to", props.path, props.size)  self.size = props.size  self.filter = typeof props.filter === 'function' ? props.filter : null  if (props.sort === 'alpha') props.sort = alphasort  // start the ball rolling.  // this will stat the thing, and then call self._read()  // to start reading whatever it is.  // console.error("calling stat", props.path, currentStat)  self._stat(currentStat)}function alphasort (a, b) {  return a === b ? 0    : a.toLowerCase() > b.toLowerCase() ? 1      : a.toLowerCase() < b.toLowerCase() ? -1        : a > b ? 1          : -1}Reader.prototype._stat = function (currentStat) {  var self = this  var props = self.props  var stat = props.follow ? 'stat' : 'lstat'  // console.error("Reader._stat", self._path, currentStat)  if (currentStat) process.nextTick(statCb.bind(null, null, currentStat))  else fs[stat](self._path, statCb)  function statCb (er, props_) {    // console.error("Reader._stat, statCb", self._path, props_, props_.nlink)    if (er) return self.error(er)    Object.keys(props_).forEach(function (k) {      props[k] = props_[k]    })    // if it's not the expected size, then abort here.    if (undefined !== self.size && props.size !== self.size) {      return self.error('incorrect size')    }    self.size = props.size    var type = getType(props)    var handleHardlinks = props.hardlinks !== false    // special little thing for handling hardlinks.    if (handleHardlinks && type !== 'Directory' && props.nlink && props.nlink > 1) {      var k = props.dev + ':' + props.ino      // console.error("Reader has nlink", self._path, k)      if (hardLinks[k] === self._path || !hardLinks[k]) {        hardLinks[k] = self._path      } else {        // switch into hardlink mode.        type = self.type = self.props.type = 'Link'        self.Link = self.props.Link = true        self.linkpath = self.props.linkpath = hardLinks[k]        // console.error("Hardlink detected, switching mode", self._path, self.linkpath)        // Setting __proto__ would arguably be the "correct"        // approach here, but that just seems too wrong.        self._stat = self._read = LinkReader.prototype._read      }    }    if (self.type && self.type !== type) {      self.error('Unexpected type: ' + type)    }    // if the filter doesn't pass, then just skip over this one.    // still have to emit end so that dir-walking can move on.    if (self.filter) {      var who = self._proxy || self      // special handling for ProxyReaders      if (!self.filter.call(who, who, props)) {        if (!self._disowned) {          self.abort()          self.emit('end')          self.emit('close')        }        return      }    }    // last chance to abort or disown before the flow starts!    var events = ['_stat', 'stat', 'ready']    var e = 0    ;(function go () {      if (self._aborted) {        self.emit('end')        self.emit('close')        return      }      if (self._paused && self.type !== 'Directory') {        self.once('resume', go)        return      }      var ev = events[e++]      if (!ev) {        return self._read()      }      self.emit(ev, props)      go()    })()  }}Reader.prototype.pipe = function (dest) {  var self = this  if (typeof dest.add === 'function') {    // piping to a multi-compatible, and we've got directory entries.    self.on('entry', function (entry) {      var ret = dest.add(entry)      if (ret === false) {        self.pause()      }    })  }  // console.error("R Pipe apply Stream Pipe")  return Stream.prototype.pipe.apply(this, arguments)}Reader.prototype.pause = function (who) {  this._paused = true  who = who || this  this.emit('pause', who)  if (this._stream) this._stream.pause(who)}Reader.prototype.resume = function (who) {  this._paused = false  who = who || this  this.emit('resume', who)  if (this._stream) this._stream.resume(who)  this._read()}Reader.prototype._read = function () {  this.error('Cannot read unknown type: ' + this.type)}
 |