Only call hdiutil if it’s a bzip2 or zlib.
This commit is contained in:
parent
278eace2fa
commit
e8f7a88976
@ -1,7 +1,31 @@
|
||||
module UnpackStrategy
|
||||
# length of the longest regex (currently Tar)
|
||||
MAX_MAGIC_NUMBER_LENGTH = 262
|
||||
private_constant :MAX_MAGIC_NUMBER_LENGTH
|
||||
module Magic
|
||||
# length of the longest regex (currently Tar)
|
||||
MAX_MAGIC_NUMBER_LENGTH = 262
|
||||
|
||||
refine Pathname do
|
||||
def magic_number
|
||||
@magic_number ||= if directory?
|
||||
""
|
||||
else
|
||||
binread(MAX_MAGIC_NUMBER_LENGTH) || ""
|
||||
end
|
||||
end
|
||||
|
||||
def file_type
|
||||
@file_type ||= system_command("file", args: ["-b", self], print_stderr: false)
|
||||
.stdout.chomp
|
||||
end
|
||||
|
||||
def zipinfo
|
||||
@zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false)
|
||||
.stdout
|
||||
.encode(Encoding::UTF_8, invalid: :replace)
|
||||
.split("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
private_constant :Magic
|
||||
|
||||
def self.strategies
|
||||
@strategies ||= [
|
||||
@ -18,7 +42,6 @@ module UnpackStrategy
|
||||
Xz,
|
||||
Lzip,
|
||||
Executable,
|
||||
Diff,
|
||||
Git,
|
||||
Mercurial,
|
||||
Subversion,
|
||||
@ -54,14 +77,8 @@ module UnpackStrategy
|
||||
end
|
||||
|
||||
def self.from_path(path)
|
||||
magic_number = if path.directory?
|
||||
""
|
||||
else
|
||||
File.binread(path, MAX_MAGIC_NUMBER_LENGTH) || ""
|
||||
end
|
||||
|
||||
strategy = strategies.detect do |s|
|
||||
s.can_extract?(path: path, magic_number: magic_number)
|
||||
s.can_extract?(path)
|
||||
end
|
||||
|
||||
# This is so that bad files produce good error messages.
|
||||
@ -127,7 +144,6 @@ require "unpack_strategy/bzip2"
|
||||
require "unpack_strategy/cab"
|
||||
require "unpack_strategy/compress"
|
||||
require "unpack_strategy/cvs"
|
||||
require "unpack_strategy/diff"
|
||||
require "unpack_strategy/directory"
|
||||
require "unpack_strategy/dmg"
|
||||
require "unpack_strategy/executable"
|
||||
|
||||
@ -2,7 +2,9 @@ module UnpackStrategy
|
||||
class Air
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.extname == ".air"
|
||||
end
|
||||
|
||||
|
||||
@ -2,7 +2,9 @@ require_relative "directory"
|
||||
|
||||
module UnpackStrategy
|
||||
class Bazaar < Directory
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
super && (path/".bzr").directory?
|
||||
end
|
||||
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Bzip2
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\ABZh/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\ABZh/n)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Cab
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\AMSCF/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\AMSCF/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
|
||||
@ -2,8 +2,10 @@ require_relative "tar"
|
||||
|
||||
module UnpackStrategy
|
||||
class Compress < Tar
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A\037\235/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\A\037\235/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,7 +2,9 @@ require_relative "directory"
|
||||
|
||||
module UnpackStrategy
|
||||
class Cvs < Directory
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
super && (path/"CVS").directory?
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
require_relative "uncompressed"
|
||||
|
||||
module UnpackStrategy
|
||||
class Diff < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A---\040/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -2,7 +2,9 @@ module UnpackStrategy
|
||||
class Directory
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.directory?
|
||||
end
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ module UnpackStrategy
|
||||
class Dmg
|
||||
include UnpackStrategy
|
||||
|
||||
using Magic
|
||||
|
||||
module Bom
|
||||
DMG_METADATA = Set.new %w[
|
||||
.background
|
||||
@ -83,12 +85,21 @@ module UnpackStrategy
|
||||
end
|
||||
|
||||
system_command! "ditto", args: ["--bom", bomfile.path, "--", path, unpack_dir]
|
||||
|
||||
FileUtils.chmod "u+w", Pathname.glob(unpack_dir/"**/*").reject(&:symlink?)
|
||||
end
|
||||
end
|
||||
end
|
||||
private_constant :Mount
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
def self.can_extract?(path)
|
||||
bzip2 = Bzip2.can_extract?(path)
|
||||
|
||||
zlib = path.magic_number.match?(/\A(\x78|\x08|\x18|\x28|\x38|\x48|\x58|\x68)/n) &&
|
||||
(path.magic_number[0...2].unpack("S>").first % 31).zero?
|
||||
|
||||
return false unless bzip2 || zlib
|
||||
|
||||
imageinfo = system_command("hdiutil",
|
||||
args: ["imageinfo", path],
|
||||
print_stderr: false).stdout
|
||||
|
||||
@ -2,8 +2,10 @@ require_relative "uncompressed"
|
||||
|
||||
module UnpackStrategy
|
||||
class Executable < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A#!\s*\S+/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\A#!\s*\S+/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Fossil
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
return false unless magic_number.match?(/\ASQLite format 3\000/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
return false unless path.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'"
|
||||
|
||||
@ -2,7 +2,9 @@ module UnpackStrategy
|
||||
class GenericUnar
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(_path)
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
@ -2,7 +2,9 @@ require_relative "directory"
|
||||
|
||||
module UnpackStrategy
|
||||
class Git < Directory
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
super && (path/".git").directory?
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Gzip
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A\037\213/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\A\037\213/n)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@ -2,14 +2,13 @@ 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)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
return false unless Zip.can_extract?(path)
|
||||
|
||||
# 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")
|
||||
path.zipinfo.include?("META-INF/MANIFEST.MF")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ 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)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\A..-(lh0|lh1|lz4|lz5|lzs|lh\\40|lhd|lh2|lh3|lh4|lh5)-/n)
|
||||
end
|
||||
|
||||
def dependencies
|
||||
|
||||
@ -2,14 +2,13 @@ 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)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
return false unless Zip.can_extract?(path)
|
||||
|
||||
# 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}) }
|
||||
path.zipinfo.grep(%r{\A[^/]+.rockspec\Z}).any?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Lzip
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\ALZIP/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\ALZIP/n)
|
||||
end
|
||||
|
||||
def dependencies
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Lzma
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A\]\000\000\200\000/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\A\]\000\000\200\000/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
|
||||
@ -2,7 +2,9 @@ require_relative "directory"
|
||||
|
||||
module UnpackStrategy
|
||||
class Mercurial < Directory
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
super && (path/".hg").directory?
|
||||
end
|
||||
|
||||
|
||||
@ -2,12 +2,14 @@ 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)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
return false unless Zip.can_extract?(path)
|
||||
|
||||
# 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)
|
||||
path.magic_number.match?(/\APK\003\004/n) &&
|
||||
path.magic_number.match?(%r{\A.{30}(\[Content_Types\]\.xml|_rels/\.rels)}n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ require_relative "uncompressed"
|
||||
|
||||
module UnpackStrategy
|
||||
class Otf < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\AOTTO/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\AOTTO/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class P7Zip
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A7z\xBC\xAF\x27\x1C/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\A7z\xBC\xAF\x27\x1C/n)
|
||||
end
|
||||
|
||||
def dependencies
|
||||
|
||||
@ -2,9 +2,11 @@ require_relative "uncompressed"
|
||||
|
||||
module UnpackStrategy
|
||||
class Pkg < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.extname.match?(/\A.m?pkg\Z/) &&
|
||||
(path.directory? || magic_number.match?(/\Axar!/n))
|
||||
(path.directory? || path.magic_number.match?(/\Axar!/n))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Rar
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\ARar!/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\ARar!/n)
|
||||
end
|
||||
|
||||
def dependencies
|
||||
|
||||
@ -2,12 +2,12 @@ require_relative "generic_unar"
|
||||
|
||||
module UnpackStrategy
|
||||
class SelfExtractingExecutable < GenericUnar
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
return false unless magic_number.match?(/\AMZ/n)
|
||||
using Magic
|
||||
|
||||
system_command("file",
|
||||
args: [path],
|
||||
print_stderr: false).stdout.include?("self-extracting")
|
||||
def self.can_extract?(path)
|
||||
return false unless path.magic_number.match?(/\AMZ/n)
|
||||
|
||||
path.file_type.include?("self-extracting archive")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ require_relative "generic_unar"
|
||||
|
||||
module UnpackStrategy
|
||||
class Sit < GenericUnar
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\AStuffIt/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\AStuffIt/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,7 +2,9 @@ require_relative "directory"
|
||||
|
||||
module UnpackStrategy
|
||||
class Subversion < Directory
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
super && (path/".svn").directory?
|
||||
end
|
||||
|
||||
|
||||
@ -2,8 +2,14 @@ module UnpackStrategy
|
||||
class Tar
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
return true if magic_number.match?(/\A.{257}ustar/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
return true if path.magic_number.match?(/\A.{257}ustar/n)
|
||||
|
||||
unless [Bzip2, Gzip, Lzip, Xz].any? { |s| s.can_extract?(path) }
|
||||
return false
|
||||
end
|
||||
|
||||
# Check if `tar` can list the contents, then it can also extract it.
|
||||
IO.popen(["tar", "tf", path], err: File::NULL) do |stdout|
|
||||
|
||||
@ -2,11 +2,13 @@ require_relative "uncompressed"
|
||||
|
||||
module UnpackStrategy
|
||||
class Ttf < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
# TrueType Font
|
||||
magic_number.match?(/\A\000\001\000\000\000/n) ||
|
||||
path.magic_number.match?(/\A\000\001\000\000\000/n) ||
|
||||
# Truetype Font Collection
|
||||
magic_number.match?(/\Attcf/n)
|
||||
path.magic_number.match?(/\Attcf/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Xar
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\Axar!/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\Axar!/n)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Xz
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A\xFD7zXZ\x00/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\A\xFD7zXZ\x00/n)
|
||||
end
|
||||
|
||||
def dependencies
|
||||
|
||||
@ -2,8 +2,10 @@ module UnpackStrategy
|
||||
class Zip
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\APK(\003\004|\005\006)/n)
|
||||
using Magic
|
||||
|
||||
def self.can_extract?(path)
|
||||
path.magic_number.match?(/\APK(\003\004|\005\006)/n)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user