2 module.exports = Reader
4 var fs = require("graceful-fs")
5 , Stream = require("stream").Stream
6 , inherits = require("inherits")
7 , path = require("path")
8 , getType = require("./get-type.js")
9 , hardLinks = Reader.hardLinks = {}
10 , Abstract = require("./abstract.js")
12 // Must do this *before* loading the child classes
13 inherits(Reader, Abstract)
15 var DirReader = require("./dir-reader.js")
16 , FileReader = require("./file-reader.js")
17 , LinkReader = require("./link-reader.js")
18 , SocketReader = require("./socket-reader.js")
19 , ProxyReader = require("./proxy-reader.js")
21 function Reader (props, currentStat) {
23 if (!(me instanceof Reader)) return new Reader(props, currentStat)
25 if (typeof props === "string") {
26 props = { path: props }
30 me.error("Must provide a path", null, true)
34 // call fstream.Reader(dir) to get a DirReader object, etc.
35 // Note that, unlike in the Writer case, ProxyReader is going
36 // to be the *normal* state of affairs, since we rarely know
37 // the type of a file prior to reading it.
43 if (props.type && typeof props.type === "function") {
51 if (currentStat && !type) {
52 type = getType(currentStat)
63 // XXX hard links are just files.
64 // However, it would be good to keep track of files' dev+inode
65 // and nlink values, and create a HardLinkReader that emits
66 // a linkpath value of the original copy, so that the tar
67 // writer can preserve them.
68 // ClassType = HardLinkReader
72 ClassType = FileReader
76 ClassType = LinkReader
80 ClassType = SocketReader
84 ClassType = ProxyReader
88 if (!(me instanceof ClassType)) {
89 return new ClassType(props)
99 me.depth = props.depth = props.depth || 0
100 me.parent = props.parent || null
101 me.root = props.root || (props.parent && props.parent.root) || me
103 me._path = me.path = path.resolve(props.path)
104 if (process.platform === "win32") {
105 me.path = me._path = me.path.replace(/\?/g, "_")
106 if (me._path.length >= 260) {
107 // how DOES one create files on the moon?
108 // if the path has spaces in it, then UNC will fail.
109 me._swallowErrors = true
110 //if (me._path.indexOf(" ") === -1) {
111 me._path = "\\\\?\\" + me.path.replace(/\//g, "\\")
115 me.basename = props.basename = path.basename(me.path)
116 me.dirname = props.dirname = path.dirname(me.path)
118 // these have served their purpose, and are now just noisy clutter
119 props.parent = props.root = null
121 // console.error("\n\n\n%s setting size to", props.path, props.size)
123 me.filter = typeof props.filter === "function" ? props.filter : null
124 if (props.sort === "alpha") props.sort = alphasort
126 // start the ball rolling.
127 // this will stat the thing, and then call me._read()
128 // to start reading whatever it is.
129 // console.error("calling stat", props.path, currentStat)
130 me._stat(currentStat)
133 function alphasort (a, b) {
135 : a.toLowerCase() > b.toLowerCase() ? 1
136 : a.toLowerCase() < b.toLowerCase() ? -1
141 Reader.prototype._stat = function (currentStat) {
144 , stat = props.follow ? "stat" : "lstat"
145 // console.error("Reader._stat", me._path, currentStat)
146 if (currentStat) process.nextTick(statCb.bind(null, null, currentStat))
147 else fs[stat](me._path, statCb)
150 function statCb (er, props_) {
151 // console.error("Reader._stat, statCb", me._path, props_, props_.nlink)
152 if (er) return me.error(er)
154 Object.keys(props_).forEach(function (k) {
158 // if it's not the expected size, then abort here.
159 if (undefined !== me.size && props.size !== me.size) {
160 return me.error("incorrect size")
164 var type = getType(props)
165 var handleHardlinks = props.hardlinks !== false
167 // special little thing for handling hardlinks.
168 if (handleHardlinks && type !== "Directory" && props.nlink && props.nlink > 1) {
169 var k = props.dev + ":" + props.ino
170 // console.error("Reader has nlink", me._path, k)
171 if (hardLinks[k] === me._path || !hardLinks[k]) hardLinks[k] = me._path
173 // switch into hardlink mode.
174 type = me.type = me.props.type = "Link"
175 me.Link = me.props.Link = true
176 me.linkpath = me.props.linkpath = hardLinks[k]
177 // console.error("Hardlink detected, switching mode", me._path, me.linkpath)
178 // Setting __proto__ would arguably be the "correct"
179 // approach here, but that just seems too wrong.
180 me._stat = me._read = LinkReader.prototype._read
184 if (me.type && me.type !== type) {
185 me.error("Unexpected type: " + type)
188 // if the filter doesn't pass, then just skip over this one.
189 // still have to emit end so that dir-walking can move on.
191 var who = me._proxy || me
192 // special handling for ProxyReaders
193 if (!me.filter.call(who, who, props)) {
203 // last chance to abort or disown before the flow starts!
204 var events = ["_stat", "stat", "ready"]
214 me.once("resume", go)
218 var ev = events[e ++]
219 if (!ev) return me._read()
226 Reader.prototype.pipe = function (dest, opts) {
228 if (typeof dest.add === "function") {
229 // piping to a multi-compatible, and we've got directory entries.
230 me.on("entry", function (entry) {
231 var ret = dest.add(entry)
238 // console.error("R Pipe apply Stream Pipe")
239 return Stream.prototype.pipe.apply(this, arguments)
242 Reader.prototype.pause = function (who) {
245 this.emit("pause", who)
246 if (this._stream) this._stream.pause(who)
249 Reader.prototype.resume = function (who) {
252 this.emit("resume", who)
253 if (this._stream) this._stream.resume(who)
257 Reader.prototype._read = function () {
258 this.error("Cannot read unknown type: "+this.type)