diff --git a/bin/lsa b/bin/lsa index 2730681..c7f4c58 100755 --- a/bin/lsa +++ b/bin/lsa @@ -1,3 +1,3 @@ #!/usr/bin/env node -require('../lib/ls-archive-cli')() +require('../src/ls-archive-cli')() diff --git a/package.json b/package.json index cd149d7..1bc189b 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "bugs": { "url": "https://github.com/pulsar-edit/node-ls-archive/issues" }, - "main": "./lib/ls-archive.js", + "main": "./src/ls-archive.js", "bin": { "lsa": "./bin/lsa" }, diff --git a/spec/bzip-spec.coffee b/spec/bzip-spec.coffee index c7d0175..2012414 100644 --- a/spec/bzip-spec.coffee +++ b/spec/bzip-spec.coffee @@ -1,4 +1,4 @@ -archive = require '../lib/ls-archive' +archive = require '../src/ls-archive' path = require 'path' describe "bzipped tar files", -> @@ -32,7 +32,7 @@ describe "bzipped tar files", -> expect(bzipPaths[0].isDirectory()).toBe false expect(bzipPaths[0].isFile()).toBe true expect(bzipPaths[0].isSymbolicLink()).toBe false - + it "returns files in the bzipped tar archive", -> bzipPaths = null callback = (error, paths) -> bzipPaths = paths @@ -68,7 +68,7 @@ describe "bzipped tar files", -> expect(bzipPaths[0].isDirectory()).toBe true expect(bzipPaths[0].isFile()).toBe false expect(bzipPaths[0].isSymbolicLink()).toBe false - + it "returns folders in the bzipped tar archive", -> bzipPaths = null callback = (error, paths) -> bzipPaths = paths @@ -125,7 +125,7 @@ describe "bzipped tar files", -> archive.readFile(archivePath, 'file.txt', callback) waitsFor -> pathContents? runs -> expect(pathContents.toString()).toBe 'hello\n' - + it "calls back with the contents of the given path", -> archivePath = path.join(fixturesRoot, 'one-file.tbz2') pathContents = null diff --git a/spec/common-spec.coffee b/spec/common-spec.coffee index 56e1a49..ae49c2f 100644 --- a/spec/common-spec.coffee +++ b/spec/common-spec.coffee @@ -1,4 +1,4 @@ -archive = require '../lib/ls-archive' +archive = require '../src/ls-archive' path = require 'path' describe "Common behavior", -> diff --git a/spec/gzip-spec.coffee b/spec/gzip-spec.coffee index b59169c..5d4bcb7 100644 --- a/spec/gzip-spec.coffee +++ b/spec/gzip-spec.coffee @@ -1,4 +1,4 @@ -archive = require '../lib/ls-archive' +archive = require '../src/ls-archive' path = require 'path' describe "gzipped tar files", -> diff --git a/spec/tar-spec.coffee b/spec/tar-spec.coffee index c4aeaf6..50ffe4b 100644 --- a/spec/tar-spec.coffee +++ b/spec/tar-spec.coffee @@ -1,4 +1,4 @@ -archive = require '../lib/ls-archive' +archive = require '../src/ls-archive' path = require 'path' describe "tar files", -> diff --git a/spec/zip-spec.coffee b/spec/zip-spec.coffee index a331c83..a387a74 100644 --- a/spec/zip-spec.coffee +++ b/spec/zip-spec.coffee @@ -1,4 +1,4 @@ -archive = require '../lib/ls-archive' +archive = require '../src/ls-archive' path = require 'path' describe "zip files", -> diff --git a/src/ls-archive-cli.coffee b/src/ls-archive-cli.coffee deleted file mode 100644 index 50127b8..0000000 --- a/src/ls-archive-cli.coffee +++ /dev/null @@ -1,45 +0,0 @@ -path = require 'path' -async = require 'async' -colors = require 'colors' -optimist = require 'optimist' -archive = require './ls-archive' - -module.exports = -> - cli = optimist.usage( """ - Usage: lsa [file ...] - - List the files and folders inside an archive file. - - Supports .zip, .tar, .tar.gz, .tgz, .tar.bz2, .tbz and .tbz2 files. - """) - .describe('colors', 'Enable colored output').default('colors', true).boolean('colors') - .describe('help', 'Show this message').alias('h', 'help') - .demand(1) - - if cli.argv.help - cli.showHelp() - return - - unless cli.argv.colors - colors.setTheme - cyan: 'stripColors' - red: 'stripColors' - - queue = async.queue (archivePath, callback) -> - do (archivePath) -> - archive.list archivePath, (error, files) -> - if error? - console.error("Error reading: #{archivePath}".red) - else - console.log("#{archivePath.cyan} (#{files.length})") - for file, index in files - if index is files.length - 1 - prefix = '\u2514\u2500\u2500 ' - else - prefix = '\u251C\u2500\u2500 ' - console.log "#{prefix}#{file.getPath()}" - console.log() - callback() - - files = cli.argv._ - files.forEach (file) -> queue.push(path.resolve(process.cwd(), file)) diff --git a/src/ls-archive-cli.js b/src/ls-archive-cli.js new file mode 100644 index 0000000..cebc407 --- /dev/null +++ b/src/ls-archive-cli.js @@ -0,0 +1,60 @@ +const path = require("path"); +const async = require("async"); +const colors = require("colors"); +const optimist = require("optimist"); +const archive = require("./ls-archive.js"); + +module.exports = function() { + const cli = optimist.usage(`Usage: lsa [file ...] + +List the files and folders inside an archive file. + +Supports .zip, .tar, .tar.gz, .tgz, .tar.bz2, .tbz and .tbz2 files.`) + .describe('colors', 'Enable colored output') + .default('colors', true) + .boolean('colors') + .describe('help', 'Show this message') + .alias('h', 'help') + .demand(1); + + if (cli.argv.help) { + cli.showHelp(); + return; + } + if (!cli.argv.colors) { + colors.setTheme({ + cyan: 'stripColors', + red: 'stripColors' + }); + } + let queue = async.queue(function(archivePath, callback) { + return (function(archivePath) { + return archive.list(archivePath, function(error, files) { + if (error != null) { + console.error(`Error reading: ${archivePath}`.red); + } else { + + console.log(`${archivePath.cyan} (${files.length})`); + for (let i = 0; i < files.length; i++) { + let file = files[i]; + let prefix; + if (i === files.length - 1) { + prefix = '\u2514\u2500\u2500 '; + } else { + prefix = '\u251C\u2500\u2500 '; + } + console.log(`${prefix}${file.getPath()}`); + } + + console.log(); + } + return callback(); + }); + })(archivePath); + }); + + let files = cli.argv._; + return files.forEach(function(file) { + return queue.push(path.resolve(process.cwd(), file)); + }); +}; diff --git a/src/ls-archive.coffee b/src/ls-archive.coffee deleted file mode 100644 index 4421767..0000000 --- a/src/ls-archive.coffee +++ /dev/null @@ -1,266 +0,0 @@ -fs = require 'fs' -path = require 'path' -util = require 'util' - -class ArchiveEntry - constructor: (@path, @type) -> - @children = [] if @isDirectory() - - add: (entry) -> - return false unless @isParentOf(entry) - - segments = entry.getPath().substring(@getPath().length + 1).split(path.sep) - return false if segments.length is 0 - - if segments.length is 1 - @children.push(entry) - true - else - name = segments[0] - child = findEntryWithName(@children, name) - unless child? - child = new ArchiveEntry("#{@getPath()}#{path.sep}#{name}", 5) - @children.push(child) - if child.isDirectory() - child.add(entry) - else - false - - isParentOf: (entry) -> - @isDirectory() and entry.getPath().indexOf("#{@getPath()}#{path.sep}") is 0 - - getPath: -> @path - getName: -> @name ?= path.basename(@path) - isFile: -> @type is 0 - isDirectory: -> @type is 5 - isSymbolicLink: -> @type is 2 - toString: -> @getPath() - -findEntryWithName = (entries, name) -> - return entry for entry in entries when name is entry.getName() - -convertToTree = (entries) -> - rootEntries = [] - for entry in entries - segments = entry.getPath().split(path.sep) - if segments.length is 1 - rootEntries.push(entry) - else - name = segments[0] - parent = findEntryWithName(rootEntries, name) - unless parent? - parent = new ArchiveEntry(name, 5) - rootEntries.push(parent) - parent.add(entry) - rootEntries - -wrapCallback = (callback) -> - called = false - (error, data) -> - unless called - error = new Error(error) if error? and not util.isError(error) - called = true - callback(error, data) - -listZip = (archivePath, options, callback) -> - yauzl = require 'yauzl' - entries = [] - yauzl.open archivePath, {lazyEntries: true}, (error, zipFile) -> - return callback(error) if error - zipFile.readEntry() - zipFile.on 'error', callback - zipFile.on 'entry', (entry) -> - if entry.fileName[-1..] is '/' - entryPath = entry.fileName[0...-1] - entryType = 5 - else - entryPath = entry.fileName - entryType = 0 - entryPath = entryPath.replace(/\//g, path.sep) - entries.push(new ArchiveEntry(entryPath, entryType)) - zipFile.readEntry() - zipFile.on 'end', -> - entries = convertToTree(entries) if options.tree - callback(null, entries) - -listGzip = (archivePath, options, callback) -> - zlib = require 'zlib' - fileStream = fs.createReadStream(archivePath) - fileStream.on 'error', callback - gzipStream = fileStream.pipe(zlib.createGunzip()) - gzipStream.on 'error', callback - listTarStream(gzipStream, options, callback) - -listBzip = (archivePath, options, callback) -> - bzip = require 'unbzip2-stream' - fileStream = fs.createReadStream(archivePath) - fileStream.on 'error', callback - bzipStream = fileStream.pipe(bzip()) - bzipStream.on 'error', callback - listTarStream(bzipStream, options, callback) - -listTar = (archivePath, options, callback) -> - fileStream = fs.createReadStream(archivePath) - fileStream.on 'error', callback - listTarStream(fileStream, options, callback) - -listTarStream = (inputStream, options, callback) -> - entries = [] - tarStream = inputStream.pipe(require('tar').Parse()) - tarStream.on 'error', callback - tarStream.on 'entry', (entry) -> - if entry.props.path[-1..] is '/' - entryPath = entry.props.path[0...-1] - else - entryPath = entry.props.path - entryType = parseInt(entry.props.type) - entryPath = entryPath.replace(/\//g, path.sep) - entries.push(new ArchiveEntry(entryPath, entryType)) - tarStream.on 'end', -> - entries = convertToTree(entries) if options.tree - callback(null, entries) - -readFileFromZip = (archivePath, filePath, callback) -> - yauzl = require 'yauzl' - yauzl.open archivePath, {lazyEntries: true}, (error, zipFile) -> - return callback(error) if error - zipFile.readEntry() - zipFile.on 'error', callback - zipFile.on 'end', -> - callback("#{filePath} does not exist in the archive: #{archivePath}") - zipFile.on 'entry', (entry) -> - return zipFile.readEntry() unless filePath is entry.fileName.replace(/\//g, path.sep) - - if filePath[-1..] isnt path.sep - zipFile.openReadStream entry, (error, entryStream) -> - return callback(error) if error - readEntry(entryStream, callback) - else - callback("#{filePath} is not a normal file in the archive: #{archivePath}") - -readFileFromGzip = (archivePath, filePath, callback) -> - fileStream = fs.createReadStream(archivePath) - fileStream.on 'error', callback - gzipStream = fileStream.pipe(require('zlib').createGunzip()) - gzipStream.on 'error', callback - gzipStream.on 'end', -> - callback("#{filePath} does not exist in the archive: #{archivePath}") - readFileFromTarStream(gzipStream, archivePath, filePath, callback) - -readFileFromBzip = (archivePath, filePath, callback) -> - fileStream = fs.createReadStream(archivePath) - fileStream.on 'error', callback - bzipStream = fileStream.pipe(require('unbzip2-stream')()) - bzipStream.on 'error', callback - bzipStream.on 'end', -> - callback("#{filePath} does not exist in the archive: #{archivePath}") - readFileFromTarStream(bzipStream, archivePath, filePath, callback) - -readFileFromTar = (archivePath, filePath, callback) -> - fileStream = fs.createReadStream(archivePath) - fileStream.on 'error', callback - fileStream.on 'end', -> - callback("#{filePath} does not exist in the archive: #{archivePath}") - readFileFromTarStream(fileStream, archivePath, filePath, callback) - -readFileFromTarStream = (inputStream, archivePath, filePath, callback) -> - tar = require 'tar' - tarStream = inputStream.pipe(tar.Parse()) - - tarStream.on 'error', callback - tarStream.on 'entry', (entry) -> - return unless filePath is entry.props.path.replace(/\//g, path.sep) - - if entry.props.type is '0' - readEntry(entry, callback) - else - callback("#{filePath} is not a normal file in the archive: #{archivePath}") - -readEntry = (entry, callback) -> - contents = [] - entry.on 'data', (data) -> contents.push(data) - entry.on 'end', -> callback(null, Buffer.concat(contents)) - -isTarPath = (archivePath) -> - path.extname(archivePath) is '.tar' - -isZipPath = (archivePath) -> - extension = path.extname(archivePath) - extension in ['.epub', '.jar', '.love', '.war', '.zip', '.egg', '.whl', '.xpi', '.nupkg'] - -isGzipPath = (archivePath) -> - path.extname(archivePath) is '.tgz' or - path.extname(path.basename(archivePath, '.gz')) is '.tar' - -isBzipPath = (archivePath) -> - path.extname(archivePath) is '.tbz' or - path.extname(archivePath) is '.tbz2' or - path.extname(path.basename(archivePath, '.bz2')) is '.tar' - -module.exports = - isPathSupported: (archivePath) -> - return false unless archivePath - isTarPath(archivePath) or - isZipPath(archivePath) or - isGzipPath(archivePath) or - isBzipPath(archivePath) - - list: (archivePath, options={}, callback) -> - if typeof options is 'function' - callback = options - options = {} - - if isTarPath(archivePath) - listTar(archivePath, options, wrapCallback(callback)) - else if isGzipPath(archivePath) - listGzip(archivePath, options, wrapCallback(callback)) - else if isBzipPath(archivePath) - listBzip(archivePath, options, wrapCallback(callback)) - else if isZipPath(archivePath) - listZip(archivePath, options, wrapCallback(callback)) - else - callback(new Error("'#{path.extname(archivePath)}' files are not supported")) - undefined - - readFile: (archivePath, filePath, callback) -> - if isTarPath(archivePath) - readFileFromTar(archivePath, filePath, wrapCallback(callback)) - else if isGzipPath(archivePath) - readFileFromGzip(archivePath, filePath, wrapCallback(callback)) - else if isBzipPath(archivePath) - readFileFromBzip(archivePath, filePath, wrapCallback(callback)) - else if isZipPath(archivePath) - readFileFromZip(archivePath, filePath, wrapCallback(callback)) - else - callback(new Error("'#{path.extname(archivePath)}' files are not supported")) - undefined - - readGzip: (gzipArchivePath, callback) -> - callback = wrapCallback(callback) - - zlib = require 'zlib' - fileStream = fs.createReadStream(gzipArchivePath) - fileStream.on 'error', callback - gzipStream = fileStream.pipe(zlib.createGunzip()) - gzipStream.on 'error', callback - - chunks = [] - gzipStream.on 'data', (chunk) -> - chunks.push(chunk) - gzipStream.on 'end', -> - callback(null, Buffer.concat(chunks)) - - readBzip: (bzipArchivePath, callback) -> - callback = wrapCallback(callback) - - bzip = require 'unbzip2-stream' - fileStream = fs.createReadStream(bzipArchivePath) - fileStream.on 'error', callback - bzipStream = fileStream.pipe(bzip()) - bzipStream.on 'error', callback - - chunks = [] - bzipStream.on 'data', (chunk) -> - chunks.push(chunk) - bzipStream.on 'end', -> - callback(null, Buffer.concat(chunks)) diff --git a/src/ls-archive.js b/src/ls-archive.js new file mode 100644 index 0000000..3a12704 --- /dev/null +++ b/src/ls-archive.js @@ -0,0 +1,380 @@ +const fs = require("fs"); +const path = require("path"); +const util = require("util"); + +class ArchiveEntry { + constructor(path1, type) { + this.path = path1; + this.type = type; + if (this.isDirectory()) { + this.children = []; + } + } + + add(entry) { + if (!this.isParentOf(entry)) { + return false; + } + let segments = entry.getPath().substring(this.getPath().length + 1).split(path.sep); + if (segments.length === 0) { + return false; + } + if (segments.length === 1) { + this.children.push(entry); + return true; + } else { + let name = segments[0]; + let child = findEntryWithName(this.children, name); + if (child == null) { + child = new ArchiveEntry(`${this.getPath()}${path.sep}${name}`, 5); + this.children.push(child); + } + if (child.isDirectory()) { + return child.add(entry); + } else { + return false; + } + } + } + + isParentOf(entry) { + return this.isDirectory() && entry.getPath().indexOf(`${this.getPath()}${path.sep}`) === 0; + } + + getPath() { + return this.path; + } + + getName() { + return this.name != null ? this.name : this.name = path.basename(this.path); + } + + isFile() { + return this.type === 0; + } + + isDirectory() { + return this.type === 5; + } + + isSymbolicLink() { + return this.type === 2; + } + + toString() { + return this.getPath(); + } + +} + +function findEntryWithName(entries, name) { + for (let i = 0; i < entries.length; i++) { + let entry = entries[i]; + if (name === entry.getName()) { + return entry; + } + } +} + +function convertToTree(entries) { + let rootEntries = []; + + for (let i = 0; i < entries.length; i++) { + let entry = entries[i]; + let segments = entry.getPath().split(path.sep); + + if (segments.length === 1) { + rootEntries.push(entry); + } else { + let name = segments[0]; + let parent = findEntryWithName(rootEntries, name); + if (parent == null) { + parent = new ArchiveEntry(name, 5); + rootEntries.push(parent); + } + parent.add(entry); + } + } + return rootEntries; +} + +function wrapCallback(cb) { + let called = false; + + return function(error, data) { + if (!called) { + if ((error != null) && !util.isError(error)) { + error = new Error(error); + } + called = true; + return cb(error, data); + } + }; +} + +function listZip(archivePath, options, callback) { + const yauzl = require("yauzl"); + let entries = []; + return yauzl.open(archivePath, { + lazyEntries: true + }, function(error, zipFile) { + if (error) { + return callback(error); + } + zipFile.readEntry(); + zipFile.on('error', callback); + zipFile.on('entry', function(entry) { + var entryPath, entryType; + if (entry.fileName.slice(-1) === '/') { + entryPath = entry.fileName.slice(0, -1); + entryType = 5; + } else { + entryPath = entry.fileName; + entryType = 0; + } + entryPath = entryPath.replace(/\//g, path.sep); + entries.push(new ArchiveEntry(entryPath, entryType)); + return zipFile.readEntry(); + }); + return zipFile.on('end', function() { + if (options.tree) { + entries = convertToTree(entries); + } + return callback(null, entries); + }); + }); +} + +function listGzip(archivePath, options, callback) { + const zlib = require("zlib"); + let fileStream = fs.createReadStream(archivePath); + fileStream.on("error", callback); + + let gzipStream = fileStream.pipe(zlib.createGunzip()); + gzipStream.on("error", callback); + + return listTarStream(gzipStream, options, callback); +} + +function listBzip(archivePath, options, callback) { + const bzip = require("unbzip2-stream"); + + let fileStream = fs.createReadStream(archivePath); + fileStream.on("error", callback); + + let bzipStream = fileStream.pipe(bzip()); + bzipStream.on("error", callback); + + return listTarStream(bzipStream, options, callback); +} + +function listTar(archivePath, options, callback) { + let fileStream = fs.createReadStream(archivePath); + fileStream.on("error", callback); + + return listTarStream(fileStream, options, callback); +} + +function listTarStream(inputStream, options, callback) { + let entries = []; + let tarStream = inputStream.pipe(require("tar").Parse()); + tarStream.on("error", callback); + tarStream.on("entry", function(entry) { + let entryPath; + if (entry.props.path.slice(-1) === "/") { + entryPath = entry.props.path.slice(0, -1); + } else { + entryPath = entry.props.path; + } + let entryType = parseInt(entry.props.type); + entryPath = entryPath.replace(/\//g, path.sep); + return entries.push(new ArchiveEntry(entryPath, entryType)); + }); + return tarStream.on("end", function () { + if (options.tree) { + entries = convertToTree(entries); + } + return callback(null, entries); + }); +} + +function readFileFromZip(archivePath, filePath, callback) { + const yauzl = require("yauzl"); + return yauzl.open(archivePath, { + lazyEntries: true + }, function(error, zipFile) { + if (error) { + return callback(error); + } + zipFile.readEntry(); + zipFile.on('error', callback); + zipFile.on('end', function() { + return callback(`${filePath} does not exist in the archive: ${archivePath}`); + }); + return zipFile.on('entry', function(entry) { + if (filePath !== entry.fileName.replace(/\//g, path.sep)) { + return zipFile.readEntry(); + } + if (filePath.slice(-1) !== path.sep) { + return zipFile.openReadStream(entry, function(error, entryStream) { + if (error) { + return callback(error); + } + return readEntry(entryStream, callback); + }); + } else { + return callback(`${filePath} is not a normal file in the archive: ${archivePath}`); + } + }); + }); +} + +function readFileFromGzip(archivePath, filePath, callback) { + let fileStream = fs.createReadStream(archivePath); + fileStream.on("error", callback); + let gzipStream = fileStream.pipe(require("zlib").createGunzip()); + gzipStream.on("error", callback); + gzipStream.on("end", function() { + return callback(`${filePath} does not exist in the archive: ${archivePath}`); + }); + return readFileFromTarStream(gzipStream, archivePath, filePath, callback); +} + +function readFileFromBzip(archivePath, filePath, callback) { + let fileStream = fs.createReadStream(archivePath); + fileStream.on("error", callback); + let bzipStream = fileStream.pipe(require("unbzip2-stream")()); + bzipStream.on("error", callback); + bzipStream.on("end", function() { + return callback(`${filePath} does not exist in the archive: ${archivePath}`); + }); + return readFileFromTarStream(bzipStream, archivePath, filePath, callback); +} + +function readFileFromTar(archivePath, filePath, callback) { + let fileStream = fs.createReadStream(archivePath); + fileStream.on('error', callback); + fileStream.on('end', function() { + return callback(`${filePath} does not exist in the archive: ${archivePath}`); + }); + return readFileFromTarStream(fileStream, archivePath, filePath, callback); +} + +function readFileFromTarStream(inputStream, archivePath, filePath, callback) { + const tar = require("tar"); + let tarStream = inputStream.pipe(tar.Parse()); + tarStream.on("error", callback); + + return tarStream.on("entry", function(entry) { + if (filePath !== entry.props.path.replace(/\//g, path.sep)) { + return; + } + if (entry.props.type === "0") { + return readEntry(entry, callback); + } else { + return callback(`${filePath} is not a normal file in the archive: ${archivePath}`); + } + }); +} + +function readEntry(entry, callback) { + let contents = []; + + entry.on("data", function(data) { + return contents.push(data); + }); + + return entry.on("end", function() { + return callback(null, Buffer.concat(contents)); + }); +} + +function isTarPath(archivePath) { + return path.extname(archivePath) === ".tar"; +} + +function isZipPath(archivePath) { + let ext = path.extname(archivePath); + let exts = [".epub", ".jar", ".love", ".war", ".zip", ".egg", ".whl", ".xpi", ".nupkg"]; + return exts.includes(ext); +} + +function isGzipPath(archivePath) { + return path.extname(archivePath) === ".tgz" || path.extname(path.basename(archivePath, ".gz")) === ".tar"; +} + +function isBzipPath(archivePath) { + return path.extname(archivePath) === ".tbz" || path.extname(archivePath) === ".tbz2" || path.extname(path.basename(archivePath, ".bz2")) === ".tar"; +} + +module.exports = { + isPathSupported: function(archivePath) { + if (!archivePath) { + return false; + } + return isTarPath(archivePath) || isZipPath(archivePath) || isGzipPath(archivePath) || isBzipPath(archivePath); + }, + list: function(archivePath, options = {}, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + if (isTarPath(archivePath)) { + listTar(archivePath, options, wrapCallback(callback)); + } else if (isGzipPath(archivePath)) { + listGzip(archivePath, options, wrapCallback(callback)); + } else if (isBzipPath(archivePath)) { + listBzip(archivePath, options, wrapCallback(callback)); + } else if (isZipPath(archivePath)) { + listZip(archivePath, options, wrapCallback(callback)); + } else { + callback(new Error(`'${path.extname(archivePath)}' files are not supported`)); + } + return void 0; + }, + readFile: function(archivePath, filePath, callback) { + if (isTarPath(archivePath)) { + readFileFromTar(archivePath, filePath, wrapCallback(callback)); + } else if (isGzipPath(archivePath)) { + readFileFromGzip(archivePath, filePath, wrapCallback(callback)); + } else if (isBzipPath(archivePath)) { + readFileFromBzip(archivePath, filePath, wrapCallback(callback)); + } else if (isZipPath(archivePath)) { + readFileFromZip(archivePath, filePath, wrapCallback(callback)); + } else { + callback(new Error(`'${path.extname(archivePath)}' files are not supported`)); + } + return void 0; + }, + readGzip: function(gzipArchivePath, callback) { + callback = wrapCallback(callback); + const zlib = require("zlib"); + let fileStream = fs.createReadStream(gzipArchivePath); + fileStream.on("error", callback); + let gzipStream = fileStream.pipe(zlib.createGunzip()); + gzipStream.on("error", callback); + let chunks = []; + + gzipStream.on("data", function(chunk) { + return chunks.push(chunk); + }); + return gzipStream.on("end", function() { + return callback(null, Buffer.concat(chunks)); + }); + }, + readBzip: function(bzipArchivePath, callback) { + callback = wrapCallback(callback); + const bzip = require("unbzip2-stream"); + let fileStream = fs.createReadStream(bzipArchivePath); + fileStream.on("error", callback); + let bzipStream = fileStream.pipe(bzip()); + bzipStream.on("error", callback); + let chunks = []; + bzipStream.on("data", function(chunk) { + return chunks.push(chunk); + }); + return bzipStream.on("end", function() { + return callback(null, Buffer.concat(chunks)); + }); + }, +};