gem 0.0.1.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.md +75 -0
- data/bin/gem +7 -0
- data/lib/gem.rb +249 -0
- data/lib/gem/configuration.rb +43 -0
- data/lib/gem/dependency.rb +2 -0
- data/lib/gem/platform.rb +76 -0
- data/lib/gem/progressbar.rb +269 -0
- data/lib/gem/requirement.rb +9 -0
- data/lib/gem/source_index.rb +8 -0
- data/lib/gem/specification.rb +202 -0
- data/lib/gem/tar.rb +7 -0
- data/lib/gem/tar/entry.rb +145 -0
- data/lib/gem/tar/header.rb +266 -0
- data/lib/gem/tar/reader.rb +101 -0
- data/lib/gem/tar/writer.rb +240 -0
- data/lib/gem/thread_poolable.rb +59 -0
- data/lib/gem/version.rb +73 -0
- data/lib/gem/version/requirement.rb +1 -0
- data/lib/net/http/faster.rb +27 -0
- data/lib/net/http/persistent.rb +978 -0
- data/lib/net/http/persistent/ssl_reuse.rb +129 -0
- data/lib/rubygems.rb +1 -0
- data/lib/ubygems.rb +1 -0
- metadata +70 -0
data/lib/gem/tar.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#++
|
3
|
+
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
|
4
|
+
# See LICENSE.txt for additional licensing information.
|
5
|
+
#--
|
6
|
+
|
7
|
+
##
|
8
|
+
# Class for reading entries out of a tar file
|
9
|
+
|
10
|
+
class Gem::Tar::Entry
|
11
|
+
|
12
|
+
##
|
13
|
+
# Header for this tar entry
|
14
|
+
|
15
|
+
attr_reader :header
|
16
|
+
|
17
|
+
##
|
18
|
+
# Creates a new tar entry for +header+ that will be read from +io+
|
19
|
+
|
20
|
+
def initialize(header, io)
|
21
|
+
@closed = false
|
22
|
+
@header = header
|
23
|
+
@io = io
|
24
|
+
@orig_pos = @io.pos
|
25
|
+
@read = 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_closed # :nodoc:
|
29
|
+
raise IOError, "closed #{self.class}" if closed?
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Number of bytes read out of the tar entry
|
34
|
+
|
35
|
+
def bytes_read
|
36
|
+
@read
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Closes the tar entry
|
41
|
+
|
42
|
+
def close
|
43
|
+
@closed = true
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Is the tar entry closed?
|
48
|
+
|
49
|
+
def closed?
|
50
|
+
@closed
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Are we at the end of the tar entry?
|
55
|
+
|
56
|
+
def eof?
|
57
|
+
check_closed
|
58
|
+
|
59
|
+
@read >= @header.size
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Full name of the tar entry
|
64
|
+
|
65
|
+
def full_name
|
66
|
+
if @header.prefix != "" then
|
67
|
+
File.join @header.prefix, @header.name
|
68
|
+
else
|
69
|
+
@header.name
|
70
|
+
end
|
71
|
+
rescue ArgumentError => e
|
72
|
+
raise unless e.message == 'string contains null byte'
|
73
|
+
raise InvalidError,
|
74
|
+
'tar is corrupt, name contains null byte'
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Read one byte from the tar entry
|
79
|
+
|
80
|
+
def getc
|
81
|
+
check_closed
|
82
|
+
|
83
|
+
return nil if @read >= @header.size
|
84
|
+
|
85
|
+
ret = @io.getc
|
86
|
+
@read += 1 if ret
|
87
|
+
|
88
|
+
ret
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Is this tar entry a directory?
|
93
|
+
|
94
|
+
def directory?
|
95
|
+
@header.typeflag == "5"
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Is this tar entry a file?
|
100
|
+
|
101
|
+
def file?
|
102
|
+
@header.typeflag == "0"
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# The position in the tar entry
|
107
|
+
|
108
|
+
def pos
|
109
|
+
check_closed
|
110
|
+
|
111
|
+
bytes_read
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Reads +len+ bytes from the tar file entry, or the rest of the entry if
|
116
|
+
# nil
|
117
|
+
|
118
|
+
def read(len = nil)
|
119
|
+
check_closed
|
120
|
+
|
121
|
+
return nil if @read >= @header.size
|
122
|
+
|
123
|
+
len ||= @header.size - @read
|
124
|
+
max_read = [len, @header.size - @read].min
|
125
|
+
|
126
|
+
ret = @io.read max_read
|
127
|
+
@read += ret.size
|
128
|
+
|
129
|
+
ret
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Rewinds to the beginning of the tar file entry
|
134
|
+
|
135
|
+
def rewind
|
136
|
+
check_closed
|
137
|
+
|
138
|
+
raise NonSeekableIO unless @io.respond_to? :pos=
|
139
|
+
|
140
|
+
@io.pos = @orig_pos
|
141
|
+
@read = 0
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
@@ -0,0 +1,266 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#--
|
3
|
+
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
|
4
|
+
# See LICENSE.txt for additional licensing information.
|
5
|
+
#++
|
6
|
+
|
7
|
+
##
|
8
|
+
#--
|
9
|
+
# struct tarfile_entry_posix {
|
10
|
+
# char name[100]; # ASCII + (Z unless filled)
|
11
|
+
# char mode[8]; # 0 padded, octal, null
|
12
|
+
# char uid[8]; # ditto
|
13
|
+
# char gid[8]; # ditto
|
14
|
+
# char size[12]; # 0 padded, octal, null
|
15
|
+
# char mtime[12]; # 0 padded, octal, null
|
16
|
+
# char checksum[8]; # 0 padded, octal, null, space
|
17
|
+
# char typeflag[1]; # file: "0" dir: "5"
|
18
|
+
# char linkname[100]; # ASCII + (Z unless filled)
|
19
|
+
# char magic[6]; # "ustar\0"
|
20
|
+
# char version[2]; # "00"
|
21
|
+
# char uname[32]; # ASCIIZ
|
22
|
+
# char gname[32]; # ASCIIZ
|
23
|
+
# char devmajor[8]; # 0 padded, octal, null
|
24
|
+
# char devminor[8]; # o padded, octal, null
|
25
|
+
# char prefix[155]; # ASCII + (Z unless filled)
|
26
|
+
# };
|
27
|
+
#++
|
28
|
+
# A header for a tar file
|
29
|
+
|
30
|
+
class Gem::Tar::Header
|
31
|
+
|
32
|
+
##
|
33
|
+
# Fields in the tar header
|
34
|
+
|
35
|
+
FIELDS = [
|
36
|
+
:checksum,
|
37
|
+
:devmajor,
|
38
|
+
:devminor,
|
39
|
+
:gid,
|
40
|
+
:gname,
|
41
|
+
:linkname,
|
42
|
+
:magic,
|
43
|
+
:mode,
|
44
|
+
:mtime,
|
45
|
+
:name,
|
46
|
+
:prefix,
|
47
|
+
:size,
|
48
|
+
:typeflag,
|
49
|
+
:uid,
|
50
|
+
:uname,
|
51
|
+
:version,
|
52
|
+
]
|
53
|
+
|
54
|
+
##
|
55
|
+
# Pack format for a tar header
|
56
|
+
|
57
|
+
PACK_FORMAT = 'a100' + # name
|
58
|
+
'a8' + # mode
|
59
|
+
'a8' + # uid
|
60
|
+
'a8' + # gid
|
61
|
+
'a12' + # size
|
62
|
+
'a12' + # mtime
|
63
|
+
'a7a' + # chksum
|
64
|
+
'a' + # typeflag
|
65
|
+
'a100' + # linkname
|
66
|
+
'a6' + # magic
|
67
|
+
'a2' + # version
|
68
|
+
'a32' + # uname
|
69
|
+
'a32' + # gname
|
70
|
+
'a8' + # devmajor
|
71
|
+
'a8' + # devminor
|
72
|
+
'a155' # prefix
|
73
|
+
|
74
|
+
##
|
75
|
+
# Unpack format for a tar header
|
76
|
+
|
77
|
+
UNPACK_FORMAT = 'A100' + # name
|
78
|
+
'A8' + # mode
|
79
|
+
'A8' + # uid
|
80
|
+
'A8' + # gid
|
81
|
+
'A12' + # size
|
82
|
+
'A12' + # mtime
|
83
|
+
'A8' + # checksum
|
84
|
+
'A' + # typeflag
|
85
|
+
'A100' + # linkname
|
86
|
+
'A6' + # magic
|
87
|
+
'A2' + # version
|
88
|
+
'A32' + # uname
|
89
|
+
'A32' + # gname
|
90
|
+
'A8' + # devmajor
|
91
|
+
'A8' + # devminor
|
92
|
+
'A155' # prefix
|
93
|
+
|
94
|
+
attr_reader(*FIELDS)
|
95
|
+
|
96
|
+
##
|
97
|
+
# Creates a tar header from IO +stream+
|
98
|
+
|
99
|
+
def self.from(stream)
|
100
|
+
header = stream.read 512
|
101
|
+
empty = (header == "\0" * 512)
|
102
|
+
|
103
|
+
fields = header.unpack UNPACK_FORMAT
|
104
|
+
|
105
|
+
name = fields.shift
|
106
|
+
mode = fields.shift.oct
|
107
|
+
uid = fields.shift.oct
|
108
|
+
gid = fields.shift.oct
|
109
|
+
size = fields.shift.oct
|
110
|
+
mtime = fields.shift.oct
|
111
|
+
checksum = fields.shift.oct
|
112
|
+
typeflag = fields.shift
|
113
|
+
linkname = fields.shift
|
114
|
+
magic = fields.shift
|
115
|
+
version = fields.shift.oct
|
116
|
+
uname = fields.shift
|
117
|
+
gname = fields.shift
|
118
|
+
devmajor = fields.shift.oct
|
119
|
+
devminor = fields.shift.oct
|
120
|
+
prefix = fields.shift
|
121
|
+
|
122
|
+
new :name => name,
|
123
|
+
:mode => mode,
|
124
|
+
:uid => uid,
|
125
|
+
:gid => gid,
|
126
|
+
:size => size,
|
127
|
+
:mtime => mtime,
|
128
|
+
:checksum => checksum,
|
129
|
+
:typeflag => typeflag,
|
130
|
+
:linkname => linkname,
|
131
|
+
:magic => magic,
|
132
|
+
:version => version,
|
133
|
+
:uname => uname,
|
134
|
+
:gname => gname,
|
135
|
+
:devmajor => devmajor,
|
136
|
+
:devminor => devminor,
|
137
|
+
:prefix => prefix,
|
138
|
+
|
139
|
+
:empty => empty
|
140
|
+
|
141
|
+
# HACK unfactor for Rubinius
|
142
|
+
#new :name => fields.shift,
|
143
|
+
# :mode => fields.shift.oct,
|
144
|
+
# :uid => fields.shift.oct,
|
145
|
+
# :gid => fields.shift.oct,
|
146
|
+
# :size => fields.shift.oct,
|
147
|
+
# :mtime => fields.shift.oct,
|
148
|
+
# :checksum => fields.shift.oct,
|
149
|
+
# :typeflag => fields.shift,
|
150
|
+
# :linkname => fields.shift,
|
151
|
+
# :magic => fields.shift,
|
152
|
+
# :version => fields.shift.oct,
|
153
|
+
# :uname => fields.shift,
|
154
|
+
# :gname => fields.shift,
|
155
|
+
# :devmajor => fields.shift.oct,
|
156
|
+
# :devminor => fields.shift.oct,
|
157
|
+
# :prefix => fields.shift,
|
158
|
+
|
159
|
+
# :empty => empty
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Creates a new TarHeader using +vals+
|
164
|
+
|
165
|
+
def initialize(vals)
|
166
|
+
unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then
|
167
|
+
raise ArgumentError, ":name, :size, :prefix and :mode required"
|
168
|
+
end
|
169
|
+
|
170
|
+
vals[:uid] ||= 0
|
171
|
+
vals[:gid] ||= 0
|
172
|
+
vals[:mtime] ||= 0
|
173
|
+
vals[:checksum] ||= ""
|
174
|
+
vals[:typeflag] ||= "0"
|
175
|
+
vals[:magic] ||= "ustar"
|
176
|
+
vals[:version] ||= "00"
|
177
|
+
vals[:uname] ||= "wheel"
|
178
|
+
vals[:gname] ||= "wheel"
|
179
|
+
vals[:devmajor] ||= 0
|
180
|
+
vals[:devminor] ||= 0
|
181
|
+
|
182
|
+
FIELDS.each do |name|
|
183
|
+
instance_variable_set "@#{name}", vals[name]
|
184
|
+
end
|
185
|
+
|
186
|
+
@empty = vals[:empty]
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Is the tar entry empty?
|
191
|
+
|
192
|
+
def empty?
|
193
|
+
@empty
|
194
|
+
end
|
195
|
+
|
196
|
+
def ==(other) # :nodoc:
|
197
|
+
self.class === other and
|
198
|
+
@checksum == other.checksum and
|
199
|
+
@devmajor == other.devmajor and
|
200
|
+
@devminor == other.devminor and
|
201
|
+
@gid == other.gid and
|
202
|
+
@gname == other.gname and
|
203
|
+
@linkname == other.linkname and
|
204
|
+
@magic == other.magic and
|
205
|
+
@mode == other.mode and
|
206
|
+
@mtime == other.mtime and
|
207
|
+
@name == other.name and
|
208
|
+
@prefix == other.prefix and
|
209
|
+
@size == other.size and
|
210
|
+
@typeflag == other.typeflag and
|
211
|
+
@uid == other.uid and
|
212
|
+
@uname == other.uname and
|
213
|
+
@version == other.version
|
214
|
+
end
|
215
|
+
|
216
|
+
def to_s # :nodoc:
|
217
|
+
update_checksum
|
218
|
+
header
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# Updates the TarHeader's checksum
|
223
|
+
|
224
|
+
def update_checksum
|
225
|
+
header = header " " * 8
|
226
|
+
@checksum = oct calculate_checksum(header), 6
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
def calculate_checksum(header)
|
232
|
+
header.unpack("C*").inject { |a, b| a + b }
|
233
|
+
end
|
234
|
+
|
235
|
+
def header(checksum = @checksum)
|
236
|
+
header = [
|
237
|
+
name,
|
238
|
+
oct(mode, 7),
|
239
|
+
oct(uid, 7),
|
240
|
+
oct(gid, 7),
|
241
|
+
oct(size, 11),
|
242
|
+
oct(mtime, 11),
|
243
|
+
checksum,
|
244
|
+
" ",
|
245
|
+
typeflag,
|
246
|
+
linkname,
|
247
|
+
magic,
|
248
|
+
oct(version, 2),
|
249
|
+
uname,
|
250
|
+
gname,
|
251
|
+
oct(devmajor, 7),
|
252
|
+
oct(devminor, 7),
|
253
|
+
prefix
|
254
|
+
]
|
255
|
+
|
256
|
+
header = header.pack PACK_FORMAT
|
257
|
+
|
258
|
+
header << ("\0" * ((512 - header.size) % 512))
|
259
|
+
end
|
260
|
+
|
261
|
+
def oct(num, len)
|
262
|
+
"%0#{len}o" % num
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#--
|
3
|
+
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
|
4
|
+
# See LICENSE.txt for additional licensing information.
|
5
|
+
#++
|
6
|
+
|
7
|
+
##
|
8
|
+
# TarReader reads tar files and allows iteration over their items
|
9
|
+
|
10
|
+
class Gem::Tar::Reader
|
11
|
+
|
12
|
+
##
|
13
|
+
# Raised if the tar IO is not seekable
|
14
|
+
|
15
|
+
class UnexpectedEOF < StandardError; end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Creates a new TarReader on +io+ and yields it to the block, if given.
|
19
|
+
|
20
|
+
def self.new(io)
|
21
|
+
reader = super
|
22
|
+
|
23
|
+
return reader unless block_given?
|
24
|
+
|
25
|
+
begin
|
26
|
+
yield reader
|
27
|
+
ensure
|
28
|
+
reader.close
|
29
|
+
end
|
30
|
+
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Creates a new tar file reader on +io+ which needs to respond to #pos,
|
36
|
+
# #eof?, #read, #getc and #pos=
|
37
|
+
|
38
|
+
def initialize(io)
|
39
|
+
@io = io
|
40
|
+
@init_pos = io.pos
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Close the tar file
|
45
|
+
|
46
|
+
def close
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Iterates over files in the tarball yielding each entry
|
51
|
+
|
52
|
+
def each
|
53
|
+
loop do
|
54
|
+
return if @io.eof?
|
55
|
+
|
56
|
+
header = Gem::Tar::Header.from @io
|
57
|
+
return if header.empty?
|
58
|
+
|
59
|
+
entry = Gem::Tar::Entry.new header, @io
|
60
|
+
size = entry.header.size
|
61
|
+
|
62
|
+
yield entry
|
63
|
+
|
64
|
+
skip = (512 - (size % 512)) % 512
|
65
|
+
pending = size - entry.bytes_read
|
66
|
+
|
67
|
+
begin
|
68
|
+
# avoid reading...
|
69
|
+
@io.seek pending, IO::SEEK_CUR
|
70
|
+
pending = 0
|
71
|
+
rescue Errno::EINVAL, NameError
|
72
|
+
while pending > 0 do
|
73
|
+
bytes_read = @io.read([pending, 4096].min).size
|
74
|
+
raise UnexpectedEOF if @io.eof?
|
75
|
+
pending -= bytes_read
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
@io.read skip # discard trailing zeros
|
80
|
+
|
81
|
+
# make sure nobody can use #read, #getc or #rewind anymore
|
82
|
+
entry.close
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
alias each_entry each
|
87
|
+
|
88
|
+
##
|
89
|
+
# NOTE: Do not call #rewind during #each
|
90
|
+
|
91
|
+
def rewind
|
92
|
+
if @init_pos == 0 then
|
93
|
+
raise NonSeekableIO unless @io.respond_to? :rewind
|
94
|
+
@io.rewind
|
95
|
+
else
|
96
|
+
raise NonSeekableIO unless @io.respond_to? :pos=
|
97
|
+
@io.pos = @init_pos
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|