update style
This commit is contained in:
parent
0b57f1c1c6
commit
bc15a53e8b
5 changed files with 511 additions and 509 deletions
100
README.md
100
README.md
|
@ -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
|
||||||
|
@ -72,20 +74,20 @@ Most of these values can be found by stat'ing a file.
|
||||||
|
|
||||||
``` js
|
``` js
|
||||||
{
|
{
|
||||||
name: 'path/to/this/entry.txt',
|
name: 'path/to/this/entry.txt',
|
||||||
size: 1314, // entry size. defaults to 0
|
size: 1314, // entry size. defaults to 0
|
||||||
mode: 0644, // entry mode. defaults to to 0755 for dirs and 0644 otherwise
|
mode: 0644, // entry mode. defaults to to 0755 for dirs and 0644 otherwise
|
||||||
mtime: new Date(), // last modified date for entry. defaults to now.
|
mtime: new Date(), // last modified date for entry. defaults to now.
|
||||||
type: 'file', // type of entry. defaults to file. can be:
|
type: 'file', // type of entry. defaults to file. can be:
|
||||||
// file | link | symlink | directory | block-device
|
// file | link | symlink | directory | block-device
|
||||||
// character-device | fifo | contigious-file
|
// character-device | fifo | contigious-file
|
||||||
linkname: 'path', // linked file name
|
linkname: 'path', // linked file name
|
||||||
uid: 0, // uid of entry owner. defaults to 0
|
uid: 0, // uid of entry owner. defaults to 0
|
||||||
gid: 0, // gid of entry owner. defaults to 0
|
gid: 0, // gid of entry owner. defaults to 0
|
||||||
uname: 'maf', // uname of entry owner. defaults to null
|
uname: 'maf', // uname of entry owner. defaults to null
|
||||||
gname: 'staff', // gname of entry owner. defaults to null
|
gname: 'staff', // gname of entry owner. defaults to null
|
||||||
devmajor: 0, // device major version. defaults to 0
|
devmajor: 0, // device major version. defaults to 0
|
||||||
devminor: 0 // device minor version. defaults to 0
|
devminor: 0 // device minor version. defaults to 0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
298
extract.js
298
extract.js
|
@ -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
|
||||||
|
|
338
headers.js
338
headers.js
|
@ -1,221 +1,221 @@
|
||||||
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)
|
||||||
|
|
||||||
return result;
|
buf = buf.slice(len)
|
||||||
};
|
}
|
||||||
|
|
||||||
|
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 {
|
|
||||||
name: name,
|
|
||||||
mode: mode,
|
|
||||||
uid: uid,
|
|
||||||
gid: gid,
|
|
||||||
size: size,
|
|
||||||
mtime: new Date(1000 * mtime),
|
|
||||||
type: toType(typeflag),
|
|
||||||
linkname: linkname,
|
|
||||||
uname: uname,
|
|
||||||
gname: gname,
|
|
||||||
devmajor: devmajor,
|
|
||||||
devminor: devminor
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
mode: mode,
|
||||||
|
uid: uid,
|
||||||
|
gid: gid,
|
||||||
|
size: size,
|
||||||
|
mtime: new Date(1000 * mtime),
|
||||||
|
type: toType(typeflag),
|
||||||
|
linkname: linkname,
|
||||||
|
uname: uname,
|
||||||
|
gname: gname,
|
||||||
|
devmajor: devmajor,
|
||||||
|
devminor: devminor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
4
index.js
4
index.js
|
@ -1,2 +1,2 @@
|
||||||
exports.extract = require('./extract');
|
exports.extract = require('./extract')
|
||||||
exports.pack = require('./pack');
|
exports.pack = require('./pack')
|
280
pack.js
280
pack.js
|
@ -1,194 +1,194 @@
|
||||||
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',
|
||||||
mode: header.mode,
|
mode: header.mode,
|
||||||
uid: header.uid,
|
uid: header.uid,
|
||||||
gid: header.gid,
|
gid: header.gid,
|
||||||
size: paxHeader.length,
|
size: paxHeader.length,
|
||||||
mtime: header.mtime,
|
mtime: header.mtime,
|
||||||
type: 'pax-header',
|
type: 'pax-header',
|
||||||
linkname: header.linkname && 'PaxHeader',
|
linkname: header.linkname && 'PaxHeader',
|
||||||
uname: header.uname,
|
uname: header.uname,
|
||||||
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
|
||||||
|
|
Loading…
Reference in a new issue