diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index 301ce2ec6f..c63210bc98 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -329,8 +329,8 @@ end # Useful for installing jars. class NoUnzipCurlDownloadStrategy < CurlDownloadStrategy def stage - UncompressedUnpackStrategy.new(cached_location) - .extract(basename: basename_without_params) + UnpackStrategy::Uncompressed.new(cached_location) + .extract(basename: basename_without_params) end end diff --git a/Library/Homebrew/test/unpack_strategy/bazaar_spec.rb b/Library/Homebrew/test/unpack_strategy/bazaar_spec.rb new file mode 100644 index 0000000000..dac93a05e9 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/bazaar_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/bzip2_spec.rb b/Library/Homebrew/test/unpack_strategy/bzip2_spec.rb new file mode 100644 index 0000000000..5bb2b5dc83 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/bzip2_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/cvs_spec.rb b/Library/Homebrew/test/unpack_strategy/cvs_spec.rb new file mode 100644 index 0000000000..b3addd4cbf --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/cvs_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/directory_spec.rb b/Library/Homebrew/test/unpack_strategy/directory_spec.rb new file mode 100644 index 0000000000..0b0ba803c4 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/directory_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/git_spec.rb b/Library/Homebrew/test/unpack_strategy/git_spec.rb new file mode 100644 index 0000000000..a499c8a796 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/git_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/gzip_spec.rb b/Library/Homebrew/test/unpack_strategy/gzip_spec.rb new file mode 100644 index 0000000000..38d12c1737 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/gzip_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/jar_spec.rb b/Library/Homebrew/test/unpack_strategy/jar_spec.rb new file mode 100644 index 0000000000..a180ae82f1 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/jar_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/lha_spec.rb b/Library/Homebrew/test/unpack_strategy/lha_spec.rb new file mode 100644 index 0000000000..7e70844a85 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/lha_spec.rb @@ -0,0 +1,7 @@ +require_relative "shared_examples" + +describe UnpackStrategy::Lha do + let(:path) { TEST_FIXTURE_DIR/"test.lha" } + + include_examples "UnpackStrategy::detect" +end diff --git a/Library/Homebrew/test/unpack_strategy/lzip_spec.rb b/Library/Homebrew/test/unpack_strategy/lzip_spec.rb new file mode 100644 index 0000000000..119eb213b2 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/lzip_spec.rb @@ -0,0 +1,7 @@ +require_relative "shared_examples" + +describe UnpackStrategy::Lzip do + let(:path) { TEST_FIXTURE_DIR/"test.lz" } + + include_examples "UnpackStrategy::detect" +end diff --git a/Library/Homebrew/test/unpack_strategy/mercurial_spec.rb b/Library/Homebrew/test/unpack_strategy/mercurial_spec.rb new file mode 100644 index 0000000000..edd22a5bbc --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/mercurial_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/p7zip_spec.rb b/Library/Homebrew/test/unpack_strategy/p7zip_spec.rb new file mode 100644 index 0000000000..33a56923f2 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/p7zip_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/rar_spec.rb b/Library/Homebrew/test/unpack_strategy/rar_spec.rb new file mode 100644 index 0000000000..0f315b1746 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/rar_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/shared_examples.rb b/Library/Homebrew/test/unpack_strategy/shared_examples.rb new file mode 100644 index 0000000000..285de1c3e2 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/shared_examples.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/subversion_spec.rb b/Library/Homebrew/test/unpack_strategy/subversion_spec.rb new file mode 100644 index 0000000000..002899556d --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/subversion_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/tar_spec.rb b/Library/Homebrew/test/unpack_strategy/tar_spec.rb new file mode 100644 index 0000000000..0f748f5d63 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/tar_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/uncompressed_spec.rb b/Library/Homebrew/test/unpack_strategy/uncompressed_spec.rb new file mode 100644 index 0000000000..9d08257126 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/uncompressed_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/xar_spec.rb b/Library/Homebrew/test/unpack_strategy/xar_spec.rb new file mode 100644 index 0000000000..61257d3492 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/xar_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/xz_spec.rb b/Library/Homebrew/test/unpack_strategy/xz_spec.rb new file mode 100644 index 0000000000..6374833a73 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/xz_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy/zip_spec.rb b/Library/Homebrew/test/unpack_strategy/zip_spec.rb new file mode 100644 index 0000000000..091e1d4582 --- /dev/null +++ b/Library/Homebrew/test/unpack_strategy/zip_spec.rb @@ -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 diff --git a/Library/Homebrew/test/unpack_strategy_spec.rb b/Library/Homebrew/test/unpack_strategy_spec.rb index a2a3e5a3c1..62bbc29ea0 100644 --- a/Library/Homebrew/test/unpack_strategy_spec.rb +++ b/Library/Homebrew/test/unpack_strategy_spec.rb @@ -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 "#extract_nestedly" do subject(:strategy) { described_class.detect(path) } @@ -75,210 +58,3 @@ describe UnpackStrategy do 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 diff --git a/Library/Homebrew/unpack_strategy.rb b/Library/Homebrew/unpack_strategy.rb index 117a8105b1..b341292cdf 100644 --- a/Library/Homebrew/unpack_strategy.rb +++ b/Library/Homebrew/unpack_strategy.rb @@ -1,30 +1,30 @@ -class UnpackStrategy - # length of the longest regex (currently TarUnpackStrategy) +module UnpackStrategy + # length of the longest regex (currently Tar) MAX_MAGIC_NUMBER_LENGTH = 262 private_constant :MAX_MAGIC_NUMBER_LENGTH def self.strategies @strategies ||= [ - JarUnpackStrategy, - LuaRockUnpackStrategy, - MicrosoftOfficeXmlUnpackStrategy, - ZipUnpackStrategy, - XarUnpackStrategy, - CompressUnpackStrategy, - TarUnpackStrategy, - GzipUnpackStrategy, - Bzip2UnpackStrategy, - XzUnpackStrategy, - LzipUnpackStrategy, - GitUnpackStrategy, - MercurialUnpackStrategy, - SubversionUnpackStrategy, - CvsUnpackStrategy, - FossilUnpackStrategy, - BazaarUnpackStrategy, - P7ZipUnpackStrategy, - RarUnpackStrategy, - LhaUnpackStrategy, + Jar, + LuaRock, + MicrosoftOfficeXml, + Zip, + Xar, + Compress, + Tar, + Gzip, + Bzip2, + Xz, + Lzip, + Git, + Mercurial, + Subversion, + Cvs, + Fossil, + Bazaar, + P7Zip, + Rar, + Lha, ].freeze end private_class_method :strategies @@ -43,11 +43,11 @@ class UnpackStrategy # This is so that bad files produce good error messages. strategy ||= case path.extname when ".tar", ".tar.gz", ".tgz", ".tar.bz2", ".tbz", ".tar.xz", ".txz" - TarUnpackStrategy + Tar when ".zip" - ZipUnpackStrategy + Zip else - UncompressedUnpackStrategy + Uncompressed end strategy.new(path, ref_type: ref_type, ref: ref) @@ -77,301 +77,36 @@ class UnpackStrategy children = tmp_unpack_dir.children 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) next end - DirectoryUnpackStrategy.new(tmp_unpack_dir).extract(to: to, verbose: verbose) + Directory.new(tmp_unpack_dir).extract(to: to, verbose: verbose) end end end -class DirectoryUnpackStrategy < 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 - -class UncompressedUnpackStrategy < 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 - -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 +require "unpack_strategy/bazaar" +require "unpack_strategy/bzip2" +require "unpack_strategy/compress" +require "unpack_strategy/cvs" +require "unpack_strategy/directory" +require "unpack_strategy/fossil" +require "unpack_strategy/git" +require "unpack_strategy/gzip" +require "unpack_strategy/jar" +require "unpack_strategy/lha" +require "unpack_strategy/lua_rock" +require "unpack_strategy/lzip" +require "unpack_strategy/mercurial" +require "unpack_strategy/microsoft_office_xml" +require "unpack_strategy/p7zip" +require "unpack_strategy/rar" +require "unpack_strategy/subversion" +require "unpack_strategy/tar" +require "unpack_strategy/uncompressed" +require "unpack_strategy/xar" +require "unpack_strategy/xz" +require "unpack_strategy/zip" diff --git a/Library/Homebrew/unpack_strategy/bazaar.rb b/Library/Homebrew/unpack_strategy/bazaar.rb new file mode 100644 index 0000000000..4811d27279 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/bazaar.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/bzip2.rb b/Library/Homebrew/unpack_strategy/bzip2.rb new file mode 100644 index 0000000000..aa401368ce --- /dev/null +++ b/Library/Homebrew/unpack_strategy/bzip2.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/compress.rb b/Library/Homebrew/unpack_strategy/compress.rb new file mode 100644 index 0000000000..a1851ccf4d --- /dev/null +++ b/Library/Homebrew/unpack_strategy/compress.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/cvs.rb b/Library/Homebrew/unpack_strategy/cvs.rb new file mode 100644 index 0000000000..cca3010703 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/cvs.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/directory.rb b/Library/Homebrew/unpack_strategy/directory.rb new file mode 100644 index 0000000000..540369de75 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/directory.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/fossil.rb b/Library/Homebrew/unpack_strategy/fossil.rb new file mode 100644 index 0000000000..e72908c401 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/fossil.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/git.rb b/Library/Homebrew/unpack_strategy/git.rb new file mode 100644 index 0000000000..2f9f6668b1 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/git.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/gzip.rb b/Library/Homebrew/unpack_strategy/gzip.rb new file mode 100644 index 0000000000..e83feccfe3 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/gzip.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/jar.rb b/Library/Homebrew/unpack_strategy/jar.rb new file mode 100644 index 0000000000..35e2b211c0 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/jar.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/lha.rb b/Library/Homebrew/unpack_strategy/lha.rb new file mode 100644 index 0000000000..b5d7f8b16b --- /dev/null +++ b/Library/Homebrew/unpack_strategy/lha.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/lua_rock.rb b/Library/Homebrew/unpack_strategy/lua_rock.rb new file mode 100644 index 0000000000..d9a9c2e6ba --- /dev/null +++ b/Library/Homebrew/unpack_strategy/lua_rock.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/lzip.rb b/Library/Homebrew/unpack_strategy/lzip.rb new file mode 100644 index 0000000000..5454423269 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/lzip.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/mercurial.rb b/Library/Homebrew/unpack_strategy/mercurial.rb new file mode 100644 index 0000000000..8f71fdc95e --- /dev/null +++ b/Library/Homebrew/unpack_strategy/mercurial.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/microsoft_office_xml.rb b/Library/Homebrew/unpack_strategy/microsoft_office_xml.rb new file mode 100644 index 0000000000..6f3501816f --- /dev/null +++ b/Library/Homebrew/unpack_strategy/microsoft_office_xml.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/p7zip.rb b/Library/Homebrew/unpack_strategy/p7zip.rb new file mode 100644 index 0000000000..c299756601 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/p7zip.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/rar.rb b/Library/Homebrew/unpack_strategy/rar.rb new file mode 100644 index 0000000000..326caeffe0 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/rar.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/subversion.rb b/Library/Homebrew/unpack_strategy/subversion.rb new file mode 100644 index 0000000000..a1937183b1 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/subversion.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/tar.rb b/Library/Homebrew/unpack_strategy/tar.rb new file mode 100644 index 0000000000..a8865ec3e9 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/tar.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/uncompressed.rb b/Library/Homebrew/unpack_strategy/uncompressed.rb new file mode 100644 index 0000000000..b3facb172c --- /dev/null +++ b/Library/Homebrew/unpack_strategy/uncompressed.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/xar.rb b/Library/Homebrew/unpack_strategy/xar.rb new file mode 100644 index 0000000000..c78233f4f5 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/xar.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/xz.rb b/Library/Homebrew/unpack_strategy/xz.rb new file mode 100644 index 0000000000..a629de237b --- /dev/null +++ b/Library/Homebrew/unpack_strategy/xz.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/zip.rb b/Library/Homebrew/unpack_strategy/zip.rb new file mode 100644 index 0000000000..c5544a30c8 --- /dev/null +++ b/Library/Homebrew/unpack_strategy/zip.rb @@ -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