You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							255 lines
						
					
					
						
							6.9 KiB
						
					
					
				
			
		
		
	
	
							255 lines
						
					
					
						
							6.9 KiB
						
					
					
				module.exports = Reader | 
						|
 | 
						|
var fs = require('graceful-fs') | 
						|
var Stream = require('stream').Stream | 
						|
var 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 classes | 
						|
inherits(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) | 
						|
}
 | 
						|
 |