npm: Upgrade to 1.3.17
[platform/upstream/nodejs.git] / deps / npm / node_modules / fstream / lib / reader.js
1
2 module.exports = Reader
3
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")
11
12 // Must do this *before* loading the child classes
13 inherits(Reader, Abstract)
14
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")
20
21 function Reader (props, currentStat) {
22   var me = this
23   if (!(me instanceof Reader)) return new Reader(props, currentStat)
24
25   if (typeof props === "string") {
26     props = { path: props }
27   }
28
29   if (!props.path) {
30     me.error("Must provide a path", null, true)
31   }
32
33   // polymorphism.
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.
38
39
40   var type
41     , ClassType
42
43   if (props.type && typeof props.type === "function") {
44     type = props.type
45     ClassType = type
46   } else {
47     type = getType(props)
48     ClassType = Reader
49   }
50
51   if (currentStat && !type) {
52     type = getType(currentStat)
53     props[type] = true
54     props.type = type
55   }
56
57   switch (type) {
58     case "Directory":
59       ClassType = DirReader
60       break
61
62     case "Link":
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
69       // break
70
71     case "File":
72       ClassType = FileReader
73       break
74
75     case "SymbolicLink":
76       ClassType = LinkReader
77       break
78
79     case "Socket":
80       ClassType = SocketReader
81       break
82
83     case null:
84       ClassType = ProxyReader
85       break
86   }
87
88   if (!(me instanceof ClassType)) {
89     return new ClassType(props)
90   }
91
92   Abstract.call(me)
93
94   me.readable = true
95   me.writable = false
96
97   me.type = type
98   me.props = 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
102
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, "\\")
112       //}
113     }
114   }
115   me.basename = props.basename = path.basename(me.path)
116   me.dirname = props.dirname = path.dirname(me.path)
117
118   // these have served their purpose, and are now just noisy clutter
119   props.parent = props.root = null
120
121   // console.error("\n\n\n%s setting size to", props.path, props.size)
122   me.size = props.size
123   me.filter = typeof props.filter === "function" ? props.filter : null
124   if (props.sort === "alpha") props.sort = alphasort
125
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)
131 }
132
133 function alphasort (a, b) {
134   return a === b ? 0
135        : a.toLowerCase() > b.toLowerCase() ? 1
136        : a.toLowerCase() < b.toLowerCase() ? -1
137        : a > b ? 1
138        : -1
139 }
140
141 Reader.prototype._stat = function (currentStat) {
142   var me = this
143     , props = me.props
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)
148
149
150   function statCb (er, props_) {
151     // console.error("Reader._stat, statCb", me._path, props_, props_.nlink)
152     if (er) return me.error(er)
153
154     Object.keys(props_).forEach(function (k) {
155       props[k] = props_[k]
156     })
157
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")
161     }
162     me.size = props.size
163
164     var type = getType(props)
165     var handleHardlinks = props.hardlinks !== false
166     
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
172       else {
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
181       }
182     }
183
184     if (me.type && me.type !== type) {
185       me.error("Unexpected type: " + type)
186     }
187
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.
190     if (me.filter) {
191       var who = me._proxy || me
192       // special handling for ProxyReaders
193       if (!me.filter.call(who, who, props)) {
194         if (!me._disowned) {
195           me.abort()
196           me.emit("end")
197           me.emit("close")
198         }
199         return
200       }
201     }
202
203     // last chance to abort or disown before the flow starts!
204     var events = ["_stat", "stat", "ready"]
205     var e = 0
206     ;(function go () {
207       if (me._aborted) {
208         me.emit("end")
209         me.emit("close")
210         return
211       }
212
213       if (me._paused) {
214         me.once("resume", go)
215         return
216       }
217
218       var ev = events[e ++]
219       if (!ev) return me._read()
220       me.emit(ev, props)
221       go()
222     })()
223   }
224 }
225
226 Reader.prototype.pipe = function (dest, opts) {
227   var me = this
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)
232       if (false === ret) {
233         me.pause()
234       }
235     })
236   }
237
238   // console.error("R Pipe apply Stream Pipe")
239   return Stream.prototype.pipe.apply(this, arguments)
240 }
241
242 Reader.prototype.pause = function (who) {
243   this._paused = true
244   who = who || this
245   this.emit("pause", who)
246   if (this._stream) this._stream.pause(who)
247 }
248
249 Reader.prototype.resume = function (who) {
250   this._paused = false
251   who = who || this
252   this.emit("resume", who)
253   if (this._stream) this._stream.resume(who)
254   this._read()
255 }
256
257 Reader.prototype._read = function () {
258   this.error("Cannot read unknown type: "+this.type)
259 }
260