Refactor unpack strategies into separate files.

This commit is contained in:
Markus Reiter 2018-07-23 20:59:21 +02:00
parent 2c9dc62d35
commit b6e54a06e0
44 changed files with 666 additions and 540 deletions

View File

@ -329,7 +329,7 @@ end
# Useful for installing jars. # Useful for installing jars.
class NoUnzipCurlDownloadStrategy < CurlDownloadStrategy class NoUnzipCurlDownloadStrategy < CurlDownloadStrategy
def stage def stage
UncompressedUnpackStrategy.new(cached_location) UnpackStrategy::Uncompressed.new(cached_location)
.extract(basename: basename_without_params) .extract(basename: basename_without_params)
end end
end end

View File

@ -0,0 +1,14 @@
require_relative "shared_examples"
describe UnpackStrategy::Bazaar do
let(:repo) {
mktmpdir.tap do |repo|
FileUtils.touch repo/"test"
(repo/".bzr").mkpath
end
}
let(:path) { repo }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["test"]
end

View File

@ -0,0 +1,8 @@
require_relative "shared_examples"
describe UnpackStrategy::Bzip2 do
let(:path) { TEST_FIXTURE_DIR/"cask/container.bz2" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["container"]
end

View File

@ -0,0 +1,14 @@
require_relative "shared_examples"
describe UnpackStrategy::Cvs do
let(:repo) {
mktmpdir.tap do |repo|
FileUtils.touch repo/"test"
(repo/"CVS").mkpath
end
}
let(:path) { repo }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["CVS", "test"]
end

View File

@ -0,0 +1,32 @@
require_relative "shared_examples"
describe UnpackStrategy::Directory do
let(:path) {
mktmpdir.tap do |path|
FileUtils.touch path/"file"
FileUtils.ln_s "file", path/"symlink"
end
}
subject(:strategy) { described_class.new(path) }
let(:unpack_dir) { mktmpdir }
it "does not follow symlinks" do
strategy.extract(to: unpack_dir)
expect(unpack_dir/"symlink").to be_a_symlink
end
it "preserves permissions of contained files" do
FileUtils.chmod 0644, path/"file"
strategy.extract(to: unpack_dir)
expect((unpack_dir/"file").stat.mode & 0777).to eq 0644
end
it "preserves the permissions of the destination directory" do
FileUtils.chmod 0700, path
FileUtils.chmod 0755, unpack_dir
strategy.extract(to: unpack_dir)
expect(unpack_dir.stat.mode & 0777).to eq 0755
end
end

View File

@ -0,0 +1,17 @@
require_relative "shared_examples"
describe UnpackStrategy::Git do
let(:repo) {
mktmpdir.tap do |repo|
system "git", "-C", repo, "init"
FileUtils.touch repo/"test"
system "git", "-C", repo, "add", "test"
system "git", "-C", repo, "commit", "-m", "Add `test` file."
end
}
let(:path) { repo }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: [".git", "test"]
end

View File

@ -0,0 +1,8 @@
require_relative "shared_examples"
describe UnpackStrategy::Gzip do
let(:path) { TEST_FIXTURE_DIR/"cask/container.gz" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["container"]
end

View File

@ -0,0 +1,8 @@
require_relative "shared_examples"
describe UnpackStrategy::Jar do
let(:path) { TEST_FIXTURE_DIR/"test.jar" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["test.jar"]
end

View File

@ -0,0 +1,7 @@
require_relative "shared_examples"
describe UnpackStrategy::Lha do
let(:path) { TEST_FIXTURE_DIR/"test.lha" }
include_examples "UnpackStrategy::detect"
end

View File

@ -0,0 +1,7 @@
require_relative "shared_examples"
describe UnpackStrategy::Lzip do
let(:path) { TEST_FIXTURE_DIR/"test.lz" }
include_examples "UnpackStrategy::detect"
end

View File

@ -0,0 +1,12 @@
require_relative "shared_examples"
describe UnpackStrategy::Mercurial do
let(:repo) {
mktmpdir.tap do |repo|
(repo/".hg").mkpath
end
}
let(:path) { repo }
include_examples "UnpackStrategy::detect"
end

View File

@ -0,0 +1,7 @@
require_relative "shared_examples"
describe UnpackStrategy::P7Zip do
let(:path) { TEST_FIXTURE_DIR/"cask/container.7z" }
include_examples "UnpackStrategy::detect"
end

View File

@ -0,0 +1,7 @@
require_relative "shared_examples"
describe UnpackStrategy::Rar do
let(:path) { TEST_FIXTURE_DIR/"cask/container.rar" }
include_examples "UnpackStrategy::detect"
end

View File

@ -0,0 +1,16 @@
require "unpack_strategy"
shared_examples "UnpackStrategy::detect" do
it "is correctly detected" do
expect(UnpackStrategy.detect(path)).to be_a described_class
end
end
shared_examples "#extract" do |children: []|
specify "#extract" do
mktmpdir do |unpack_dir|
described_class.new(path).extract(to: unpack_dir)
expect(unpack_dir.children(false).map(&:to_s)).to match_array children
end
end
end

View File

@ -0,0 +1,22 @@
require_relative "shared_examples"
describe UnpackStrategy::Subversion do
let(:repo) {
mktmpdir.tap do |repo|
system "svnadmin", "create", repo
end
}
let(:working_copy) {
mktmpdir.tap do |working_copy|
system "svn", "checkout", "file://#{repo}", working_copy
FileUtils.touch working_copy/"test"
system "svn", "add", working_copy/"test"
system "svn", "commit", working_copy, "-m", "Add `test` file."
end
}
let(:path) { working_copy }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["test"]
end

View File

@ -0,0 +1,18 @@
require_relative "shared_examples"
describe UnpackStrategy::Tar do
let(:path) { TEST_FIXTURE_DIR/"cask/container.tar.gz" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["container"]
context "when TAR archive is corrupted" do
let(:path) {
(mktmpdir/"test.tar").tap do |path|
FileUtils.touch path
end
}
include_examples "UnpackStrategy::detect"
end
end

View File

@ -0,0 +1,11 @@
require_relative "shared_examples"
describe UnpackStrategy::Uncompressed do
let(:path) {
(mktmpdir/"test").tap do |path|
FileUtils.touch path
end
}
include_examples "UnpackStrategy::detect"
end

View File

@ -0,0 +1,8 @@
require_relative "shared_examples"
describe UnpackStrategy::Xar, :needs_macos do
let(:path) { TEST_FIXTURE_DIR/"cask/container.xar" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["container"]
end

View File

@ -0,0 +1,7 @@
require_relative "shared_examples"
describe UnpackStrategy::Xz do
let(:path) { TEST_FIXTURE_DIR/"cask/container.xz" }
include_examples "UnpackStrategy::detect"
end

View File

@ -0,0 +1,18 @@
require_relative "shared_examples"
describe UnpackStrategy::Zip do
let(:path) { TEST_FIXTURE_DIR/"cask/MyFancyApp.zip" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["MyFancyApp"]
context "when ZIP archive is corrupted" do
let(:path) {
(mktmpdir/"test.zip").tap do |path|
FileUtils.touch path
end
}
include_examples "UnpackStrategy::detect"
end
end

View File

@ -1,20 +1,3 @@
require "unpack_strategy"
RSpec.shared_examples "UnpackStrategy::detect" do
it "is correctly detected" do
expect(UnpackStrategy.detect(path)).to be_a described_class
end
end
RSpec.shared_examples "#extract" do |children: []|
specify "#extract" do
mktmpdir do |unpack_dir|
described_class.new(path).extract(to: unpack_dir)
expect(unpack_dir.children(false).map(&:to_s)).to match_array children
end
end
end
describe UnpackStrategy do describe UnpackStrategy do
describe "#extract_nestedly" do describe "#extract_nestedly" do
subject(:strategy) { described_class.detect(path) } subject(:strategy) { described_class.detect(path) }
@ -75,210 +58,3 @@ describe UnpackStrategy do
end end
end end
end end
describe DirectoryUnpackStrategy do
let(:path) {
mktmpdir.tap do |path|
FileUtils.touch path/"file"
FileUtils.ln_s "file", path/"symlink"
end
}
subject(:strategy) { described_class.new(path) }
let(:unpack_dir) { mktmpdir }
it "does not follow symlinks" do
strategy.extract(to: unpack_dir)
expect(unpack_dir/"symlink").to be_a_symlink
end
it "preserves permissions of contained files" do
FileUtils.chmod 0644, path/"file"
strategy.extract(to: unpack_dir)
expect((unpack_dir/"file").stat.mode & 0777).to eq 0644
end
it "preserves the permissions of the destination directory" do
FileUtils.chmod 0700, path
FileUtils.chmod 0755, unpack_dir
strategy.extract(to: unpack_dir)
expect(unpack_dir.stat.mode & 0777).to eq 0755
end
end
describe UncompressedUnpackStrategy do
let(:path) {
(mktmpdir/"test").tap do |path|
FileUtils.touch path
end
}
include_examples "UnpackStrategy::detect"
end
describe P7ZipUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"cask/container.7z" }
include_examples "UnpackStrategy::detect"
end
describe XarUnpackStrategy, :needs_macos do
let(:path) { TEST_FIXTURE_DIR/"cask/container.xar" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["container"]
end
describe XzUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"cask/container.xz" }
include_examples "UnpackStrategy::detect"
end
describe RarUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"cask/container.rar" }
include_examples "UnpackStrategy::detect"
end
describe LzipUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"test.lz" }
include_examples "UnpackStrategy::detect"
end
describe LhaUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"test.lha" }
include_examples "UnpackStrategy::detect"
end
describe JarUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"test.jar" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["test.jar"]
end
describe ZipUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"cask/MyFancyApp.zip" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["MyFancyApp"]
context "when ZIP archive is corrupted" do
let(:path) {
(mktmpdir/"test.zip").tap do |path|
FileUtils.touch path
end
}
include_examples "UnpackStrategy::detect"
end
end
describe GzipUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"cask/container.gz" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["container"]
end
describe Bzip2UnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"cask/container.bz2" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["container"]
end
describe TarUnpackStrategy do
let(:path) { TEST_FIXTURE_DIR/"cask/container.tar.gz" }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["container"]
context "when TAR archive is corrupted" do
let(:path) {
(mktmpdir/"test.tar").tap do |path|
FileUtils.touch path
end
}
include_examples "UnpackStrategy::detect"
end
end
describe GitUnpackStrategy do
let(:repo) {
mktmpdir.tap do |repo|
system "git", "-C", repo, "init"
FileUtils.touch repo/"test"
system "git", "-C", repo, "add", "test"
system "git", "-C", repo, "commit", "-m", "Add `test` file."
end
}
let(:path) { repo }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: [".git", "test"]
end
describe SubversionUnpackStrategy do
let(:repo) {
mktmpdir.tap do |repo|
system "svnadmin", "create", repo
end
}
let(:working_copy) {
mktmpdir.tap do |working_copy|
system "svn", "checkout", "file://#{repo}", working_copy
FileUtils.touch working_copy/"test"
system "svn", "add", working_copy/"test"
system "svn", "commit", working_copy, "-m", "Add `test` file."
end
}
let(:path) { working_copy }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["test"]
end
describe CvsUnpackStrategy do
let(:repo) {
mktmpdir.tap do |repo|
FileUtils.touch repo/"test"
(repo/"CVS").mkpath
end
}
let(:path) { repo }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["CVS", "test"]
end
describe BazaarUnpackStrategy do
let(:repo) {
mktmpdir.tap do |repo|
FileUtils.touch repo/"test"
(repo/".bzr").mkpath
end
}
let(:path) { repo }
include_examples "UnpackStrategy::detect"
include_examples "#extract", children: ["test"]
end
describe MercurialUnpackStrategy do
let(:repo) {
mktmpdir.tap do |repo|
(repo/".hg").mkpath
end
}
let(:path) { repo }
include_examples "UnpackStrategy::detect"
end

View File

@ -1,30 +1,30 @@
class UnpackStrategy module UnpackStrategy
# length of the longest regex (currently TarUnpackStrategy) # length of the longest regex (currently Tar)
MAX_MAGIC_NUMBER_LENGTH = 262 MAX_MAGIC_NUMBER_LENGTH = 262
private_constant :MAX_MAGIC_NUMBER_LENGTH private_constant :MAX_MAGIC_NUMBER_LENGTH
def self.strategies def self.strategies
@strategies ||= [ @strategies ||= [
JarUnpackStrategy, Jar,
LuaRockUnpackStrategy, LuaRock,
MicrosoftOfficeXmlUnpackStrategy, MicrosoftOfficeXml,
ZipUnpackStrategy, Zip,
XarUnpackStrategy, Xar,
CompressUnpackStrategy, Compress,
TarUnpackStrategy, Tar,
GzipUnpackStrategy, Gzip,
Bzip2UnpackStrategy, Bzip2,
XzUnpackStrategy, Xz,
LzipUnpackStrategy, Lzip,
GitUnpackStrategy, Git,
MercurialUnpackStrategy, Mercurial,
SubversionUnpackStrategy, Subversion,
CvsUnpackStrategy, Cvs,
FossilUnpackStrategy, Fossil,
BazaarUnpackStrategy, Bazaar,
P7ZipUnpackStrategy, P7Zip,
RarUnpackStrategy, Rar,
LhaUnpackStrategy, Lha,
].freeze ].freeze
end end
private_class_method :strategies private_class_method :strategies
@ -43,11 +43,11 @@ class UnpackStrategy
# This is so that bad files produce good error messages. # This is so that bad files produce good error messages.
strategy ||= case path.extname strategy ||= case path.extname
when ".tar", ".tar.gz", ".tgz", ".tar.bz2", ".tbz", ".tar.xz", ".txz" when ".tar", ".tar.gz", ".tgz", ".tar.bz2", ".tbz", ".tar.xz", ".txz"
TarUnpackStrategy Tar
when ".zip" when ".zip"
ZipUnpackStrategy Zip
else else
UncompressedUnpackStrategy Uncompressed
end end
strategy.new(path, ref_type: ref_type, ref: ref) strategy.new(path, ref_type: ref_type, ref: ref)
@ -77,301 +77,36 @@ class UnpackStrategy
children = tmp_unpack_dir.children children = tmp_unpack_dir.children
if children.count == 1 && !children.first.directory? if children.count == 1 && !children.first.directory?
s = self.class.detect(children.first) s = UnpackStrategy.detect(children.first)
s.extract_nestedly(to: to, verbose: verbose) s.extract_nestedly(to: to, verbose: verbose)
next next
end end
DirectoryUnpackStrategy.new(tmp_unpack_dir).extract(to: to, verbose: verbose) Directory.new(tmp_unpack_dir).extract(to: to, verbose: verbose)
end end
end end
end end
class DirectoryUnpackStrategy < UnpackStrategy require "unpack_strategy/bazaar"
def self.can_extract?(path:, magic_number:) require "unpack_strategy/bzip2"
path.directory? require "unpack_strategy/compress"
end require "unpack_strategy/cvs"
require "unpack_strategy/directory"
private require "unpack_strategy/fossil"
require "unpack_strategy/git"
def extract_to_dir(unpack_dir, basename:, verbose:) require "unpack_strategy/gzip"
path.children.each do |child| require "unpack_strategy/jar"
FileUtils.copy_entry child, unpack_dir/child.basename, true, false require "unpack_strategy/lha"
end require "unpack_strategy/lua_rock"
end require "unpack_strategy/lzip"
end require "unpack_strategy/mercurial"
require "unpack_strategy/microsoft_office_xml"
class UncompressedUnpackStrategy < UnpackStrategy require "unpack_strategy/p7zip"
alias extract_nestedly extract require "unpack_strategy/rar"
require "unpack_strategy/subversion"
private require "unpack_strategy/tar"
require "unpack_strategy/uncompressed"
def extract_to_dir(unpack_dir, basename:, verbose:) require "unpack_strategy/xar"
FileUtils.cp path, unpack_dir/basename, preserve: true, verbose: verbose require "unpack_strategy/xz"
end require "unpack_strategy/zip"
end
class MicrosoftOfficeXmlUnpackStrategy < UncompressedUnpackStrategy
def self.can_extract?(path:, magic_number:)
return false unless ZipUnpackStrategy.can_extract?(path: path, magic_number: magic_number)
# Check further if the ZIP is a Microsoft Office XML document.
magic_number.match?(/\APK\003\004/n) &&
magic_number.match?(%r{\A.{30}(\[Content_Types\]\.xml|_rels/\.rels)}n)
end
end
class LuaRockUnpackStrategy < UncompressedUnpackStrategy
def self.can_extract?(path:, magic_number:)
return false unless ZipUnpackStrategy.can_extract?(path: path, magic_number: magic_number)
# Check further if the ZIP is a LuaRocks package.
out, = Open3.capture3("zipinfo", "-1", path)
out.encode(Encoding::UTF_8, invalid: :replace)
.split("\n")
.any? { |line| line.match?(%r{\A[^/]+.rockspec\Z}) }
end
end
class JarUnpackStrategy < UncompressedUnpackStrategy
def self.can_extract?(path:, magic_number:)
return false unless ZipUnpackStrategy.can_extract?(path: path, magic_number: magic_number)
# Check further if the ZIP is a JAR/WAR.
out, = Open3.capture3("zipinfo", "-1", path)
out.encode(Encoding::UTF_8, invalid: :replace)
.split("\n")
.include?("META-INF/MANIFEST.MF")
end
end
class P7ZipUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A7z\xBC\xAF\x27\x1C/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system "7zr", "x", "-y", "-bd", "-bso0", path, "-o#{unpack_dir}"
end
end
class ZipUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\APK(\003\004|\005\006)/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
quiet_flags = verbose ? [] : ["-qq"]
safe_system "unzip", *quiet_flags, path, "-d", unpack_dir
end
end
class TarUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
return true if magic_number.match?(/\A.{257}ustar/n)
# Check if `tar` can list the contents, then it can also extract it.
IO.popen(["tar", "tf", path], err: File::NULL) do |stdout|
!stdout.read(1).nil?
end
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system "tar", "xf", path, "-C", unpack_dir
end
end
class CompressUnpackStrategy < TarUnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A\037\235/n)
end
end
class XzUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A\xFD7zXZ\x00/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
safe_system Formula["xz"].opt_bin/"unxz", *quiet_flags, "-T0", unpack_dir/basename
extract_nested_tar(unpack_dir)
end
def extract_nested_tar(unpack_dir)
return unless DependencyCollector.tar_needs_xz_dependency?
return if (children = unpack_dir.children).count != 1
return if (tar = children.first).extname != ".tar"
Dir.mktmpdir do |tmpdir|
tmpdir = Pathname(tmpdir)
FileUtils.mv tar, tmpdir/tar.basename
TarUnpackStrategy.new(tmpdir/tar.basename).extract(to: unpack_dir)
end
end
end
class Bzip2UnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\ABZh/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
safe_system "bunzip2", *quiet_flags, unpack_dir/basename
end
end
class GzipUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A\037\213/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
safe_system "gunzip", *quiet_flags, "-N", unpack_dir/basename
end
end
class LzipUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\ALZIP/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
safe_system Formula["lzip"].opt_bin/"lzip", "-d", *quiet_flags, unpack_dir/basename
end
end
class XarUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\Axar!/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system "xar", "-x", "-f", path, "-C", unpack_dir
end
end
class RarUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\ARar!/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system Formula["unrar"].opt_bin/"unrar", "x", "-inul", path, unpack_dir
end
end
class LhaUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A..-(lh0|lh1|lz4|lz5|lzs|lh\\40|lhd|lh2|lh3|lh4|lh5)-/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system Formula["lha"].opt_bin/"lha", "xq2w=#{unpack_dir}", path
end
end
class GitUnpackStrategy < DirectoryUnpackStrategy
def self.can_extract?(path:, magic_number:)
super && (path/".git").directory?
end
end
class SubversionUnpackStrategy < DirectoryUnpackStrategy
def self.can_extract?(path:, magic_number:)
super && (path/".svn").directory?
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system "svn", "export", "--force", path, unpack_dir
end
end
class CvsUnpackStrategy < DirectoryUnpackStrategy
def self.can_extract?(path:, magic_number:)
super && (path/"CVS").directory?
end
end
class MercurialUnpackStrategy < DirectoryUnpackStrategy
def self.can_extract?(path:, magic_number:)
super && (path/".hg").directory?
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
with_env "PATH" => PATH.new(Formula["mercurial"].opt_bin, ENV["PATH"]) do
safe_system "hg", "--cwd", path, "archive", "--subrepos", "-y", "-t", "files", unpack_dir
end
end
end
class FossilUnpackStrategy < UnpackStrategy
def self.can_extract?(path:, magic_number:)
return false unless magic_number.match?(/\ASQLite format 3\000/n)
# Fossil database is made up of artifacts, so the `artifact` table must exist.
query = "select count(*) from sqlite_master where type = 'view' and name = 'artifact'"
Utils.popen_read("sqlite3", path, query).to_i == 1
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
args = if @ref_type && @ref
[@ref]
else
[]
end
with_env "PATH" => PATH.new(Formula["fossil"].opt_bin, ENV["PATH"]) do
safe_system "fossil", "open", path, *args, chdir: unpack_dir
end
end
end
class BazaarUnpackStrategy < DirectoryUnpackStrategy
def self.can_extract?(path:, magic_number:)
super && (path/".bzr").directory?
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
super
# The export command doesn't work on checkouts (see https://bugs.launchpad.net/bzr/+bug/897511).
FileUtils.rm_r unpack_dir/".bzr"
end
end

View File

@ -0,0 +1,18 @@
require_relative "directory"
module UnpackStrategy
class Bazaar < Directory
def self.can_extract?(path:, magic_number:)
super && (path/".bzr").directory?
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
super
# The export command doesn't work on checkouts (see https://bugs.launchpad.net/bzr/+bug/897511).
FileUtils.rm_r unpack_dir/".bzr"
end
end
end

View File

@ -0,0 +1,17 @@
module UnpackStrategy
class Bzip2
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\ABZh/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
safe_system "bunzip2", *quiet_flags, unpack_dir/basename
end
end
end

View File

@ -0,0 +1,9 @@
require_relative "tar"
module UnpackStrategy
class Compress < Tar
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A\037\235/n)
end
end
end

View File

@ -0,0 +1,9 @@
require_relative "directory"
module UnpackStrategy
class Cvs < Directory
def self.can_extract?(path:, magic_number:)
super && (path/"CVS").directory?
end
end
end

View File

@ -0,0 +1,17 @@
module UnpackStrategy
class Directory
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
path.directory?
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
path.children.each do |child|
FileUtils.copy_entry child, unpack_dir/child.basename, true, false
end
end
end
end

View File

@ -0,0 +1,27 @@
module UnpackStrategy
class Fossil
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
return false unless magic_number.match?(/\ASQLite format 3\000/n)
# Fossil database is made up of artifacts, so the `artifact` table must exist.
query = "select count(*) from sqlite_master where type = 'view' and name = 'artifact'"
Utils.popen_read("sqlite3", path, query).to_i == 1
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
args = if @ref_type && @ref
[@ref]
else
[]
end
with_env "PATH" => PATH.new(Formula["fossil"].opt_bin, ENV["PATH"]) do
safe_system "fossil", "open", path, *args, chdir: unpack_dir
end
end
end
end

View File

@ -0,0 +1,9 @@
require_relative "directory"
module UnpackStrategy
class Git < Directory
def self.can_extract?(path:, magic_number:)
super && (path/".git").directory?
end
end
end

View File

@ -0,0 +1,17 @@
module UnpackStrategy
class Gzip
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A\037\213/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
safe_system "gunzip", *quiet_flags, "-N", unpack_dir/basename
end
end
end

View File

@ -0,0 +1,15 @@
require_relative "uncompressed"
module UnpackStrategy
class Jar < Uncompressed
def self.can_extract?(path:, magic_number:)
return false unless Zip.can_extract?(path: path, magic_number: magic_number)
# Check further if the ZIP is a JAR/WAR.
out, = Open3.capture3("zipinfo", "-1", path)
out.encode(Encoding::UTF_8, invalid: :replace)
.split("\n")
.include?("META-INF/MANIFEST.MF")
end
end
end

View File

@ -0,0 +1,19 @@
module UnpackStrategy
class Lha
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A..-(lh0|lh1|lz4|lz5|lzs|lh\\40|lhd|lh2|lh3|lh4|lh5)-/n)
end
def dependencies
@dependencies ||= [Formula["lha"]]
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system Formula["lha"].opt_bin/"lha", "xq2w=#{unpack_dir}", path
end
end
end

View File

@ -0,0 +1,15 @@
require_relative "uncompressed"
module UnpackStrategy
class LuaRock < Uncompressed
def self.can_extract?(path:, magic_number:)
return false unless Zip.can_extract?(path: path, magic_number: magic_number)
# Check further if the ZIP is a LuaRocks package.
out, = Open3.capture3("zipinfo", "-1", path)
out.encode(Encoding::UTF_8, invalid: :replace)
.split("\n")
.any? { |line| line.match?(%r{\A[^/]+.rockspec\Z}) }
end
end
end

View File

@ -0,0 +1,21 @@
module UnpackStrategy
class Lzip
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\ALZIP/n)
end
def dependencies
@dependencies ||= [Formula["lzip"]]
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
safe_system Formula["lzip"].opt_bin/"lzip", "-d", *quiet_flags, unpack_dir/basename
end
end
end

View File

@ -0,0 +1,17 @@
require_relative "directory"
module UnpackStrategy
class Mercurial < Directory
def self.can_extract?(path:, magic_number:)
super && (path/".hg").directory?
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
with_env "PATH" => PATH.new(Formula["mercurial"].opt_bin, ENV["PATH"]) do
safe_system "hg", "--cwd", path, "archive", "--subrepos", "-y", "-t", "files", unpack_dir
end
end
end
end

View File

@ -0,0 +1,13 @@
require_relative "uncompressed"
module UnpackStrategy
class MicrosoftOfficeXml < Uncompressed
def self.can_extract?(path:, magic_number:)
return false unless Zip.can_extract?(path: path, magic_number: magic_number)
# Check further if the ZIP is a Microsoft Office XML document.
magic_number.match?(/\APK\003\004/n) &&
magic_number.match?(%r{\A.{30}(\[Content_Types\]\.xml|_rels/\.rels)}n)
end
end
end

View File

@ -0,0 +1,19 @@
module UnpackStrategy
class P7Zip
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A7z\xBC\xAF\x27\x1C/n)
end
def dependencies
@dependencies ||= [Formula["p7zip"]]
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system "7zr", "x", "-y", "-bd", "-bso0", path, "-o#{unpack_dir}"
end
end
end

View File

@ -0,0 +1,19 @@
module UnpackStrategy
class Rar
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\ARar!/n)
end
def dependencies
@dependencies ||= [Formula["unrar"]]
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system Formula["unrar"].opt_bin/"unrar", "x", "-inul", path, unpack_dir
end
end
end

View File

@ -0,0 +1,15 @@
require_relative "directory"
module UnpackStrategy
class Subversion < Directory
def self.can_extract?(path:, magic_number:)
super && (path/".svn").directory?
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system "svn", "export", "--force", path, unpack_dir
end
end
end

View File

@ -0,0 +1,20 @@
module UnpackStrategy
class Tar
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
return true if magic_number.match?(/\A.{257}ustar/n)
# Check if `tar` can list the contents, then it can also extract it.
IO.popen(["tar", "tf", path], err: File::NULL) do |stdout|
!stdout.read(1).nil?
end
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system "tar", "xf", path, "-C", unpack_dir
end
end
end

View File

@ -0,0 +1,13 @@
module UnpackStrategy
class Uncompressed
include UnpackStrategy
alias extract_nestedly extract
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true, verbose: verbose
end
end
end

View File

@ -0,0 +1,15 @@
module UnpackStrategy
class Xar
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\Axar!/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
safe_system "xar", "-x", "-f", path, "-C", unpack_dir
end
end
end

View File

@ -0,0 +1,34 @@
module UnpackStrategy
class Xz
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\A\xFD7zXZ\x00/n)
end
def dependencies
@dependencies ||= [Formula["xz"]]
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
safe_system Formula["xz"].opt_bin/"unxz", *quiet_flags, "-T0", unpack_dir/basename
extract_nested_tar(unpack_dir)
end
def extract_nested_tar(unpack_dir)
return unless DependencyCollector.tar_needs_xz_dependency?
return if (children = unpack_dir.children).count != 1
return if (tar = children.first).extname != ".tar"
Dir.mktmpdir do |tmpdir|
tmpdir = Pathname(tmpdir)
FileUtils.mv tar, tmpdir/tar.basename
Tar.new(tmpdir/tar.basename).extract(to: unpack_dir)
end
end
end
end

View File

@ -0,0 +1,16 @@
module UnpackStrategy
class Zip
include UnpackStrategy
def self.can_extract?(path:, magic_number:)
magic_number.match?(/\APK(\003\004|\005\006)/n)
end
private
def extract_to_dir(unpack_dir, basename:, verbose:)
quiet_flags = verbose ? [] : ["-qq"]
safe_system "unzip", *quiet_flags, path, "-d", unpack_dir
end
end
end