tar.js 4.9 KB

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