calibre-web/cps/static/js/archive/untar.js

180 lines
5.6 KiB
JavaScript
Raw Normal View History

/**
2019-05-09 16:16:03 -07:00
* untar.js
*
* Licensed under the MIT License
*
* Copyright(c) 2011 Google Inc.
*
* Reference Documentation:
*
* TAR format: http://www.gnu.org/software/automake/manual/tar/Standard.html
*/
2019-06-17 10:48:17 -07:00
/* global bitjs, importScripts, Uint8Array */
// This file expects to be invoked as a Worker (see onmessage below).
2019-06-17 10:48:17 -07:00
importScripts("../io/bytestream.js");
importScripts("archive.js");
// Progress variables.
2019-05-12 22:53:25 -07:00
var currentFilename = "";
var currentFileNumber = 0;
var currentBytesUnarchivedInFile = 0;
var currentBytesUnarchived = 0;
var totalUncompressedBytesInArchive = 0;
var totalFilesInArchive = 0;
2019-06-17 10:48:17 -07:00
var allLocalFiles = [];
// Helper functions.
2019-05-12 22:53:25 -07:00
var info = function(str) {
2019-05-13 12:28:06 -07:00
postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
};
2019-05-12 22:53:25 -07:00
var err = function(str) {
2019-05-13 12:28:06 -07:00
postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
};
// Removes all characters from the first zero-byte in the string onwards.
var readCleanString = function(bstr, numBytes) {
var str = bstr.readString(numBytes);
var zIndex = str.indexOf(String.fromCharCode(0));
return zIndex != -1 ? str.substr(0, zIndex) : str;
};
2019-05-12 22:53:25 -07:00
var postProgress = function() {
2019-05-13 12:28:06 -07:00
postMessage(new bitjs.archive.UnarchiveProgressEvent(
currentFilename,
currentFileNumber,
currentBytesUnarchivedInFile,
currentBytesUnarchived,
totalUncompressedBytesInArchive,
2019-06-17 10:48:17 -07:00
totalFilesInArchive
));
2019-05-09 16:16:03 -07:00
};
2019-05-12 22:53:25 -07:00
// takes a ByteStream and parses out the local file information
var TarLocalFile = function(bstream) {
2019-05-13 12:28:06 -07:00
this.isValid = false;
var bytesRead = 0;
2019-05-13 12:28:06 -07:00
// Read in the header block
this.name = readCleanString(bstream, 100);
this.mode = readCleanString(bstream, 8);
this.uid = readCleanString(bstream, 8);
this.gid = readCleanString(bstream, 8);
this.size = parseInt(readCleanString(bstream, 12), 8);
this.mtime = readCleanString(bstream, 12);
this.chksum = readCleanString(bstream, 8);
this.typeflag = readCleanString(bstream, 1);
this.linkname = readCleanString(bstream, 100);
this.maybeMagic = readCleanString(bstream, 6);
2019-06-17 10:48:17 -07:00
if (this.maybeMagic === "ustar") {
2019-05-13 12:28:06 -07:00
this.version = readCleanString(bstream, 2);
this.uname = readCleanString(bstream, 32);
this.gname = readCleanString(bstream, 32);
this.devmajor = readCleanString(bstream, 8);
this.devminor = readCleanString(bstream, 8);
this.prefix = readCleanString(bstream, 155);
if (this.prefix.length) {
this.name = this.prefix + this.name;
}
bstream.readBytes(12); // 512 - 500
} else {
bstream.readBytes(255); // 512 - 257
2019-05-12 22:53:25 -07:00
}
bytesRead += 512;
2019-05-13 12:28:06 -07:00
// Done header, now rest of blocks are the file contents.
this.filename = this.name;
this.fileData = null;
info("Untarring file '" + this.filename + "'");
info(" size = " + this.size);
info(" typeflag = " + this.typeflag);
// A regular file.
if (this.typeflag == 0) {
info(" This is a regular file.");
var sizeInBytes = parseInt(this.size);
this.fileData = new Uint8Array(bstream.readBytes(sizeInBytes));
bytesRead += sizeInBytes;
2019-05-13 12:28:06 -07:00
if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) {
this.isValid = true;
}
// Round up to 512-byte blocks.
2019-06-17 10:48:17 -07:00
var remaining = 512 - (bytesRead % 512);
2019-05-13 12:28:06 -07:00
if (remaining > 0 && remaining < 512) {
bstream.readBytes(remaining);
}
} else if (this.typeflag == 5) {
info(" This is a directory.");
}
2019-06-17 10:48:17 -07:00
};
2019-05-12 22:53:25 -07:00
var untar = function(arrayBuffer) {
postMessage(new bitjs.archive.UnarchiveStartEvent());
2019-05-13 12:28:06 -07:00
currentFilename = "";
currentFileNumber = 0;
currentBytesUnarchivedInFile = 0;
currentBytesUnarchived = 0;
totalUncompressedBytesInArchive = 0;
totalFilesInArchive = 0;
allLocalFiles = [];
2019-05-13 12:28:06 -07:00
var bstream = new bitjs.io.ByteStream(arrayBuffer);
postProgress();
/*
// go through whole file, read header of each block and memorize, filepointer
*/
2019-06-17 10:48:17 -07:00
while (bstream.peekNumber(4) !== 0) {
var localFile = new TarLocalFile(bstream);
allLocalFiles.push(localFile);
postProgress();
}
// got all local files, now sort them
allLocalFiles.sort(alphanumCase);
allLocalFiles.forEach(function(oneLocalFile) {
// While we don't encounter an empty block, keep making TarLocalFiles.
2019-05-13 12:28:06 -07:00
if (oneLocalFile && oneLocalFile.isValid) {
// If we make it to this point and haven't thrown an error, we have successfully
// read in the data for a local file, so we can update the actual bytestream.
2019-05-13 12:28:06 -07:00
totalUncompressedBytesInArchive += oneLocalFile.size;
// update progress
currentFilename = oneLocalFile.filename;
currentFileNumber = totalFilesInArchive++;
currentBytesUnarchivedInFile = oneLocalFile.size;
currentBytesUnarchived += oneLocalFile.size;
postMessage(new bitjs.archive.UnarchiveExtractEvent(oneLocalFile));
postProgress();
}
});
totalFilesInArchive = allLocalFiles.length;
2019-05-13 12:28:06 -07:00
postProgress();
postMessage(new bitjs.archive.UnarchiveFinishEvent());
};
// event.data.file has the first ArrayBuffer.
// event.data.bytes has all subsequent ArrayBuffers.
onmessage = function(event) {
try {
untar(event.data.file, true);
} catch (e) {
if (typeof e === "string" && e.startsWith("Error! Overflowed")) {
// Overrun the buffer.
// unarchiveState = UnarchiveState.WAITING;
} else {
2019-06-17 10:48:17 -07:00
err("Found an error while untarring");
err(e);
throw e;
}
}
};