diff --git a/src/js/config/Categories.js b/src/js/config/Categories.js index bca0dba5..36d32e0a 100755 --- a/src/js/config/Categories.js +++ b/src/js/config/Categories.js @@ -209,6 +209,8 @@ var Categories = [ "Zip", "Unzip", "Bzip2 Decompress", + "Tar", + "Untar", ] }, { diff --git a/src/js/config/OperationConfig.js b/src/js/config/OperationConfig.js index a9617059..df8fe22c 100755 --- a/src/js/config/OperationConfig.js +++ b/src/js/config/OperationConfig.js @@ -2993,5 +2993,26 @@ var OperationConfig = { value: MorseCode.WORD_DELIM_OPTIONS } ] + }, + "Tar": { + description: "Packs the input into a tarball.

No support for multiple files at this time.", + run: Compress.tar, + inputType: "byteArray", + outputType: "byteArray", + args: [ + { + name: "Filename", + type: "string", + value: Compress.TAR_FILENAME + } + ] + }, + "Untar": { + description: "Unpacks a tarball and displays it per file.", + run: Compress.untar, + inputType: "byteArray", + outputType: "html", + args: [ + ] } }; diff --git a/src/js/core/Utils.js b/src/js/core/Utils.js index 5c40c1e4..ef9fe1c3 100755 --- a/src/js/core/Utils.js +++ b/src/js/core/Utils.js @@ -929,6 +929,65 @@ var Utils = { }, + /** + * Formats a list of files or directories. + * + * @param {File[]} files + * @returns {html} + */ + HTMLFiles: function(files){ + var formatDirectory = function(file) { + var html = "
" + + "" + + "
"; + return html; + }; + + var formatFile = function(file, i) { + var html = "
" + + "" + + "
" + + "
" + + "
" + Utils.escapeHtml(file.contents) + "
" + + "
" + + "
"; + return html; + }; + + var Utils = this; + var html = ""; + files.forEach(function(file, i) { + if(typeof file.contents !== "undefined") { + html += formatFile(file, i); + } else { + html += formatDirectory(file); + } + }); + return html; + }, + + /** * A mapping of names of delimiter characters to their symbols. * @constant diff --git a/src/js/operations/Compress.js b/src/js/operations/Compress.js index b5a2e6a1..b8a78300 100755 --- a/src/js/operations/Compress.js +++ b/src/js/operations/Compress.js @@ -345,5 +345,113 @@ var Compress = { plain = bzip2.simple(bzip2Reader); return plain; }, - + + + /** + * @constant + * @default + */ + TAR_FILENAME: "file.txt", + + + /** + * Tar unpack operation. + * + * @param {byteArray} input + * @param {Object[]} args + * @returns {byteArray} + */ + tar: function(input, args) { + // Not implemented yet + return input; + }, + + + /** + * Untar unpack operation. + * + * @param {byteArray} input + * @param {Object[]} args + * @returns {html} + */ + untar: function(input, args) { + var Stream = function(input) { + this.bytes = input; + this.position = 0; + }; + + Stream.prototype.readString = function(numBytes) { + var result = ""; + for(var i = this.position; i < this.position + numBytes; i++) { + var currentByte = this.bytes[i]; + if(currentByte === 0) break; + result += String.fromCharCode(currentByte); + } + this.position += numBytes; + return result; + }; + + Stream.prototype.readInt = function(numBytes, base) { + var string = this.readString(numBytes); + return parseInt(string, base); + }; + + Stream.prototype.hasMore = function() { + return this.position < this.bytes.length; + }; + + var stream = new Stream(input), + files = []; + + while(stream.hasMore()) { + var dataPosition = stream.position + 512; + + var file = { + fileName: stream.readString(100), + fileMode: stream.readString(8), + ownerUID: stream.readString(8), + ownerGID: stream.readString(8), + size: parseInt(stream.readString(12), 8), // Octal + lastModTime: new Date(1000 * stream.readInt(12, 8)), // Octal + checksum: stream.readString(8), + type: stream.readString(1), + linkedFileName: stream.readString(100), + USTARFormat: stream.readString(6).indexOf("ustar") >= 0, + }; + + if(file.USTARFormat) { + file.version = stream.readString(2); + file.ownerUserName = stream.readString(32); + file.ownerGroupName = stream.readString(32); + file.deviceMajor = stream.readString(8); + file.deviceMinor = stream.readString(8); + file.filenamePrefix = stream.readString(155); + } + + stream.position = dataPosition; + + if(file.type === "0") { + // File + files.push(file); + var endPosition = stream.position + file.size; + if(file.size % 512 !== 0) { + endPosition += 512 - (file.size % 512); + } + + file.contents = ""; + + while(stream.position < endPosition) { + file.contents += stream.readString(512); + } + } else if(file.type === "5") { + // Directory + files.push(file); + } else { + // Symlink or empty bytes + } + } + + var output = Utils.HTMLFiles(files); + return output; + }, };