tar.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. var tar = tar || {};
  2. tar.Archive = class {
  3. static open(data) {
  4. const stream = data instanceof Uint8Array ? new tar.BinaryReader(data) : data;
  5. if (stream && stream.length > 512) {
  6. const buffer = stream.peek(512);
  7. const sum = buffer.map((value, index) => (index >= 148 && index < 156) ? 32 : value).reduce((a, b) => a + b, 0);
  8. const checksum = parseInt(Array.from(buffer.slice(148, 156)).map((c) => String.fromCharCode(c)).join('').split('\0').shift(), 8);
  9. if (!isNaN(checksum) && sum === checksum) {
  10. return new tar.Archive(stream);
  11. }
  12. }
  13. return null;
  14. }
  15. constructor(stream) {
  16. this._entries = new Map();
  17. const position = stream.position;
  18. while (stream.position < stream.length) {
  19. const entry = new tar.Entry(stream);
  20. if (entry.type === '0' || entry.type === '1' || entry.type === '2') {
  21. this._entries.set(entry.name, entry.stream);
  22. }
  23. if (stream.position + 512 > stream.length ||
  24. stream.peek(512).every((value) => value === 0x00)) {
  25. break;
  26. }
  27. }
  28. stream.seek(position);
  29. }
  30. get entries() {
  31. return this._entries;
  32. }
  33. };
  34. tar.Entry = class {
  35. constructor(stream) {
  36. const buffer = stream.read(512);
  37. const reader = new tar.BinaryReader(buffer);
  38. const sum = buffer.map((value, index) => (index >= 148 && index < 156) ? 32 : value).reduce((a, b) => a + b, 0);
  39. let checksum = '';
  40. for (let i = 148; i < 156 && buffer[i] !== 0x00; i++) {
  41. checksum += String.fromCharCode(buffer[i]);
  42. }
  43. checksum = parseInt(checksum, 8);
  44. if (isNaN(checksum) || sum !== checksum) {
  45. throw new tar.Error('Invalid tar archive.');
  46. }
  47. this._name = reader.string(100);
  48. reader.string(8); // file mode
  49. reader.string(8); // owner
  50. reader.string(8); // group
  51. const size = parseInt(reader.string(12).trim(), 8);
  52. reader.string(12); // timestamp
  53. reader.string(8); // checksum
  54. this._type = reader.string(1);
  55. reader.string(100); // name of linked file
  56. if (reader.string(6) === 'ustar') {
  57. reader.string(2); // ustar version
  58. reader.string(32); // owner user name
  59. reader.string(32); // owner group name
  60. reader.string(8); // device major number
  61. reader.string(8); // device number number
  62. this._name = reader.string(155) + this._name;
  63. }
  64. this._stream = stream.stream(size);
  65. stream.read(((size % 512) != 0) ? (512 - (size % 512)) : 0);
  66. }
  67. get type() {
  68. return this._type;
  69. }
  70. get name() {
  71. return this._name;
  72. }
  73. get stream() {
  74. return this._stream;
  75. }
  76. };
  77. tar.BinaryReader = class {
  78. constructor(buffer) {
  79. this._buffer = buffer;
  80. this._length = buffer.length;
  81. this._position = 0;
  82. this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  83. }
  84. get position() {
  85. return this._position;
  86. }
  87. get length() {
  88. return this._length;
  89. }
  90. create(buffer) {
  91. return new tar.BinaryReader(buffer);
  92. }
  93. stream(length) {
  94. return this.create(this.read(length));
  95. }
  96. seek(position) {
  97. this._position = position >= 0 ? position : this._length + position;
  98. }
  99. skip(offset) {
  100. this._position += offset;
  101. }
  102. peek(length) {
  103. if (this._position === 0 && length === undefined) {
  104. return this._buffer;
  105. }
  106. const position = this._position;
  107. this.skip(length !== undefined ? length : this._length - this._position);
  108. const end = this._position;
  109. this.seek(position);
  110. return this._buffer.subarray(position, end);
  111. }
  112. read(length) {
  113. if (this._position === 0 && length === undefined) {
  114. this._position = this._length;
  115. return this._buffer;
  116. }
  117. const position = this._position;
  118. this.skip(length !== undefined ? length : this._length - this._position);
  119. return this._buffer.subarray(position, this._position);
  120. }
  121. string(length) {
  122. const buffer = this.read(length);
  123. let position = 0;
  124. let content = '';
  125. for (let i = 0; i < length; i++) {
  126. const c = buffer[position++];
  127. if (c === 0) {
  128. break;
  129. }
  130. content += String.fromCharCode(c);
  131. }
  132. return content;
  133. }
  134. };
  135. tar.Error = class extends Error {
  136. constructor(message) {
  137. super(message);
  138. this.name = 'tar Error';
  139. }
  140. };
  141. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  142. module.exports.Archive = tar.Archive;
  143. }