Initial commit
[yaffs-website] / node_modules / fstream / lib / reader.js
1 module.exports = Reader
2
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')
10
11 // Must do this *before* loading the child classes
12 inherits(Reader, Abstract)
13
14 var LinkReader = require('./link-reader.js')
15
16 function Reader (props, currentStat) {
17   var self = this
18   if (!(self instanceof Reader)) return new Reader(props, currentStat)
19
20   if (typeof props === 'string') {
21     props = { path: props }
22   }
23
24   // polymorphism.
25   // call fstream.Reader(dir) to get a DirReader object, etc.
26   // Note that, unlike in the Writer case, ProxyReader is going
27   // to be the *normal* state of affairs, since we rarely know
28   // the type of a file prior to reading it.
29
30   var type
31   var ClassType
32
33   if (props.type && typeof props.type === 'function') {
34     type = props.type
35     ClassType = type
36   } else {
37     type = getType(props)
38     ClassType = Reader
39   }
40
41   if (currentStat && !type) {
42     type = getType(currentStat)
43     props[type] = true
44     props.type = type
45   }
46
47   switch (type) {
48     case 'Directory':
49       ClassType = require('./dir-reader.js')
50       break
51
52     case 'Link':
53     // XXX hard links are just files.
54     // However, it would be good to keep track of files' dev+inode
55     // and nlink values, and create a HardLinkReader that emits
56     // a linkpath value of the original copy, so that the tar
57     // writer can preserve them.
58     // ClassType = HardLinkReader
59     // break
60
61     case 'File':
62       ClassType = require('./file-reader.js')
63       break
64
65     case 'SymbolicLink':
66       ClassType = LinkReader
67       break
68
69     case 'Socket':
70       ClassType = require('./socket-reader.js')
71       break
72
73     case null:
74       ClassType = require('./proxy-reader.js')
75       break
76   }
77
78   if (!(self instanceof ClassType)) {
79     return new ClassType(props)
80   }
81
82   Abstract.call(self)
83
84   if (!props.path) {
85     self.error('Must provide a path', null, true)
86   }
87
88   self.readable = true
89   self.writable = false
90
91   self.type = type
92   self.props = 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
96
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, '\\')
106     // }
107     }
108   }
109   self.basename = props.basename = path.basename(self.path)
110   self.dirname = props.dirname = path.dirname(self.path)
111
112   // these have served their purpose, and are now just noisy clutter
113   props.parent = props.root = null
114
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
119
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)
125 }
126
127 function alphasort (a, b) {
128   return a === b ? 0
129     : a.toLowerCase() > b.toLowerCase() ? 1
130       : a.toLowerCase() < b.toLowerCase() ? -1
131         : a > b ? 1
132           : -1
133 }
134
135 Reader.prototype._stat = function (currentStat) {
136   var self = this
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)
142
143   function statCb (er, props_) {
144     // console.error("Reader._stat, statCb", self._path, props_, props_.nlink)
145     if (er) return self.error(er)
146
147     Object.keys(props_).forEach(function (k) {
148       props[k] = props_[k]
149     })
150
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')
154     }
155     self.size = props.size
156
157     var type = getType(props)
158     var handleHardlinks = props.hardlinks !== false
159
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
166       } else {
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
175       }
176     }
177
178     if (self.type && self.type !== type) {
179       self.error('Unexpected type: ' + type)
180     }
181
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.
184     if (self.filter) {
185       var who = self._proxy || self
186       // special handling for ProxyReaders
187       if (!self.filter.call(who, who, props)) {
188         if (!self._disowned) {
189           self.abort()
190           self.emit('end')
191           self.emit('close')
192         }
193         return
194       }
195     }
196
197     // last chance to abort or disown before the flow starts!
198     var events = ['_stat', 'stat', 'ready']
199     var e = 0
200     ;(function go () {
201       if (self._aborted) {
202         self.emit('end')
203         self.emit('close')
204         return
205       }
206
207       if (self._paused && self.type !== 'Directory') {
208         self.once('resume', go)
209         return
210       }
211
212       var ev = events[e++]
213       if (!ev) {
214         return self._read()
215       }
216       self.emit(ev, props)
217       go()
218     })()
219   }
220 }
221
222 Reader.prototype.pipe = function (dest) {
223   var self = this
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)
228       if (ret === false) {
229         self.pause()
230       }
231     })
232   }
233
234   // console.error("R Pipe apply Stream Pipe")
235   return Stream.prototype.pipe.apply(this, arguments)
236 }
237
238 Reader.prototype.pause = function (who) {
239   this._paused = true
240   who = who || this
241   this.emit('pause', who)
242   if (this._stream) this._stream.pause(who)
243 }
244
245 Reader.prototype.resume = function (who) {
246   this._paused = false
247   who = who || this
248   this.emit('resume', who)
249   if (this._stream) this._stream.resume(who)
250   this._read()
251 }
252
253 Reader.prototype._read = function () {
254   this.error('Cannot read unknown type: ' + this.type)
255 }