inital commit
This commit is contained in:
commit
a9b21b6ee0
2 changed files with 268 additions and 0 deletions
132
extract.js
Normal file
132
extract.js
Normal 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
136
headers.js
Normal 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
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in a new issue