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) }