| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- const tar = {};
- tar.Archive = class {
- static open(data) {
- const stream = data instanceof Uint8Array ? new tar.BinaryReader(data) : data;
- if (stream && 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);
- const checksum = parseInt(Array.from(buffer.slice(148, 156)).map((c) => String.fromCharCode(c)).join('').split('\0').shift(), 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 === '' || 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 = reader.size();
- 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
- const prefix = reader.string(155);
- this._name = prefix ? `${prefix}/${this._name}` : this._name;
- }
- this._stream = stream.stream(size);
- stream.read(((size % 512) === 0) ? 0 : (512 - (size % 512)));
- }
- 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;
- if (this._position > this._length || this._position < 0) {
- throw new tar.Error('Invalid tar archive. Unexpected end of file.');
- }
- }
- skip(offset) {
- this._position += offset;
- if (this._position > this._length || this._position < 0) {
- throw new tar.Error('Invalid tar archive. Unexpected end of file.');
- }
- }
- peek(length) {
- if (this._position === 0 && length === undefined) {
- return this._buffer;
- }
- const position = this._position;
- this.skip(length === undefined ? this._length - this._position : length);
- 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 ? this._length - this._position : length);
- 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;
- }
- size() {
- const buffer = this.read(12);
- if (buffer[0] & 0x80) {
- buffer[0] &= 0x7f;
- let value = 0n;
- for (const byte of buffer) {
- value = (value << 8n) | BigInt(byte);
- if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
- throw new tar.Error('Tar entry size exceeds safe integer.');
- }
- }
- return value.toNumber();
- }
- const octal = String.fromCharCode(...buffer);
- return parseInt(octal.trim() || '0', 8);
- }
- };
- tar.Error = class extends Error {
- constructor(message) {
- super(message);
- this.name = 'tar Error';
- }
- };
- export const Archive = tar.Archive;
|