You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
4.9 KiB
JavaScript
171 lines
4.9 KiB
JavaScript
|
|
var tar = tar || {};
|
|
|
|
tar.Archive = class {
|
|
|
|
static open(data) {
|
|
const stream = data instanceof Uint8Array ? new tar.BinaryReader(data) : data;
|
|
if (stream.length > 512) {
|
|
const buffer = stream.peek(512);
|
|
const sum = buffer.map((value, index) => (index >= 148 && index < 156) ? 32 : value).reduce((a, b) => a + b, 0);
|
|
let checksum = '';
|
|
for (let i = 148; i < 156 && buffer[i] !== 0x00; i++) {
|
|
checksum += String.fromCharCode(buffer[i]);
|
|
}
|
|
checksum = parseInt(checksum, 8);
|
|
if (!isNaN(checksum) && sum === checksum) {
|
|
return new tar.Archive(stream);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
constructor(stream) {
|
|
this._entries = new Map();
|
|
const position = stream.position;
|
|
while (stream.position < stream.length) {
|
|
const entry = new tar.Entry(stream);
|
|
if (entry.type === '0' || entry.type === '1' || entry.type === '2') {
|
|
this._entries.set(entry.name, entry.stream);
|
|
}
|
|
if (stream.position + 512 > stream.length ||
|
|
stream.peek(512).every((value) => value === 0x00)) {
|
|
break;
|
|
}
|
|
}
|
|
stream.seek(position);
|
|
}
|
|
|
|
get entries() {
|
|
return this._entries;
|
|
}
|
|
};
|
|
|
|
tar.Entry = class {
|
|
|
|
constructor(stream) {
|
|
const buffer = stream.read(512);
|
|
const reader = new tar.BinaryReader(buffer);
|
|
const sum = buffer.map((value, index) => (index >= 148 && index < 156) ? 32 : value).reduce((a, b) => a + b, 0);
|
|
let checksum = '';
|
|
for (let i = 148; i < 156 && buffer[i] !== 0x00; i++) {
|
|
checksum += String.fromCharCode(buffer[i]);
|
|
}
|
|
checksum = parseInt(checksum, 8);
|
|
if (isNaN(checksum) || sum !== checksum) {
|
|
throw new tar.Error('Invalid tar archive.');
|
|
}
|
|
this._name = reader.string(100);
|
|
reader.string(8); // file mode
|
|
reader.string(8); // owner
|
|
reader.string(8); // group
|
|
const size = parseInt(reader.string(12).trim(), 8);
|
|
reader.string(12); // timestamp
|
|
reader.string(8); // checksum
|
|
this._type = reader.string(1);
|
|
reader.string(100); // name of linked file
|
|
if (reader.string(6) === 'ustar') {
|
|
reader.string(2); // ustar version
|
|
reader.string(32); // owner user name
|
|
reader.string(32); // owner group name
|
|
reader.string(8); // device major number
|
|
reader.string(8); // device number number
|
|
this._name = reader.string(155) + this._name;
|
|
}
|
|
this._stream = stream.stream(size);
|
|
stream.read(((size % 512) != 0) ? (512 - (size % 512)) : 0);
|
|
}
|
|
|
|
get type() {
|
|
return this._type;
|
|
}
|
|
|
|
get name() {
|
|
return this._name;
|
|
}
|
|
|
|
get stream() {
|
|
return this._stream;
|
|
}
|
|
};
|
|
|
|
tar.BinaryReader = class {
|
|
|
|
constructor(buffer) {
|
|
this._buffer = buffer;
|
|
this._length = buffer.length;
|
|
this._position = 0;
|
|
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
}
|
|
|
|
get position() {
|
|
return this._position;
|
|
}
|
|
|
|
get length() {
|
|
return this._length;
|
|
}
|
|
|
|
create(buffer) {
|
|
return new tar.BinaryReader(buffer);
|
|
}
|
|
|
|
stream(length) {
|
|
return this.create(this.read(length));
|
|
}
|
|
|
|
seek(position) {
|
|
this._position = position >= 0 ? position : this._length + position;
|
|
}
|
|
|
|
skip(offset) {
|
|
this._position += offset;
|
|
}
|
|
|
|
peek(length) {
|
|
if (this._position === 0 && length === undefined) {
|
|
return this._buffer;
|
|
}
|
|
const position = this._position;
|
|
this.skip(length !== undefined ? length : this._length - this._position);
|
|
const end = this._position;
|
|
this.seek(position);
|
|
return this._buffer.subarray(position, end);
|
|
}
|
|
|
|
read(length) {
|
|
if (this._position === 0 && length === undefined) {
|
|
this._position = this._length;
|
|
return this._buffer;
|
|
}
|
|
const position = this._position;
|
|
this.skip(length !== undefined ? length : this._length - this._position);
|
|
return this._buffer.subarray(position, this._position);
|
|
}
|
|
|
|
string(length) {
|
|
const buffer = this.read(length);
|
|
let position = 0;
|
|
let content = '';
|
|
for (let i = 0; i < length; i++) {
|
|
const c = buffer[position++];
|
|
if (c === 0) {
|
|
break;
|
|
}
|
|
content += String.fromCharCode(c);
|
|
}
|
|
return content;
|
|
}
|
|
};
|
|
|
|
tar.Error = class extends Error {
|
|
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'tar Error';
|
|
}
|
|
};
|
|
|
|
if (typeof module !== 'undefined' && typeof module.exports === 'object') {
|
|
module.exports.Archive = tar.Archive;
|
|
} |