update style

This commit is contained in:
Mathias Buus 2014-09-23 07:30:58 -07:00
parent 0b57f1c1c6
commit bc15a53e8b
5 changed files with 511 additions and 509 deletions

View file

@ -2,7 +2,9 @@
tar-stream is a streaming tar parser and generator and nothing else. It is streams2 and operates purely using streams which means you can easily extract/parse tarballs without ever hitting the file system. tar-stream is a streaming tar parser and generator and nothing else. It is streams2 and operates purely using streams which means you can easily extract/parse tarballs without ever hitting the file system.
```
npm install tar-stream npm install tar-stream
```
[![build status](https://secure.travis-ci.org/mafintosh/tar-stream.png)](http://travis-ci.org/mafintosh/tar-stream) [![build status](https://secure.travis-ci.org/mafintosh/tar-stream.png)](http://travis-ci.org/mafintosh/tar-stream)
@ -22,22 +24,22 @@ If you want to pack/unpack directories on the file system check out [tar-fs](htt
To create a pack stream use `tar.pack()` and call `pack.entry(header, [callback])` to add tar entries. To create a pack stream use `tar.pack()` and call `pack.entry(header, [callback])` to add tar entries.
``` js ``` js
var tar = require('tar-stream'); var tar = require('tar-stream')
var pack = tar.pack(); // p is a streams2 stream var pack = tar.pack() // p is a streams2 stream
// add a file called my-test.txt with the content "Hello World!" // add a file called my-test.txt with the content "Hello World!"
pack.entry({ name: 'my-test.txt' }, 'Hello World!'); pack.entry({ name: 'my-test.txt' }, 'Hello World!')
// add a file called my-stream-test.txt from a stream // add a file called my-stream-test.txt from a stream
var entry = pack.entry({ name: 'my-stream-test.txt' }, function(err) { var entry = pack.entry({ name: 'my-stream-test.txt' }, function(err) {
// the stream was added // the stream was added
// no more entries // no more entries
pack.finalize(); pack.finalize()
}); })
myStream.pipe(entry); myStream.pipe(entry)
// pipe the pack stream somewhere // pipe the pack stream somewhere
pack.pipe(process.stdout); pack.pipe(process.stdout)
``` ```
## Extracting ## Extracting
@ -45,24 +47,24 @@ pack.pipe(process.stdout);
To extract a stream use `tar.extract()` and listen for `extract.on('entry', header, stream, callback)` To extract a stream use `tar.extract()` and listen for `extract.on('entry', header, stream, callback)`
``` js ``` js
var extract = tar.extract(); var extract = tar.extract()
extract.on('entry', function(header, stream, callback) { extract.on('entry', function(header, stream, callback) {
// header is the tar header // header is the tar header
// stream is the content body (might be an empty stream) // stream is the content body (might be an empty stream)
// call next when you are done with this entry // call next when you are done with this entry
stream.resume(); // just auto drain the stream stream.resume() // just auto drain the stream
stream.on('end', function() { stream.on('end', function() {
callback(); // ready for next entry callback() // ready for next entry
}); })
}); })
extract.on('finish', function() { extract.on('finish', function() {
// all entries read // all entries read
}); })
pack.pipe(extract); pack.pipe(extract)
``` ```
## Headers ## Headers
@ -94,27 +96,27 @@ Most of these values can be found by stat'ing a file.
Using tar-stream it is easy to rewrite paths / change modes etc in an existing tarball. Using tar-stream it is easy to rewrite paths / change modes etc in an existing tarball.
``` js ``` js
var extract = tar.extract(); var extract = tar.extract()
var pack = tar.pack(); var pack = tar.pack()
var path = require('path'); var path = require('path')
extract.on('entry', function(header, stream, callback) { extract.on('entry', function(header, stream, callback) {
// let's prefix all names with 'tmp' // let's prefix all names with 'tmp'
header.name = path.join('tmp', header.name); header.name = path.join('tmp', header.name)
// write the new entry to the pack stream // write the new entry to the pack stream
stream.pipe(pack.entry(header, callback)); stream.pipe(pack.entry(header, callback))
}); })
extract.on('finish', function() { extract.on('finish', function() {
// all entries done - lets finalize it // all entries done - lets finalize it
pack.finalize(); pack.finalize()
}); })
// pipe the old tarball to the extractor // pipe the old tarball to the extractor
oldTarball.pipe(extract); oldTarball.pipe(extract)
// pipe the new tarball the another stream // pipe the new tarball the another stream
pack.pipe(newTarball); pack.pipe(newTarball)
``` ```
## Performance ## Performance

View file

@ -1,194 +1,194 @@
var util = require('util'); var util = require('util')
var bl = require('bl'); var bl = require('bl')
var xtend = require('xtend'); var xtend = require('xtend')
var headers = require('./headers'); var headers = require('./headers')
var Writable = require('readable-stream').Writable; var Writable = require('readable-stream').Writable
var PassThrough = require('readable-stream').PassThrough; var PassThrough = require('readable-stream').PassThrough
var noop = function() {}; var noop = function() {}
var overflow = function(size) { var overflow = function(size) {
size &= 511; size &= 511
return size && 512 - size; return size && 512 - size
}; }
var emptyStream = function() { var emptyStream = function() {
var s = new PassThrough(); var s = new PassThrough()
s.end(); s.end()
return s; return s
}; }
var mixinPax = function(header, pax) { var mixinPax = function(header, pax) {
if (pax.path) header.name = pax.path; if (pax.path) header.name = pax.path
if (pax.linkpath) header.linkname = pax.linkpath; if (pax.linkpath) header.linkname = pax.linkpath
return header; return header
}; }
var Extract = function(opts) { var Extract = function(opts) {
if (!(this instanceof Extract)) return new Extract(opts); if (!(this instanceof Extract)) return new Extract(opts)
Writable.call(this, opts); Writable.call(this, opts)
this._buffer = bl(); this._buffer = bl()
this._missing = 0; this._missing = 0
this._onparse = noop; this._onparse = noop
this._header = null; this._header = null
this._stream = null; this._stream = null
this._overflow = null; this._overflow = null
this._cb = null; this._cb = null
this._locked = false; this._locked = false
this._destroyed = false; this._destroyed = false
this._pax = null; this._pax = null
this._paxGlobal = null; this._paxGlobal = null
var self = this; var self = this
var b = self._buffer; var b = self._buffer
var oncontinue = function() { var oncontinue = function() {
self._continue(); self._continue()
}; }
var onunlock = function(err) { var onunlock = function(err) {
self._locked = false; self._locked = false
if (err) return self.destroy(err); if (err) return self.destroy(err)
if (!self._stream) oncontinue(); if (!self._stream) oncontinue()
}; }
var onstreamend = function() { var onstreamend = function() {
self._stream = null; self._stream = null
var drain = overflow(self._header.size); var drain = overflow(self._header.size)
if (drain) self._parse(drain, ondrain); if (drain) self._parse(drain, ondrain)
else self._parse(512, onheader); else self._parse(512, onheader)
if (!self._locked) oncontinue(); if (!self._locked) oncontinue()
}; }
var ondrain = function() { var ondrain = function() {
self._buffer.consume(overflow(self._header.size)); self._buffer.consume(overflow(self._header.size))
self._parse(512, onheader); self._parse(512, onheader)
oncontinue(); oncontinue()
}; }
var onpaxglobalheader = function() { var onpaxglobalheader = function() {
var size = self._header.size; var size = self._header.size
self._paxGlobal = headers.decodePax(b.slice(0, size)); self._paxGlobal = headers.decodePax(b.slice(0, size))
b.consume(size); b.consume(size)
onstreamend(); onstreamend()
} }
var onpaxheader = function() { var onpaxheader = function() {
var size = self._header.size; var size = self._header.size
self._pax = headers.decodePax(b.slice(0, size)); self._pax = headers.decodePax(b.slice(0, size))
if (self._paxGlobal) self._pax = xtend(self._paxGlobal, self._pax); if (self._paxGlobal) self._pax = xtend(self._paxGlobal, self._pax)
b.consume(size); b.consume(size)
onstreamend(); onstreamend()
}; }
var onheader = function() { var onheader = function() {
var header var header
try { try {
header = self._header = headers.decode(b.slice(0, 512)); header = self._header = headers.decode(b.slice(0, 512))
} catch (err) { } catch (err) {
self.emit('error', err); self.emit('error', err)
} }
b.consume(512); b.consume(512)
if (!header) { if (!header) {
self._parse(512, onheader); self._parse(512, onheader)
oncontinue(); oncontinue()
return; return
} }
if (header.type === 'pax-global-header') { if (header.type === 'pax-global-header') {
self._parse(header.size, onpaxglobalheader); self._parse(header.size, onpaxglobalheader)
oncontinue(); oncontinue()
return; return
} }
if (header.type === 'pax-header') { if (header.type === 'pax-header') {
self._parse(header.size, onpaxheader); self._parse(header.size, onpaxheader)
oncontinue(); oncontinue()
return; return
} }
if (self._pax) { if (self._pax) {
self._header = header = mixinPax(header, self._pax); self._header = header = mixinPax(header, self._pax)
self._pax = null; self._pax = null
} }
self._locked = true; self._locked = true
if (!header.size) { if (!header.size) {
self._parse(512, onheader); self._parse(512, onheader)
self.emit('entry', header, emptyStream(), onunlock); self.emit('entry', header, emptyStream(), onunlock)
return; return
} }
self._stream = new PassThrough(); self._stream = new PassThrough()
self.emit('entry', header, self._stream, onunlock); self.emit('entry', header, self._stream, onunlock)
self._parse(header.size, onstreamend); self._parse(header.size, onstreamend)
oncontinue(); oncontinue()
}; }
this._parse(512, onheader); this._parse(512, onheader)
}; }
util.inherits(Extract, Writable); util.inherits(Extract, Writable)
Extract.prototype.destroy = function(err) { Extract.prototype.destroy = function(err) {
if (this._destroyed) return; if (this._destroyed) return
this._destroyed = true; this._destroyed = true
if (err) this.emit('error', err); if (err) this.emit('error', err)
this.emit('close'); this.emit('close')
if (this._stream) this._stream.emit('close'); if (this._stream) this._stream.emit('close')
}; }
Extract.prototype._parse = function(size, onparse) { Extract.prototype._parse = function(size, onparse) {
if (this._destroyed) return; if (this._destroyed) return
this._missing = size; this._missing = size
this._onparse = onparse; this._onparse = onparse
}; }
Extract.prototype._continue = function(err) { Extract.prototype._continue = function(err) {
if (this._destroyed) return; if (this._destroyed) return
var cb = this._cb; var cb = this._cb
this._cb = noop; this._cb = noop
if (this._overflow) this._write(this._overflow, undefined, cb); if (this._overflow) this._write(this._overflow, undefined, cb)
else cb(); else cb()
}; }
Extract.prototype._write = function(data, enc, cb) { Extract.prototype._write = function(data, enc, cb) {
if (this._destroyed) return; if (this._destroyed) return
var s = this._stream; var s = this._stream
var b = this._buffer; var b = this._buffer
var missing = this._missing; var missing = this._missing
// we do not reach end-of-chunk now. just forward it // we do not reach end-of-chunk now. just forward it
if (data.length < missing) { if (data.length < missing) {
this._missing -= data.length; this._missing -= data.length
this._overflow = null; this._overflow = null
if (s) return s.write(data, cb); if (s) return s.write(data, cb)
b.append(data); b.append(data)
return cb(); return cb()
} }
// end-of-chunk. the parser should call cb. // end-of-chunk. the parser should call cb.
this._cb = cb; this._cb = cb
this._missing = 0; this._missing = 0
var overflow = null; var overflow = null
if (data.length > missing) { if (data.length > missing) {
overflow = data.slice(missing); overflow = data.slice(missing)
data = data.slice(0, missing); data = data.slice(0, missing)
} }
if (s) s.end(data); if (s) s.end(data)
else b.append(data); else b.append(data)
this._overflow = overflow; this._overflow = overflow
this._onparse(); this._onparse()
}; }
module.exports = Extract; module.exports = Extract

View file

@ -1,207 +1,208 @@
var ZEROS = '0000000000000000000'; var ZEROS = '0000000000000000000'
var ZERO_OFFSET = '0'.charCodeAt(0); var ZERO_OFFSET = '0'.charCodeAt(0)
var USTAR = 'ustar\x0000'; var USTAR = 'ustar\x0000'
var clamp = function(index, len, defaultValue) { var clamp = function(index, len, defaultValue) {
if (typeof index !== 'number') return defaultValue; if (typeof index !== 'number') return defaultValue
index = ~~index; // Coerce to integer. index = ~~index // Coerce to integer.
if (index >= len) return len; if (index >= len) return len
if (index >= 0) return index; if (index >= 0) return index
index += len; index += len
if (index >= 0) return index; if (index >= 0) return index
return 0; return 0
}; }
var toType = function(flag) { var toType = function(flag) {
switch (flag) { switch (flag) {
case 0: case 0:
return 'file'; return 'file'
case 1: case 1:
return 'link'; return 'link'
case 2: case 2:
return 'symlink'; return 'symlink'
case 3: case 3:
return 'character-device'; return 'character-device'
case 4: case 4:
return 'block-device'; return 'block-device'
case 5: case 5:
return 'directory'; return 'directory'
case 6: case 6:
return 'fifo'; return 'fifo'
case 7: case 7:
return 'contiguous-file'; return 'contiguous-file'
case 72: case 72:
return 'pax-header'; return 'pax-header'
case 55: case 55:
return 'pax-global-header' return 'pax-global-header'
} }
return null; return null
}; }
var toTypeflag = function(flag) { var toTypeflag = function(flag) {
switch (flag) { switch (flag) {
case 'file': case 'file':
return 0; return 0
case 'link': case 'link':
return 1; return 1
case 'symlink': case 'symlink':
return 2; return 2
case 'character-device': case 'character-device':
return 3; return 3
case 'block-device': case 'block-device':
return 4; return 4
case 'directory': case 'directory':
return 5; return 5
case 'fifo': case 'fifo':
return 6; return 6
case 'contiguous-file': case 'contiguous-file':
return 7; return 7
case 'pax-header': case 'pax-header':
return 72; return 72
} }
return 0; return 0
}; }
var alloc = function(size) { var alloc = function(size) {
var buf = new Buffer(size); var buf = new Buffer(size)
buf.fill(0); buf.fill(0)
return buf; return buf
}; }
var indexOf = function(block, num, offset, end) { var indexOf = function(block, num, offset, end) {
for (; offset < end; offset++) { for (; offset < end; offset++) {
if (block[offset] === num) return offset; if (block[offset] === num) return offset
}
return end
} }
return end;
};
var cksum = function(block) { var cksum = function(block) {
var sum = 8 * 32; var sum = 8 * 32
for (var i = 0; i < 148; i++) sum += block[i]; for (var i = 0; i < 148; i++) sum += block[i]
for (var i = 156; i < 512; i++) sum += block[i]; for (var i = 156; i < 512; i++) sum += block[i]
return sum; return sum
}; }
var encodeOct = function(val, n) { var encodeOct = function(val, n) {
val = val.toString(8); val = val.toString(8)
return ZEROS.slice(0, n-val.length)+val+' '; return ZEROS.slice(0, n-val.length)+val+' '
}; }
var decodeOct = function(val, offset) { var decodeOct = function(val, offset) {
// Older versions of tar can prefix with spaces // Older versions of tar can prefix with spaces
while (val[offset] === 32) offset += 1; while (offset < val.length && val[offset] === 32) offset++
return parseInt(val.slice(offset, clamp(indexOf(val, 32, offset, val.length), val.length, val.length)).toString(), 8); return parseInt(val.slice(offset, clamp(indexOf(val, 32, offset, val.length), val.length, val.length)).toString(), 8)
}; }
var decodeStr = function(val, offset, length) { var decodeStr = function(val, offset, length) {
return val.slice(offset, indexOf(val, 0, offset, offset+length)).toString(); return val.slice(offset, indexOf(val, 0, offset, offset+length)).toString();
}; }
var addLength = function(str) { var addLength = function(str) {
var len = Buffer.byteLength(str); var len = Buffer.byteLength(str)
var digits = Math.floor(Math.log(len) / Math.log(10)) + 1; var digits = Math.floor(Math.log(len) / Math.log(10)) + 1
if (len + digits > Math.pow(10, digits)) digits++; if (len + digits > Math.pow(10, digits)) digits++
return (len+digits)+str; return (len+digits)+str
}; }
exports.encodePax = function(opts) { // TODO: encode more stuff in pax exports.encodePax = function(opts) { // TODO: encode more stuff in pax
var result = ''; var result = ''
if (opts.name) result += addLength(' path='+opts.name+'\n'); if (opts.name) result += addLength(' path='+opts.name+'\n')
if (opts.linkname) result += addLength(' linkpath='+opts.linkname+'\n'); if (opts.linkname) result += addLength(' linkpath='+opts.linkname+'\n')
return new Buffer(result); return new Buffer(result)
}; }
exports.decodePax = function(buf) { exports.decodePax = function(buf) {
var result = {}; var result = {}
while (buf.length) { while (buf.length) {
var i = 0; var i = 0
for (; i < buf.length && buf[i] !== 32; i++); while (i < buf.length && buf[i] !== 32) i++
var len = parseInt(buf.slice(0, i).toString());
if (!len) return result;
var b = buf.slice(i+1, len-1).toString(); var len = parseInt(buf.slice(0, i).toString())
var keyIndex = b.indexOf('='); if (!len) return result
if (keyIndex === -1) return result;
result[b.slice(0, keyIndex)] = b.slice(keyIndex+1);
buf = buf.slice(len); var b = buf.slice(i+1, len-1).toString()
var keyIndex = b.indexOf('=')
if (keyIndex === -1) return result
result[b.slice(0, keyIndex)] = b.slice(keyIndex+1)
buf = buf.slice(len)
} }
return result; return result
}; }
exports.encode = function(opts) { exports.encode = function(opts) {
var buf = alloc(512); var buf = alloc(512)
var name = opts.name; var name = opts.name
var prefix = ''; var prefix = ''
if (opts.typeflag === 5 && name[name.length-1] !== '/') name += '/'; if (opts.typeflag === 5 && name[name.length-1] !== '/') name += '/'
if (Buffer.byteLength(name) !== name.length) return null; // utf-8 if (Buffer.byteLength(name) !== name.length) return null // utf-8
while (Buffer.byteLength(name) > 100) { while (Buffer.byteLength(name) > 100) {
var i = name.indexOf('/'); var i = name.indexOf('/')
if (i === -1) return null; if (i === -1) return null
prefix += prefix ? '/' + name.slice(0, i) : name.slice(0, i); prefix += prefix ? '/' + name.slice(0, i) : name.slice(0, i)
name = name.slice(i+1); name = name.slice(i+1)
} }
if (Buffer.byteLength(name) > 100 || Buffer.byteLength(prefix) > 155) return null; if (Buffer.byteLength(name) > 100 || Buffer.byteLength(prefix) > 155) return null
if (opts.linkname && Buffer.byteLength(opts.linkname) > 100) return null; if (opts.linkname && Buffer.byteLength(opts.linkname) > 100) return null
buf.write(name); buf.write(name)
buf.write(encodeOct(opts.mode & 07777, 6), 100); buf.write(encodeOct(opts.mode & 07777, 6), 100)
buf.write(encodeOct(opts.uid, 6), 108); buf.write(encodeOct(opts.uid, 6), 108)
buf.write(encodeOct(opts.gid, 6), 116); buf.write(encodeOct(opts.gid, 6), 116)
buf.write(encodeOct(opts.size, 11), 124); buf.write(encodeOct(opts.size, 11), 124)
buf.write(encodeOct((opts.mtime.getTime() / 1000) | 0, 11), 136); buf.write(encodeOct((opts.mtime.getTime() / 1000) | 0, 11), 136)
buf[156] = ZERO_OFFSET + toTypeflag(opts.type); buf[156] = ZERO_OFFSET + toTypeflag(opts.type)
if (opts.linkname) buf.write(opts.linkname, 157); if (opts.linkname) buf.write(opts.linkname, 157)
buf.write(USTAR, 257); buf.write(USTAR, 257)
if (opts.uname) buf.write(opts.uname, 265); if (opts.uname) buf.write(opts.uname, 265)
if (opts.gname) buf.write(opts.gname, 297); if (opts.gname) buf.write(opts.gname, 297)
buf.write(encodeOct(opts.devmajor || 0, 6), 329); buf.write(encodeOct(opts.devmajor || 0, 6), 329)
buf.write(encodeOct(opts.devminor || 0, 6), 337); buf.write(encodeOct(opts.devminor || 0, 6), 337)
if (prefix) buf.write(prefix, 345); if (prefix) buf.write(prefix, 345)
buf.write(encodeOct(cksum(buf), 6), 148); buf.write(encodeOct(cksum(buf), 6), 148)
return buf; return buf
}; }
exports.decode = function(buf) { exports.decode = function(buf) {
var typeflag = buf[156] === 0 ? 0 : buf[156] - ZERO_OFFSET; var typeflag = buf[156] === 0 ? 0 : buf[156] - ZERO_OFFSET
var type = toType(typeflag); var type = toType(typeflag)
var name = decodeStr(buf, 0, 100); var name = decodeStr(buf, 0, 100)
var mode = decodeOct(buf, 100); var mode = decodeOct(buf, 100)
var uid = decodeOct(buf, 108); var uid = decodeOct(buf, 108)
var gid = decodeOct(buf, 116); var gid = decodeOct(buf, 116)
var size = decodeOct(buf, 124); var size = decodeOct(buf, 124)
var mtime = decodeOct(buf, 136); var mtime = decodeOct(buf, 136)
var linkname = buf[157] === 0 ? null : decodeStr(buf, 157, 100); var linkname = buf[157] === 0 ? null : decodeStr(buf, 157, 100)
var uname = decodeStr(buf, 265, 32); var uname = decodeStr(buf, 265, 32)
var gname = decodeStr(buf, 297, 32); var gname = decodeStr(buf, 297, 32)
var devmajor = decodeOct(buf, 329); var devmajor = decodeOct(buf, 329)
var devminor = decodeOct(buf, 337); var devminor = decodeOct(buf, 337)
if (buf[345]) name = decodeStr(buf, 345, 155)+'/'+name; if (buf[345]) name = decodeStr(buf, 345, 155)+'/'+name
var c = cksum(buf) var c = cksum(buf)
//checksum is still initial value if header was null. //checksum is still initial value if header was null.
if (c === 8*32) return null; if (c === 8*32) return null
//valid checksum //valid checksum
if (c !== decodeOct(buf, 148)) throw new Error('invalid header'); if (c !== decodeOct(buf, 148)) throw new Error('invalid header')
return { return {
name: name, name: name,
@ -216,6 +217,5 @@ exports.decode = function(buf) {
gname: gname, gname: gname,
devmajor: devmajor, devmajor: devmajor,
devminor: devminor devminor: devminor
}; }
}; }

View file

@ -1,2 +1,2 @@
exports.extract = require('./extract'); exports.extract = require('./extract')
exports.pack = require('./pack'); exports.pack = require('./pack')

216
pack.js
View file

@ -1,165 +1,165 @@
var util = require('util'); var util = require('util')
var eos = require('end-of-stream'); var eos = require('end-of-stream')
var headers = require('./headers'); var headers = require('./headers')
var Readable = require('readable-stream').Readable; var Readable = require('readable-stream').Readable
var Writable = require('readable-stream').Writable; var Writable = require('readable-stream').Writable
var PassThrough = require('readable-stream').PassThrough; var PassThrough = require('readable-stream').PassThrough
var END_OF_TAR = new Buffer(1024); var END_OF_TAR = new Buffer(1024)
END_OF_TAR.fill(0); END_OF_TAR.fill(0)
var noop = function() {}; var noop = function() {}
var overflow = function(self, size) { var overflow = function(self, size) {
size &= 511; size &= 511
if (size) self.push(END_OF_TAR.slice(0, 512 - size)); if (size) self.push(END_OF_TAR.slice(0, 512 - size))
}; }
var Sink = function(to) { var Sink = function(to) {
Writable.call(this); Writable.call(this)
this.written = 0; this.written = 0
this._to = to; this._to = to
this._destroyed = false; this._destroyed = false
}; }
util.inherits(Sink, Writable); util.inherits(Sink, Writable)
Sink.prototype._write = function(data, enc, cb) { Sink.prototype._write = function(data, enc, cb) {
this.written += data.length; this.written += data.length
if (this._to.push(data)) return cb(); if (this._to.push(data)) return cb()
this._to._drain = cb; this._to._drain = cb
}; }
Sink.prototype.destroy = function() { Sink.prototype.destroy = function() {
if (this._destroyed) return; if (this._destroyed) return
this._destroyed = true; this._destroyed = true
this.emit('close'); this.emit('close')
}; }
var Void = function() { var Void = function() {
Writable.call(this) Writable.call(this)
this._destroyed = false; this._destroyed = false
}; }
util.inherits(Void, Writable); util.inherits(Void, Writable)
Void.prototype._write = function(data, enc, cb) { Void.prototype._write = function(data, enc, cb) {
cb(new Error('No body allowed for this entry')) cb(new Error('No body allowed for this entry'))
}; }
Void.prototype.destroy = function() { Void.prototype.destroy = function() {
if (this._destroyed) return; if (this._destroyed) return
this._destroyed = true; this._destroyed = true
this.emit('close') this.emit('close')
} }
var Pack = function(opts) { var Pack = function(opts) {
if (!(this instanceof Pack)) return new Pack(opts); if (!(this instanceof Pack)) return new Pack(opts)
Readable.call(this, opts); Readable.call(this, opts)
this._drain = noop; this._drain = noop
this._finalized = false; this._finalized = false
this._finalizing = false; this._finalizing = false
this._destroyed = false; this._destroyed = false
this._stream = null; this._stream = null
}; }
util.inherits(Pack, Readable); util.inherits(Pack, Readable)
Pack.prototype.entry = function(header, buffer, callback) { Pack.prototype.entry = function(header, buffer, callback) {
if (this._stream) throw new Error('already piping an entry'); if (this._stream) throw new Error('already piping an entry')
if (this._finalized || this._destroyed) return; if (this._finalized || this._destroyed) return
if (typeof buffer === 'function') { if (typeof buffer === 'function') {
callback = buffer; callback = buffer
buffer = null; buffer = null
} }
if (!callback) callback = noop; if (!callback) callback = noop
var self = this; var self = this
if (!header.size) header.size = 0; if (!header.size) header.size = 0
if (!header.type) header.type = 'file'; if (!header.type) header.type = 'file'
if (!header.mode) header.mode = header.type === 'directory' ? 0755 : 0644; if (!header.mode) header.mode = header.type === 'directory' ? 0755 : 0644
if (!header.uid) header.uid = 0; if (!header.uid) header.uid = 0
if (!header.gid) header.gid = 0; if (!header.gid) header.gid = 0
if (!header.mtime) header.mtime = new Date(); if (!header.mtime) header.mtime = new Date()
if (typeof buffer === 'string') buffer = new Buffer(buffer); if (typeof buffer === 'string') buffer = new Buffer(buffer)
if (Buffer.isBuffer(buffer)) { if (Buffer.isBuffer(buffer)) {
header.size = buffer.length; header.size = buffer.length
this._encode(header); this._encode(header)
this.push(buffer); this.push(buffer)
overflow(self, header.size); overflow(self, header.size)
process.nextTick(callback); process.nextTick(callback)
return new Void(); return new Void()
} }
if (header.type !== 'file' && header.type !== 'contigious-file') { if (header.type !== 'file' && header.type !== 'contigious-file') {
this._encode(header); this._encode(header)
process.nextTick(callback); process.nextTick(callback)
return new Void(); return new Void()
} }
var sink = new Sink(this); var sink = new Sink(this)
this._encode(header); this._encode(header)
this._stream = sink; this._stream = sink
eos(sink, function(err) { eos(sink, function(err) {
self._stream = null; self._stream = null
if (err) { // stream was closed if (err) { // stream was closed
self.destroy(); self.destroy()
return callback(err); return callback(err)
} }
if (sink.written !== header.size) { // corrupting tar if (sink.written !== header.size) { // corrupting tar
self.destroy(); self.destroy()
return callback(new Error('size mismatch')); return callback(new Error('size mismatch'))
} }
overflow(self, header.size); overflow(self, header.size)
if (self._finalizing) self.finalize(); if (self._finalizing) self.finalize()
callback(); callback()
}); })
return sink; return sink
}; }
Pack.prototype.finalize = function() { Pack.prototype.finalize = function() {
if (this._stream) { if (this._stream) {
this._finalizing = true; this._finalizing = true
return; return
} }
if (this._finalized) return; if (this._finalized) return
this._finalized = true; this._finalized = true
this.push(END_OF_TAR); this.push(END_OF_TAR)
this.push(null); this.push(null)
}; }
Pack.prototype.destroy = function(err) { Pack.prototype.destroy = function(err) {
if (this._destroyed) return; if (this._destroyed) return
this._destroyed = true; this._destroyed = true
if (err) this.emit('error', err); if (err) this.emit('error', err)
this.emit('close'); this.emit('close')
if (this._stream && this._stream.destroy) this._stream.destroy(); if (this._stream && this._stream.destroy) this._stream.destroy()
}; }
Pack.prototype._encode = function(header) { Pack.prototype._encode = function(header) {
var buf = headers.encode(header); var buf = headers.encode(header)
if (buf) this.push(buf); if (buf) this.push(buf)
else this._encodePax(header); else this._encodePax(header)
}; }
Pack.prototype._encodePax = function(header) { Pack.prototype._encodePax = function(header) {
var paxHeader = headers.encodePax({ var paxHeader = headers.encodePax({
name: header.name, name: header.name,
linkname: header.linkname linkname: header.linkname
}); })
var newHeader = { var newHeader = {
name: 'PaxHeader', name: 'PaxHeader',
@ -174,21 +174,21 @@ Pack.prototype._encodePax = function(header) {
gname: header.gname, gname: header.gname,
devmajor: header.devmajor, devmajor: header.devmajor,
devminor: header.devminor devminor: header.devminor
}; }
this.push(headers.encode(newHeader)); this.push(headers.encode(newHeader))
this.push(paxHeader); this.push(paxHeader)
overflow(this, paxHeader.length); overflow(this, paxHeader.length)
newHeader.size = header.size; newHeader.size = header.size
newHeader.type = header.type; newHeader.type = header.type
this.push(headers.encode(newHeader)); this.push(headers.encode(newHeader))
}; }
Pack.prototype._read = function(n) { Pack.prototype._read = function(n) {
var drain = this._drain; var drain = this._drain
this._drain = noop; this._drain = noop
drain(); drain()
}; }
module.exports = Pack; module.exports = Pack