Files: d2f2697f296dd39aed6a8b63c6d04a736a7db5b3 / node_modules / tar-stream / extract.js
5911 bytesRaw
1 | var util = require('util') |
2 | var bl = require('bl') |
3 | var headers = require('./headers') |
4 | |
5 | var Writable = require('readable-stream').Writable |
6 | var PassThrough = require('readable-stream').PassThrough |
7 | |
8 | var noop = function () {} |
9 | |
10 | var overflow = function (size) { |
11 | size &= 511 |
12 | return size && 512 - size |
13 | } |
14 | |
15 | var emptyStream = function (self, offset) { |
16 | var s = new Source(self, offset) |
17 | s.end() |
18 | return s |
19 | } |
20 | |
21 | var mixinPax = function (header, pax) { |
22 | if (pax.path) header.name = pax.path |
23 | if (pax.linkpath) header.linkname = pax.linkpath |
24 | if (pax.size) header.size = parseInt(pax.size, 10) |
25 | header.pax = pax |
26 | return header |
27 | } |
28 | |
29 | var Source = function (self, offset) { |
30 | this._parent = self |
31 | this.offset = offset |
32 | PassThrough.call(this) |
33 | } |
34 | |
35 | util.inherits(Source, PassThrough) |
36 | |
37 | Source.prototype.destroy = function (err) { |
38 | this._parent.destroy(err) |
39 | } |
40 | |
41 | var Extract = function (opts) { |
42 | if (!(this instanceof Extract)) return new Extract(opts) |
43 | Writable.call(this, opts) |
44 | |
45 | opts = opts || {} |
46 | |
47 | this._offset = 0 |
48 | this._buffer = bl() |
49 | this._missing = 0 |
50 | this._partial = false |
51 | this._onparse = noop |
52 | this._header = null |
53 | this._stream = null |
54 | this._overflow = null |
55 | this._cb = null |
56 | this._locked = false |
57 | this._destroyed = false |
58 | this._pax = null |
59 | this._paxGlobal = null |
60 | this._gnuLongPath = null |
61 | this._gnuLongLinkPath = null |
62 | |
63 | var self = this |
64 | var b = self._buffer |
65 | |
66 | var oncontinue = function () { |
67 | self._continue() |
68 | } |
69 | |
70 | var onunlock = function (err) { |
71 | self._locked = false |
72 | if (err) return self.destroy(err) |
73 | if (!self._stream) oncontinue() |
74 | } |
75 | |
76 | var onstreamend = function () { |
77 | self._stream = null |
78 | var drain = overflow(self._header.size) |
79 | if (drain) self._parse(drain, ondrain) |
80 | else self._parse(512, onheader) |
81 | if (!self._locked) oncontinue() |
82 | } |
83 | |
84 | var ondrain = function () { |
85 | self._buffer.consume(overflow(self._header.size)) |
86 | self._parse(512, onheader) |
87 | oncontinue() |
88 | } |
89 | |
90 | var onpaxglobalheader = function () { |
91 | var size = self._header.size |
92 | self._paxGlobal = headers.decodePax(b.slice(0, size)) |
93 | b.consume(size) |
94 | onstreamend() |
95 | } |
96 | |
97 | var onpaxheader = function () { |
98 | var size = self._header.size |
99 | self._pax = headers.decodePax(b.slice(0, size)) |
100 | if (self._paxGlobal) self._pax = Object.assign({}, self._paxGlobal, self._pax) |
101 | b.consume(size) |
102 | onstreamend() |
103 | } |
104 | |
105 | var ongnulongpath = function () { |
106 | var size = self._header.size |
107 | this._gnuLongPath = headers.decodeLongPath(b.slice(0, size), opts.filenameEncoding) |
108 | b.consume(size) |
109 | onstreamend() |
110 | } |
111 | |
112 | var ongnulonglinkpath = function () { |
113 | var size = self._header.size |
114 | this._gnuLongLinkPath = headers.decodeLongPath(b.slice(0, size), opts.filenameEncoding) |
115 | b.consume(size) |
116 | onstreamend() |
117 | } |
118 | |
119 | var onheader = function () { |
120 | var offset = self._offset |
121 | var header |
122 | try { |
123 | header = self._header = headers.decode(b.slice(0, 512), opts.filenameEncoding) |
124 | } catch (err) { |
125 | self.emit('error', err) |
126 | } |
127 | b.consume(512) |
128 | |
129 | if (!header) { |
130 | self._parse(512, onheader) |
131 | oncontinue() |
132 | return |
133 | } |
134 | if (header.type === 'gnu-long-path') { |
135 | self._parse(header.size, ongnulongpath) |
136 | oncontinue() |
137 | return |
138 | } |
139 | if (header.type === 'gnu-long-link-path') { |
140 | self._parse(header.size, ongnulonglinkpath) |
141 | oncontinue() |
142 | return |
143 | } |
144 | if (header.type === 'pax-global-header') { |
145 | self._parse(header.size, onpaxglobalheader) |
146 | oncontinue() |
147 | return |
148 | } |
149 | if (header.type === 'pax-header') { |
150 | self._parse(header.size, onpaxheader) |
151 | oncontinue() |
152 | return |
153 | } |
154 | |
155 | if (self._gnuLongPath) { |
156 | header.name = self._gnuLongPath |
157 | self._gnuLongPath = null |
158 | } |
159 | |
160 | if (self._gnuLongLinkPath) { |
161 | header.linkname = self._gnuLongLinkPath |
162 | self._gnuLongLinkPath = null |
163 | } |
164 | |
165 | if (self._pax) { |
166 | self._header = header = mixinPax(header, self._pax) |
167 | self._pax = null |
168 | } |
169 | |
170 | self._locked = true |
171 | |
172 | if (!header.size || header.type === 'directory') { |
173 | self._parse(512, onheader) |
174 | self.emit('entry', header, emptyStream(self, offset), onunlock) |
175 | return |
176 | } |
177 | |
178 | self._stream = new Source(self, offset) |
179 | |
180 | self.emit('entry', header, self._stream, onunlock) |
181 | self._parse(header.size, onstreamend) |
182 | oncontinue() |
183 | } |
184 | |
185 | this._onheader = onheader |
186 | this._parse(512, onheader) |
187 | } |
188 | |
189 | util.inherits(Extract, Writable) |
190 | |
191 | Extract.prototype.destroy = function (err) { |
192 | if (this._destroyed) return |
193 | this._destroyed = true |
194 | |
195 | if (err) this.emit('error', err) |
196 | this.emit('close') |
197 | if (this._stream) this._stream.emit('close') |
198 | } |
199 | |
200 | Extract.prototype._parse = function (size, onparse) { |
201 | if (this._destroyed) return |
202 | this._offset += size |
203 | this._missing = size |
204 | if (onparse === this._onheader) this._partial = false |
205 | this._onparse = onparse |
206 | } |
207 | |
208 | Extract.prototype._continue = function () { |
209 | if (this._destroyed) return |
210 | var cb = this._cb |
211 | this._cb = noop |
212 | if (this._overflow) this._write(this._overflow, undefined, cb) |
213 | else cb() |
214 | } |
215 | |
216 | Extract.prototype._write = function (data, enc, cb) { |
217 | if (this._destroyed) return |
218 | |
219 | var s = this._stream |
220 | var b = this._buffer |
221 | var missing = this._missing |
222 | if (data.length) this._partial = true |
223 | |
224 | // we do not reach end-of-chunk now. just forward it |
225 | |
226 | if (data.length < missing) { |
227 | this._missing -= data.length |
228 | this._overflow = null |
229 | if (s) return s.write(data, cb) |
230 | b.append(data) |
231 | return cb() |
232 | } |
233 | |
234 | // end-of-chunk. the parser should call cb. |
235 | |
236 | this._cb = cb |
237 | this._missing = 0 |
238 | |
239 | var overflow = null |
240 | if (data.length > missing) { |
241 | overflow = data.slice(missing) |
242 | data = data.slice(0, missing) |
243 | } |
244 | |
245 | if (s) s.end(data) |
246 | else b.append(data) |
247 | |
248 | this._overflow = overflow |
249 | this._onparse() |
250 | } |
251 | |
252 | Extract.prototype._final = function (cb) { |
253 | if (this._partial) return this.destroy(new Error('Unexpected end of data')) |
254 | cb() |
255 | } |
256 | |
257 | module.exports = Extract |
258 |
Built with git-ssb-web