From e809deb91428c026eeece260cee2445329eb54cf Mon Sep 17 00:00:00 2001 From: toby Date: Wed, 8 Feb 2017 00:05:52 -0500 Subject: [PATCH] Initial functionality of untar + Added skeleton "Tar" operation with no functionality + Added intial functionality of "Untar" + Added a function in `Utils` `HTMLFormat` to generalize HTML generation of files and folders (could later be used in Unzip). I had a brief search for a small library for tar and untar operations, however they were mostly for node (if anyone finds one we can drop in that would be appreciated) or unmaintained. Luckily the tar spec is relatively easy to understand just from Wikipedia. --- src/js/config/Categories.js | 2 + src/js/config/OperationConfig.js | 21 ++++++ src/js/core/Utils.js | 59 +++++++++++++++++ src/js/operations/Compress.js | 110 ++++++++++++++++++++++++++++++- 4 files changed, 191 insertions(+), 1 deletion(-) diff --git a/src/js/config/Categories.js b/src/js/config/Categories.js index bca0dba..36d32e0 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 a961705..df8fe22 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 5c40c1e..ef9fe1c 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 b5a2e6a..b8a7830 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; + }, };