1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require 'rubygems'
require 'fileutils'
require 'yaml'
require 'zlib'
##
# The format class knows the guts of the RubyGem .gem file format and provides
# the capability to read gem files
class Gem::OldFormat
attr_accessor :spec, :file_entries, :gem_path
##
# Constructs an instance of a Format object, representing the gem's data
# structure.
#
# gem:: [String] The file name of the gem
def initialize(gem_path)
@gem_path = gem_path
end
##
# Reads the named gem file and returns a Format object, representing the
# data from the gem file
#
# file_path:: [String] Path to the gem file
def self.from_file_by_path(file_path)
unless File.exist?(file_path)
raise Gem::Exception, "Cannot load gem file [#{file_path}]"
end
File.open(file_path, 'rb') do |file|
from_io(file, file_path)
end
end
##
# Reads a gem from an io stream and returns a Format object, representing
# the data from the gem file
#
# io:: [IO] Stream from which to read the gem
def self.from_io(io, gem_path="(io)")
format = self.new(gem_path)
skip_ruby(io)
format.spec = read_spec(io)
format.file_entries = []
read_files_from_gem(io) do |entry, file_data|
format.file_entries << [entry, file_data]
end
format
end
private
##
# Skips the Ruby self-install header. After calling this method, the
# IO index will be set after the Ruby code.
#
# file:: [IO] The IO to process (skip the Ruby code)
def self.skip_ruby(file)
end_seen = false
loop {
line = file.gets
if(line == nil || line.chomp == "__END__") then
end_seen = true
break
end
}
if end_seen == false then
raise Gem::Exception.new("Failed to find end of ruby script while reading gem")
end
end
##
# Reads the specification YAML from the supplied IO and constructs
# a Gem::Specification from it. After calling this method, the
# IO index will be set after the specification header.
#
# file:: [IO] The IO to process
def self.read_spec(file)
yaml = ''
read_until_dashes file do |line|
yaml << line
end
Gem::Specification.from_yaml yaml
rescue YAML::Error => e
raise Gem::Exception, "Failed to parse gem specification out of gem file"
rescue ArgumentError => e
raise Gem::Exception, "Failed to parse gem specification out of gem file"
end
##
# Reads lines from the supplied IO until a end-of-yaml (---) is
# reached
#
# file:: [IO] The IO to process
# block:: [String] The read line
def self.read_until_dashes(file)
while((line = file.gets) && line.chomp.strip != "---") do
yield line
end
end
##
# Reads the embedded file data from a gem file, yielding an entry
# containing metadata about the file and the file contents themselves
# for each file that's archived in the gem.
# NOTE: Many of these methods should be extracted into some kind of
# Gem file read/writer
#
# gem_file:: [IO] The IO to process
def self.read_files_from_gem(gem_file)
errstr = "Error reading files from gem"
header_yaml = ''
begin
self.read_until_dashes(gem_file) do |line|
header_yaml << line
end
header = YAML.load(header_yaml)
raise Gem::Exception, errstr unless header
header.each do |entry|
file_data = ''
self.read_until_dashes(gem_file) do |line|
file_data << line
end
yield [entry, Zlib::Inflate.inflate(file_data.strip.unpack("m")[0])]
end
rescue Zlib::DataError => e
raise Gem::Exception, errstr
end
end
end
|