Procházet zdrojové kódy

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.
toby před 8 roky
rodič
revize
e809deb914

+ 2 - 0
src/js/config/Categories.js

@@ -209,6 +209,8 @@ var Categories = [
             "Zip",
             "Unzip",
             "Bzip2 Decompress",
+            "Tar",
+            "Untar",
         ]
     },
     {

+ 21 - 0
src/js/config/OperationConfig.js

@@ -2993,5 +2993,26 @@ var OperationConfig = {
                 value: MorseCode.WORD_DELIM_OPTIONS
             }
         ]
+    },
+    "Tar": {
+        description: "Packs the input into a tarball.<br><br>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: [
+        ]
     }
 };

+ 59 - 0
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 = "<div class='panel panel-default'>" +
+                   "<div class='panel-heading' role='tab'>" +
+                   "<h4 class='panel-title'>" +
+                   file.fileName +
+                   // The following line is for formatting when HTML is stripped
+                   "<span style='display: none'>\n0 bytes\n</span>" +
+                   "</h4>" +
+                   "</div>" +
+                   "</div>";
+            return html;
+        };
+
+        var formatFile = function(file, i) {
+            var html = "<div class='panel panel-default'>" +
+                       "<div class='panel-heading' role='tab' id='heading" + i + "'>" +
+                       "<h4 class='panel-title'>" +
+                       "<a class='collapsed' role='button' data-toggle='collapse' " +
+                       "data-parent='#zip-accordion' href='#collapse" + i + "' " +
+                       "aria-expanded='true' aria-controls='collapse" + i +"'>" +
+                       file.fileName +
+                       "<span class='pull-right'>" +
+                       // These are for formatting when stripping HTML
+                       "<span style='display: none'>\n</span>" +
+                       file.size.toLocaleString() + " bytes" +
+                       "<span style='display: none'>\n</span>" +
+                       "</span>" +
+                       "</a>" +
+                       "</h4>" +
+                       "</div>" +
+                       "<div id='collapse" + i + "' class='panel-collapse collapse' " +
+                       "role='tabpanel' aria-labelledby='heading" + i + "'>" +
+                       "<div class='panel-body'>" +
+                       "<pre><code>" + Utils.escapeHtml(file.contents) + "</pre></code></div>" +
+                       "</div>" +
+                       "</div>";
+            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

+ 109 - 1
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;
+    },
 };