inital commit

This commit is contained in:
Mathias Buus 2013-12-19 23:08:43 +01:00
commit a9b21b6ee0
2 changed files with 268 additions and 0 deletions

132
extract.js Normal file
View file

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

136
headers.js Normal file
View file

@ -0,0 +1,136 @@
var ZEROS = '0000000000000000000';
var ZERO_OFFSET = '0'.charCodeAt(0);
var USTAR = 'ustar00';
var toType = function(flag) {
switch (flag) {
case 1:
return 'link';
case 3:
return 'character';
case 4:
return 'block';
case 5:
return 'directory';
case 6:
return 'fifo';
}
return 'file';
};
var toTypeflag = function(flag) {
switch (flag) {
case 'link':
return 1;
case 'character':
return 3;
case 'block':
return 4;
case 'directory':
return 5;
case 'fifo':
return 6;
}
return 0;
};
var alloc = function(size) {
var buf = new Buffer(size);
buf.fill(0);
return buf;
};
var indexOf = function(block, num, offset) {
for (; offset < block.length; offset++) {
if (block[offset] === num) return offset;
}
return -1;
};
var cksum = function(block) {
var sum = 8 * 32;
for (var i = 0; i < 148; i++) sum += block[i];
for (var i = 156; i < 512; i++) sum += block[i];
return sum;
};
var oct = function(val, n) {
val = val.toString(8);
return ZEROS.slice(0, n-val.length)+val+' ';
};
var decodeOct = function(val, offset) {
return parseInt(val.slice(offset, indexOf(val, 32, offset)).toString(), 8);
};
var decodeStr = function(val, offset) {
return val.slice(offset, indexOf(val, 0, offset)).toString();
};
exports.encode = function(opts) {
var buf = alloc(512);
var name = opts.path;
var prefix = '';
if (opts.typeflag === 5 && name[name.length-1] !== '/') name += '/';
while (Buffer.byteLength(name) > 100) {
var i = name.indexOf('/');
prefix += prefix ? '/' + name.slice(0, i) : name.slice(0, i);
name = name.slice(i+1);
}
buf.write(name);
buf.write(oct(opts.mode, 6), 100);
buf.write(oct(opts.uid, 6), 108);
buf.write(oct(opts.gid, 6), 116);
buf.write(oct(opts.size, 11), 124);
buf.write(oct((opts.mtime.getTime() / 1000) | 0, 11), 136);
buf[156] = ZERO_OFFSET + toTypeflag(opts.type);
if (opts.linkname) buf.write(opts.linkname, 157);
buf.write(USTAR, 257);
if (opts.uname) buf.write(opts.uname, 265);
if (opts.gname) buf.write(opts.gname, 297);
buf.write(oct(0, 6), 329);
buf.write(oct(0, 6), 337);
if (prefix) buf.write(prefix, 345);
buf.write(oct(cksum(buf), 6), 148);
return buf;
};
exports.decode = function(buf) {
var name = decodeStr(buf, 0);
var mode = decodeOct(buf, 100);
var uid = decodeOct(buf, 108);
var gid = decodeOct(buf, 116);
var size = decodeOct(buf, 124);
var mtime = decodeOct(buf, 136);
var typeflag = buf[156] - ZERO_OFFSET;
var linkname = buf[157] === 0 ? null : decodeStr(buf, 157);
var uname = decodeStr(buf, 265);
var gname = decodeStr(buf, 297);
if (buf[345]) name = decodeStr(buf, 345)+'/'+name;
if (cksum(buf) !== decodeOct(buf, 148)) return null;
return {
path: name,
mode: mode,
uid: uid,
gid: gid,
size: size,
mtime: new Date(1000 * mtime),
type: toType(typeflag),
linkname: linkname,
uname: uname,
gname: gname
};
};