1 module.exports = Reader
3 var fs = require('graceful-fs')
4 var Stream = require('stream').Stream
5 var inherits = require('inherits')
6 var path = require('path')
7 var getType = require('./get-type.js')
8 var hardLinks = Reader.hardLinks = {}
9 var Abstract = require('./abstract.js')
11 // Must do this *before* loading the child classes
12 inherits(Reader, Abstract)
14 var LinkReader = require('./link-reader.js')
16 function Reader (props, currentStat) {
18 if (!(self instanceof Reader)) return new Reader(props, currentStat)
20 if (typeof props === 'string') {
21 props = { path: props }
25 self.error('Must provide a path', null, true)
29 // call fstream.Reader(dir) to get a DirReader object, etc.
30 // Note that, unlike in the Writer case, ProxyReader is going
31 // to be the *normal* state of affairs, since we rarely know
32 // the type of a file prior to reading it.
37 if (props.type && typeof props.type === 'function') {
45 if (currentStat && !type) {
46 type = getType(currentStat)
53 ClassType = require('./dir-reader.js')
57 // XXX hard links are just files.
58 // However, it would be good to keep track of files' dev+inode
59 // and nlink values, and create a HardLinkReader that emits
60 // a linkpath value of the original copy, so that the tar
61 // writer can preserve them.
62 // ClassType = HardLinkReader
66 ClassType = require('./file-reader.js')
70 ClassType = LinkReader
74 ClassType = require('./socket-reader.js')
78 ClassType = require('./proxy-reader.js')
82 if (!(self instanceof ClassType)) {
83 return new ClassType(props)
93 self.depth = props.depth = props.depth || 0
94 self.parent = props.parent || null
95 self.root = props.root || (props.parent && props.parent.root) || self
97 self._path = self.path = path.resolve(props.path)
98 if (process.platform === 'win32') {
99 self.path = self._path = self.path.replace(/\?/g, '_')
100 if (self._path.length >= 260) {
101 // how DOES one create files on the moon?
102 // if the path has spaces in it, then UNC will fail.
103 self._swallowErrors = true
104 // if (self._path.indexOf(" ") === -1) {
105 self._path = '\\\\?\\' + self.path.replace(/\//g, '\\')
109 self.basename = props.basename = path.basename(self.path)
110 self.dirname = props.dirname = path.dirname(self.path)
112 // these have served their purpose, and are now just noisy clutter
113 props.parent = props.root = null
115 // console.error("\n\n\n%s setting size to", props.path, props.size)
116 self.size = props.size
117 self.filter = typeof props.filter === 'function' ? props.filter : null
118 if (props.sort === 'alpha') props.sort = alphasort
120 // start the ball rolling.
121 // this will stat the thing, and then call self._read()
122 // to start reading whatever it is.
123 // console.error("calling stat", props.path, currentStat)
124 self._stat(currentStat)
127 function alphasort (a, b) {
129 : a.toLowerCase() > b.toLowerCase() ? 1
130 : a.toLowerCase() < b.toLowerCase() ? -1
135 Reader.prototype._stat = function (currentStat) {
137 var props = self.props
138 var stat = props.follow ? 'stat' : 'lstat'
139 // console.error("Reader._stat", self._path, currentStat)
140 if (currentStat) process.nextTick(statCb.bind(null, null, currentStat))
141 else fs[stat](self._path, statCb)
143 function statCb (er, props_) {
144 // console.error("Reader._stat, statCb", self._path, props_, props_.nlink)
145 if (er) return self.error(er)
147 Object.keys(props_).forEach(function (k) {
151 // if it's not the expected size, then abort here.
152 if (undefined !== self.size && props.size !== self.size) {
153 return self.error('incorrect size')
155 self.size = props.size
157 var type = getType(props)
158 var handleHardlinks = props.hardlinks !== false
160 // special little thing for handling hardlinks.
161 if (handleHardlinks && type !== 'Directory' && props.nlink && props.nlink > 1) {
162 var k = props.dev + ':' + props.ino
163 // console.error("Reader has nlink", self._path, k)
164 if (hardLinks[k] === self._path || !hardLinks[k]) {
165 hardLinks[k] = self._path
167 // switch into hardlink mode.
168 type = self.type = self.props.type = 'Link'
169 self.Link = self.props.Link = true
170 self.linkpath = self.props.linkpath = hardLinks[k]
171 // console.error("Hardlink detected, switching mode", self._path, self.linkpath)
172 // Setting __proto__ would arguably be the "correct"
173 // approach here, but that just seems too wrong.
174 self._stat = self._read = LinkReader.prototype._read
178 if (self.type && self.type !== type) {
179 self.error('Unexpected type: ' + type)
182 // if the filter doesn't pass, then just skip over this one.
183 // still have to emit end so that dir-walking can move on.
185 var who = self._proxy || self
186 // special handling for ProxyReaders
187 if (!self.filter.call(who, who, props)) {
188 if (!self._disowned) {
197 // last chance to abort or disown before the flow starts!
198 var events = ['_stat', 'stat', 'ready']
207 if (self._paused && self.type !== 'Directory') {
208 self.once('resume', go)
222 Reader.prototype.pipe = function (dest) {
224 if (typeof dest.add === 'function') {
225 // piping to a multi-compatible, and we've got directory entries.
226 self.on('entry', function (entry) {
227 var ret = dest.add(entry)
234 // console.error("R Pipe apply Stream Pipe")
235 return Stream.prototype.pipe.apply(this, arguments)
238 Reader.prototype.pause = function (who) {
241 this.emit('pause', who)
242 if (this._stream) this._stream.pause(who)
245 Reader.prototype.resume = function (who) {
248 this.emit('resume', who)
249 if (this._stream) this._stream.resume(who)
253 Reader.prototype._read = function () {
254 this.error('Cannot read unknown type: ' + this.type)