Initial commit
[yaffs-website] / node_modules / tar / lib / pack.js
1 // pipe in an fstream, and it'll make a tarball.
2 // key-value pair argument is global extended header props.
3
4 module.exports = Pack
5
6 var EntryWriter = require("./entry-writer.js")
7   , Stream = require("stream").Stream
8   , path = require("path")
9   , inherits = require("inherits")
10   , GlobalHeaderWriter = require("./global-header-writer.js")
11   , collect = require("fstream").collect
12   , eof = new Buffer(512)
13
14 for (var i = 0; i < 512; i ++) eof[i] = 0
15
16 inherits(Pack, Stream)
17
18 function Pack (props) {
19   // console.error("-- p ctor")
20   var me = this
21   if (!(me instanceof Pack)) return new Pack(props)
22
23   if (props) me._noProprietary = props.noProprietary
24   else me._noProprietary = false
25
26   me._global = props
27
28   me.readable = true
29   me.writable = true
30   me._buffer = []
31   // console.error("-- -- set current to null in ctor")
32   me._currentEntry = null
33   me._processing = false
34
35   me._pipeRoot = null
36   me.on("pipe", function (src) {
37     if (src.root === me._pipeRoot) return
38     me._pipeRoot = src
39     src.on("end", function () {
40       me._pipeRoot = null
41     })
42     me.add(src)
43   })
44 }
45
46 Pack.prototype.addGlobal = function (props) {
47   // console.error("-- p addGlobal")
48   if (this._didGlobal) return
49   this._didGlobal = true
50
51   var me = this
52   GlobalHeaderWriter(props)
53     .on("data", function (c) {
54       me.emit("data", c)
55     })
56     .end()
57 }
58
59 Pack.prototype.add = function (stream) {
60   if (this._global && !this._didGlobal) this.addGlobal(this._global)
61
62   if (this._ended) return this.emit("error", new Error("add after end"))
63
64   collect(stream)
65   this._buffer.push(stream)
66   this._process()
67   this._needDrain = this._buffer.length > 0
68   return !this._needDrain
69 }
70
71 Pack.prototype.pause = function () {
72   this._paused = true
73   if (this._currentEntry) this._currentEntry.pause()
74   this.emit("pause")
75 }
76
77 Pack.prototype.resume = function () {
78   this._paused = false
79   if (this._currentEntry) this._currentEntry.resume()
80   this.emit("resume")
81   this._process()
82 }
83
84 Pack.prototype.end = function () {
85   this._ended = true
86   this._buffer.push(eof)
87   this._process()
88 }
89
90 Pack.prototype._process = function () {
91   var me = this
92   if (me._paused || me._processing) {
93     return
94   }
95
96   var entry = me._buffer.shift()
97
98   if (!entry) {
99     if (me._needDrain) {
100       me.emit("drain")
101     }
102     return
103   }
104
105   if (entry.ready === false) {
106     // console.error("-- entry is not ready", entry)
107     me._buffer.unshift(entry)
108     entry.on("ready", function () {
109       // console.error("-- -- ready!", entry)
110       me._process()
111     })
112     return
113   }
114
115   me._processing = true
116
117   if (entry === eof) {
118     // need 2 ending null blocks.
119     me.emit("data", eof)
120     me.emit("data", eof)
121     me.emit("end")
122     me.emit("close")
123     return
124   }
125
126   // Change the path to be relative to the root dir that was
127   // added to the tarball.
128   //
129   // XXX This should be more like how -C works, so you can
130   // explicitly set a root dir, and also explicitly set a pathname
131   // in the tarball to use.  That way we can skip a lot of extra
132   // work when resolving symlinks for bundled dependencies in npm.
133
134   var root = path.dirname((entry.root || entry).path);
135   if (me._global && me._global.fromBase && entry.root && entry.root.path) {
136     // user set 'fromBase: true' indicating tar root should be directory itself
137     root = entry.root.path;
138   }
139
140   var wprops = {}
141
142   Object.keys(entry.props || {}).forEach(function (k) {
143     wprops[k] = entry.props[k]
144   })
145
146   if (me._noProprietary) wprops.noProprietary = true
147
148   wprops.path = path.relative(root, entry.path || '')
149
150   // actually not a matter of opinion or taste.
151   if (process.platform === "win32") {
152     wprops.path = wprops.path.replace(/\\/g, "/")
153   }
154
155   if (!wprops.type)
156     wprops.type = 'Directory'
157
158   switch (wprops.type) {
159     // sockets not supported
160     case "Socket":
161       return
162
163     case "Directory":
164       wprops.path += "/"
165       wprops.size = 0
166       break
167
168     case "Link":
169       var lp = path.resolve(path.dirname(entry.path), entry.linkpath)
170       wprops.linkpath = path.relative(root, lp) || "."
171       wprops.size = 0
172       break
173
174     case "SymbolicLink":
175       var lp = path.resolve(path.dirname(entry.path), entry.linkpath)
176       wprops.linkpath = path.relative(path.dirname(entry.path), lp) || "."
177       wprops.size = 0
178       break
179   }
180
181   // console.error("-- new writer", wprops)
182   // if (!wprops.type) {
183   //   // console.error("-- no type?", entry.constructor.name, entry)
184   // }
185
186   // console.error("-- -- set current to new writer", wprops.path)
187   var writer = me._currentEntry = EntryWriter(wprops)
188
189   writer.parent = me
190
191   // writer.on("end", function () {
192   //   // console.error("-- -- writer end", writer.path)
193   // })
194
195   writer.on("data", function (c) {
196     me.emit("data", c)
197   })
198
199   writer.on("header", function () {
200     Buffer.prototype.toJSON = function () {
201       return this.toString().split(/\0/).join(".")
202     }
203     // console.error("-- -- writer header %j", writer.props)
204     if (writer.props.size === 0) nextEntry()
205   })
206   writer.on("close", nextEntry)
207
208   var ended = false
209   function nextEntry () {
210     if (ended) return
211     ended = true
212
213     // console.error("-- -- writer close", writer.path)
214     // console.error("-- -- set current to null", wprops.path)
215     me._currentEntry = null
216     me._processing = false
217     me._process()
218   }
219
220   writer.on("error", function (er) {
221     // console.error("-- -- writer error", writer.path)
222     me.emit("error", er)
223   })
224
225   // if it's the root, then there's no need to add its entries,
226   // or data, since they'll be added directly.
227   if (entry === me._pipeRoot) {
228     // console.error("-- is the root, don't auto-add")
229     writer.add = null
230   }
231
232   entry.pipe(writer)
233 }
234
235 Pack.prototype.destroy = function () {}
236 Pack.prototype.write = function () {}