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

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;
}