libgems 0.0.1

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.
Files changed (177) hide show
  1. data/ChangeLog +5811 -0
  2. data/History.txt +887 -0
  3. data/LICENSE.txt +51 -0
  4. data/README.md +87 -0
  5. data/Rakefile +113 -0
  6. data/lib/gauntlet_libgems.rb +50 -0
  7. data/lib/libgems.rb +1246 -0
  8. data/lib/libgems/builder.rb +102 -0
  9. data/lib/libgems/command.rb +534 -0
  10. data/lib/libgems/command_manager.rb +182 -0
  11. data/lib/libgems/commands/build_command.rb +53 -0
  12. data/lib/libgems/commands/cert_command.rb +86 -0
  13. data/lib/libgems/commands/check_command.rb +80 -0
  14. data/lib/libgems/commands/cleanup_command.rb +106 -0
  15. data/lib/libgems/commands/contents_command.rb +98 -0
  16. data/lib/libgems/commands/dependency_command.rb +195 -0
  17. data/lib/libgems/commands/environment_command.rb +133 -0
  18. data/lib/libgems/commands/fetch_command.rb +67 -0
  19. data/lib/libgems/commands/generate_index_command.rb +133 -0
  20. data/lib/libgems/commands/help_command.rb +172 -0
  21. data/lib/libgems/commands/install_command.rb +178 -0
  22. data/lib/libgems/commands/list_command.rb +35 -0
  23. data/lib/libgems/commands/lock_command.rb +110 -0
  24. data/lib/libgems/commands/mirror_command.rb +111 -0
  25. data/lib/libgems/commands/outdated_command.rb +33 -0
  26. data/lib/libgems/commands/owner_command.rb +75 -0
  27. data/lib/libgems/commands/pristine_command.rb +93 -0
  28. data/lib/libgems/commands/push_command.rb +56 -0
  29. data/lib/libgems/commands/query_command.rb +280 -0
  30. data/lib/libgems/commands/rdoc_command.rb +91 -0
  31. data/lib/libgems/commands/search_command.rb +31 -0
  32. data/lib/libgems/commands/server_command.rb +86 -0
  33. data/lib/libgems/commands/sources_command.rb +157 -0
  34. data/lib/libgems/commands/specification_command.rb +125 -0
  35. data/lib/libgems/commands/stale_command.rb +27 -0
  36. data/lib/libgems/commands/uninstall_command.rb +83 -0
  37. data/lib/libgems/commands/unpack_command.rb +121 -0
  38. data/lib/libgems/commands/update_command.rb +160 -0
  39. data/lib/libgems/commands/which_command.rb +86 -0
  40. data/lib/libgems/config_file.rb +345 -0
  41. data/lib/libgems/custom_require.rb +44 -0
  42. data/lib/libgems/defaults.rb +101 -0
  43. data/lib/libgems/dependency.rb +227 -0
  44. data/lib/libgems/dependency_installer.rb +286 -0
  45. data/lib/libgems/dependency_list.rb +208 -0
  46. data/lib/libgems/doc_manager.rb +242 -0
  47. data/lib/libgems/errors.rb +35 -0
  48. data/lib/libgems/exceptions.rb +91 -0
  49. data/lib/libgems/ext.rb +18 -0
  50. data/lib/libgems/ext/builder.rb +56 -0
  51. data/lib/libgems/ext/configure_builder.rb +25 -0
  52. data/lib/libgems/ext/ext_conf_builder.rb +24 -0
  53. data/lib/libgems/ext/rake_builder.rb +39 -0
  54. data/lib/libgems/format.rb +81 -0
  55. data/lib/libgems/gem_openssl.rb +92 -0
  56. data/lib/libgems/gem_path_searcher.rb +100 -0
  57. data/lib/libgems/gem_runner.rb +79 -0
  58. data/lib/libgems/gemcutter_utilities.rb +49 -0
  59. data/lib/libgems/indexer.rb +720 -0
  60. data/lib/libgems/install_update_options.rb +125 -0
  61. data/lib/libgems/installer.rb +604 -0
  62. data/lib/libgems/local_remote_options.rb +135 -0
  63. data/lib/libgems/old_format.rb +153 -0
  64. data/lib/libgems/package.rb +97 -0
  65. data/lib/libgems/package/f_sync_dir.rb +23 -0
  66. data/lib/libgems/package/tar_header.rb +266 -0
  67. data/lib/libgems/package/tar_input.rb +222 -0
  68. data/lib/libgems/package/tar_output.rb +144 -0
  69. data/lib/libgems/package/tar_reader.rb +106 -0
  70. data/lib/libgems/package/tar_reader/entry.rb +141 -0
  71. data/lib/libgems/package/tar_writer.rb +241 -0
  72. data/lib/libgems/package_task.rb +126 -0
  73. data/lib/libgems/platform.rb +183 -0
  74. data/lib/libgems/remote_fetcher.rb +414 -0
  75. data/lib/libgems/require_paths_builder.rb +18 -0
  76. data/lib/libgems/requirement.rb +153 -0
  77. data/lib/libgems/security.rb +814 -0
  78. data/lib/libgems/server.rb +872 -0
  79. data/lib/libgems/source_index.rb +597 -0
  80. data/lib/libgems/source_info_cache.rb +395 -0
  81. data/lib/libgems/source_info_cache_entry.rb +56 -0
  82. data/lib/libgems/spec_fetcher.rb +337 -0
  83. data/lib/libgems/specification.rb +1487 -0
  84. data/lib/libgems/test_utilities.rb +147 -0
  85. data/lib/libgems/text.rb +65 -0
  86. data/lib/libgems/uninstaller.rb +278 -0
  87. data/lib/libgems/user_interaction.rb +527 -0
  88. data/lib/libgems/validator.rb +240 -0
  89. data/lib/libgems/version.rb +316 -0
  90. data/lib/libgems/version_option.rb +65 -0
  91. data/lib/rbconfig/datadir.rb +20 -0
  92. data/test/bogussources.rb +8 -0
  93. data/test/data/gem-private_key.pem +27 -0
  94. data/test/data/gem-public_cert.pem +20 -0
  95. data/test/fake_certlib/openssl.rb +7 -0
  96. data/test/foo/discover.rb +0 -0
  97. data/test/gem_installer_test_case.rb +97 -0
  98. data/test/gem_package_tar_test_case.rb +132 -0
  99. data/test/gemutilities.rb +605 -0
  100. data/test/insure_session.rb +43 -0
  101. data/test/mockgemui.rb +56 -0
  102. data/test/plugin/exception/libgems_plugin.rb +2 -0
  103. data/test/plugin/load/libgems_plugin.rb +1 -0
  104. data/test/plugin/standarderror/libgems_plugin.rb +2 -0
  105. data/test/private_key.pem +27 -0
  106. data/test/public_cert.pem +20 -0
  107. data/test/rubygems_plugin.rb +21 -0
  108. data/test/simple_gem.rb +66 -0
  109. data/test/test_config.rb +12 -0
  110. data/test/test_gem.rb +780 -0
  111. data/test/test_gem_builder.rb +27 -0
  112. data/test/test_gem_command.rb +178 -0
  113. data/test/test_gem_command_manager.rb +207 -0
  114. data/test/test_gem_commands_build_command.rb +74 -0
  115. data/test/test_gem_commands_cert_command.rb +124 -0
  116. data/test/test_gem_commands_check_command.rb +18 -0
  117. data/test/test_gem_commands_contents_command.rb +156 -0
  118. data/test/test_gem_commands_dependency_command.rb +216 -0
  119. data/test/test_gem_commands_environment_command.rb +144 -0
  120. data/test/test_gem_commands_fetch_command.rb +76 -0
  121. data/test/test_gem_commands_generate_index_command.rb +135 -0
  122. data/test/test_gem_commands_install_command.rb +315 -0
  123. data/test/test_gem_commands_list_command.rb +36 -0
  124. data/test/test_gem_commands_lock_command.rb +68 -0
  125. data/test/test_gem_commands_mirror_command.rb +60 -0
  126. data/test/test_gem_commands_outdated_command.rb +40 -0
  127. data/test/test_gem_commands_owner_command.rb +105 -0
  128. data/test/test_gem_commands_pristine_command.rb +108 -0
  129. data/test/test_gem_commands_push_command.rb +81 -0
  130. data/test/test_gem_commands_query_command.rb +426 -0
  131. data/test/test_gem_commands_server_command.rb +59 -0
  132. data/test/test_gem_commands_sources_command.rb +209 -0
  133. data/test/test_gem_commands_specification_command.rb +139 -0
  134. data/test/test_gem_commands_stale_command.rb +38 -0
  135. data/test/test_gem_commands_uninstall_command.rb +83 -0
  136. data/test/test_gem_commands_unpack_command.rb +199 -0
  137. data/test/test_gem_commands_update_command.rb +207 -0
  138. data/test/test_gem_commands_which_command.rb +66 -0
  139. data/test/test_gem_config_file.rb +287 -0
  140. data/test/test_gem_dependency.rb +149 -0
  141. data/test/test_gem_dependency_installer.rb +661 -0
  142. data/test/test_gem_dependency_list.rb +230 -0
  143. data/test/test_gem_doc_manager.rb +31 -0
  144. data/test/test_gem_ext_configure_builder.rb +84 -0
  145. data/test/test_gem_ext_ext_conf_builder.rb +173 -0
  146. data/test/test_gem_ext_rake_builder.rb +81 -0
  147. data/test/test_gem_format.rb +70 -0
  148. data/test/test_gem_gem_path_searcher.rb +78 -0
  149. data/test/test_gem_gem_runner.rb +45 -0
  150. data/test/test_gem_gemcutter_utilities.rb +103 -0
  151. data/test/test_gem_indexer.rb +673 -0
  152. data/test/test_gem_install_update_options.rb +68 -0
  153. data/test/test_gem_installer.rb +857 -0
  154. data/test/test_gem_local_remote_options.rb +97 -0
  155. data/test/test_gem_package_tar_header.rb +130 -0
  156. data/test/test_gem_package_tar_input.rb +112 -0
  157. data/test/test_gem_package_tar_output.rb +97 -0
  158. data/test/test_gem_package_tar_reader.rb +46 -0
  159. data/test/test_gem_package_tar_reader_entry.rb +109 -0
  160. data/test/test_gem_package_tar_writer.rb +144 -0
  161. data/test/test_gem_package_task.rb +59 -0
  162. data/test/test_gem_platform.rb +264 -0
  163. data/test/test_gem_remote_fetcher.rb +740 -0
  164. data/test/test_gem_requirement.rb +292 -0
  165. data/test/test_gem_server.rb +356 -0
  166. data/test/test_gem_silent_ui.rb +113 -0
  167. data/test/test_gem_source_index.rb +461 -0
  168. data/test/test_gem_spec_fetcher.rb +410 -0
  169. data/test/test_gem_specification.rb +1334 -0
  170. data/test/test_gem_stream_ui.rb +218 -0
  171. data/test/test_gem_text.rb +43 -0
  172. data/test/test_gem_uninstaller.rb +146 -0
  173. data/test/test_gem_validator.rb +63 -0
  174. data/test/test_gem_version.rb +181 -0
  175. data/test/test_gem_version_option.rb +89 -0
  176. data/test/test_kernel.rb +59 -0
  177. metadata +402 -0
@@ -0,0 +1,222 @@
1
+ # -*- coding: iso-8859-1 -*-
2
+ #++
3
+ # Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier
4
+ # See LICENSE.txt for additional licensing information.
5
+ #--
6
+
7
+ require 'zlib'
8
+ LibGems.load_yaml
9
+
10
+ class LibGems::Package::TarInput
11
+
12
+ include LibGems::Package::FSyncDir
13
+ include Enumerable
14
+
15
+ attr_reader :metadata
16
+
17
+ private_class_method :new
18
+
19
+ def self.open(io, security_policy = nil, &block)
20
+ is = new io, security_policy
21
+
22
+ yield is
23
+ ensure
24
+ is.close if is
25
+ end
26
+
27
+ def initialize(io, security_policy = nil)
28
+ @io = io
29
+ @tarreader = LibGems::Package::TarReader.new @io
30
+ has_meta = false
31
+
32
+ data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
33
+ dgst_algo = security_policy ? LibGems::Security::OPT[:dgst_algo] : nil
34
+
35
+ @tarreader.each do |entry|
36
+ case entry.full_name
37
+ when "metadata"
38
+ @metadata = load_gemspec entry.read
39
+ has_meta = true
40
+ when "metadata.gz"
41
+ begin
42
+ # if we have a security_policy, then pre-read the metadata file
43
+ # and calculate it's digest
44
+ sio = nil
45
+ if security_policy
46
+ LibGems.ensure_ssl_available
47
+ sio = StringIO.new(entry.read)
48
+ meta_dgst = dgst_algo.digest(sio.string)
49
+ sio.rewind
50
+ end
51
+
52
+ gzis = Zlib::GzipReader.new(sio || entry)
53
+ # YAML wants an instance of IO
54
+ @metadata = load_gemspec(gzis)
55
+ has_meta = true
56
+ ensure
57
+ gzis.close unless gzis.nil?
58
+ end
59
+ when 'metadata.gz.sig'
60
+ meta_sig = entry.read
61
+ when 'data.tar.gz.sig'
62
+ data_sig = entry.read
63
+ when 'data.tar.gz'
64
+ if security_policy
65
+ LibGems.ensure_ssl_available
66
+ data_dgst = dgst_algo.digest(entry.read)
67
+ end
68
+ end
69
+ end
70
+
71
+ if security_policy then
72
+ LibGems.ensure_ssl_available
73
+
74
+ # map trust policy from string to actual class (or a serialized YAML
75
+ # file, if that exists)
76
+ if String === security_policy then
77
+ if LibGems::Security::Policies.key? security_policy then
78
+ # load one of the pre-defined security policies
79
+ security_policy = LibGems::Security::Policies[security_policy]
80
+ elsif File.exist? security_policy then
81
+ # FIXME: this doesn't work yet
82
+ security_policy = YAML.load File.read(security_policy)
83
+ else
84
+ raise LibGems::Exception, "Unknown trust policy '#{security_policy}'"
85
+ end
86
+ end
87
+
88
+ if data_sig && data_dgst && meta_sig && meta_dgst then
89
+ # the user has a trust policy, and we have a signed gem
90
+ # file, so use the trust policy to verify the gem signature
91
+
92
+ begin
93
+ security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
94
+ rescue Exception => e
95
+ raise "Couldn't verify data signature: #{e}"
96
+ end
97
+
98
+ begin
99
+ security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
100
+ rescue Exception => e
101
+ raise "Couldn't verify metadata signature: #{e}"
102
+ end
103
+ elsif security_policy.only_signed
104
+ raise LibGems::Exception, "Unsigned gem"
105
+ else
106
+ # FIXME: should display warning here (trust policy, but
107
+ # either unsigned or badly signed gem file)
108
+ end
109
+ end
110
+
111
+ @tarreader.rewind
112
+ @fileops = LibGems::FileOperations.new
113
+
114
+ raise LibGems::Package::FormatError, "No metadata found!" unless has_meta
115
+ end
116
+
117
+ def close
118
+ @io.close
119
+ @tarreader.close
120
+ end
121
+
122
+ def each(&block)
123
+ @tarreader.each do |entry|
124
+ next unless entry.full_name == "data.tar.gz"
125
+ is = zipped_stream entry
126
+
127
+ begin
128
+ LibGems::Package::TarReader.new is do |inner|
129
+ inner.each(&block)
130
+ end
131
+ ensure
132
+ is.close if is
133
+ end
134
+ end
135
+
136
+ @tarreader.rewind
137
+ end
138
+
139
+ def extract_entry(destdir, entry, expected_md5sum = nil)
140
+ if entry.directory? then
141
+ dest = File.join destdir, entry.full_name
142
+
143
+ if File.directory? dest then
144
+ @fileops.chmod entry.header.mode, dest, :verbose => false
145
+ else
146
+ @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false
147
+ end
148
+
149
+ fsync_dir dest
150
+ fsync_dir File.join(dest, "..")
151
+
152
+ return
153
+ end
154
+
155
+ # it's a file
156
+ md5 = Digest::MD5.new if expected_md5sum
157
+ destdir = File.join destdir, File.dirname(entry.full_name)
158
+ @fileops.mkdir_p destdir, :mode => 0755, :verbose => false
159
+ destfile = File.join destdir, File.basename(entry.full_name)
160
+ @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
161
+
162
+ open destfile, "wb", entry.header.mode do |os|
163
+ loop do
164
+ data = entry.read 4096
165
+ break unless data
166
+ # HACK shouldn't we check the MD5 before writing to disk?
167
+ md5 << data if expected_md5sum
168
+ os.write(data)
169
+ end
170
+
171
+ os.fsync
172
+ end
173
+
174
+ @fileops.chmod entry.header.mode, destfile, :verbose => false
175
+ fsync_dir File.dirname(destfile)
176
+ fsync_dir File.join(File.dirname(destfile), "..")
177
+
178
+ if expected_md5sum && expected_md5sum != md5.hexdigest then
179
+ raise LibGems::Package::BadCheckSum
180
+ end
181
+ end
182
+
183
+ # Attempt to YAML-load a gemspec from the given _io_ parameter. Return
184
+ # nil if it fails.
185
+ def load_gemspec(io)
186
+ LibGems::Specification.from_yaml io
187
+ rescue LibGems::Exception
188
+ nil
189
+ end
190
+
191
+ ##
192
+ # Return an IO stream for the zipped entry.
193
+ #
194
+ # NOTE: Originally this method used two approaches, Return a GZipReader
195
+ # directly, or read the GZipReader into a string and return a StringIO on
196
+ # the string. The string IO approach was used for versions of ZLib before
197
+ # 1.2.1 to avoid buffer errors on windows machines. Then we found that
198
+ # errors happened with 1.2.1 as well, so we changed the condition. Then
199
+ # we discovered errors occurred with versions as late as 1.2.3. At this
200
+ # point (after some benchmarking to show we weren't seriously crippling
201
+ # the unpacking speed) we threw our hands in the air and declared that
202
+ # this method would use the String IO approach on all platforms at all
203
+ # times. And that's the way it is.
204
+
205
+ def zipped_stream(entry)
206
+ if defined? Rubinius or defined? Maglev then
207
+ # these implementations have working Zlib
208
+ zis = Zlib::GzipReader.new entry
209
+ dis = zis.read
210
+ is = StringIO.new(dis)
211
+ else
212
+ # This is Jamis Buck's Zlib workaround for some unknown issue
213
+ entry.read(10) # skip the gzip header
214
+ zis = Zlib::Inflate.new(-Zlib::MAX_WBITS)
215
+ is = StringIO.new(zis.inflate(entry.read))
216
+ end
217
+ ensure
218
+ zis.finish if zis
219
+ end
220
+
221
+ end
222
+
@@ -0,0 +1,144 @@
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
+ # TarOutput is a wrapper to TarWriter that builds gem-format tar file.
9
+ #
10
+ # LibGems-format tar files contain the following files:
11
+ # [data.tar.gz] A gzipped tar file containing the files that compose the gem
12
+ # which will be extracted into the gem/ dir on installation.
13
+ # [metadata.gz] A YAML format LibGems::Specification.
14
+ # [data.tar.gz.sig] A signature for the gem's data.tar.gz.
15
+ # [metadata.gz.sig] A signature for the gem's metadata.gz.
16
+ #
17
+ # See TarOutput::open for usage details.
18
+
19
+ class LibGems::Package::TarOutput
20
+
21
+ ##
22
+ # Creates a new TarOutput which will yield a TarWriter object for the
23
+ # data.tar.gz portion of a gem-format tar file.
24
+ #
25
+ # See #initialize for details on +io+ and +signer+.
26
+ #
27
+ # See #add_gem_contents for details on adding metadata to the tar file.
28
+
29
+ def self.open(io, signer = nil, &block) # :yield: data_tar_writer
30
+ tar_outputter = new io, signer
31
+ tar_outputter.add_gem_contents(&block)
32
+ tar_outputter.add_metadata
33
+ tar_outputter.add_signatures
34
+
35
+ ensure
36
+ tar_outputter.close
37
+ end
38
+
39
+ ##
40
+ # Creates a new TarOutput that will write a gem-format tar file to +io+. If
41
+ # +signer+ is given, the data.tar.gz and metadata.gz will be signed and
42
+ # the signatures will be added to the tar file.
43
+
44
+ def initialize(io, signer)
45
+ @io = io
46
+ @signer = signer
47
+
48
+ @tar_writer = LibGems::Package::TarWriter.new @io
49
+
50
+ @metadata = nil
51
+
52
+ @data_signature = nil
53
+ @meta_signature = nil
54
+ end
55
+
56
+ ##
57
+ # Yields a TarWriter for the data.tar.gz inside a gem-format tar file.
58
+ # The yielded TarWriter has been extended with a #metadata= method for
59
+ # attaching a YAML format LibGems::Specification which will be written by
60
+ # add_metadata.
61
+
62
+ def add_gem_contents
63
+ @tar_writer.add_file "data.tar.gz", 0644 do |inner|
64
+ sio = @signer ? StringIO.new : nil
65
+ Zlib::GzipWriter.wrap(sio || inner) do |os|
66
+
67
+ LibGems::Package::TarWriter.new os do |data_tar_writer|
68
+ # :stopdoc:
69
+ def data_tar_writer.metadata() @metadata end
70
+ def data_tar_writer.metadata=(metadata) @metadata = metadata end
71
+ # :startdoc:
72
+
73
+ yield data_tar_writer
74
+
75
+ @metadata = data_tar_writer.metadata
76
+ end
77
+ end
78
+
79
+ # if we have a signing key, then sign the data
80
+ # digest and return the signature
81
+ if @signer then
82
+ digest = LibGems::Security::OPT[:dgst_algo].digest sio.string
83
+ @data_signature = @signer.sign digest
84
+ inner.write sio.string
85
+ end
86
+ end
87
+
88
+ self
89
+ end
90
+
91
+ ##
92
+ # Adds metadata.gz to the gem-format tar file which was saved from a
93
+ # previous #add_gem_contents call.
94
+
95
+ def add_metadata
96
+ return if @metadata.nil?
97
+
98
+ @tar_writer.add_file "metadata.gz", 0644 do |io|
99
+ begin
100
+ sio = @signer ? StringIO.new : nil
101
+ gzos = Zlib::GzipWriter.new(sio || io)
102
+ gzos.write @metadata
103
+ ensure
104
+ gzos.flush
105
+ gzos.finish
106
+
107
+ # if we have a signing key, then sign the metadata digest and return
108
+ # the signature
109
+ if @signer then
110
+ digest = LibGems::Security::OPT[:dgst_algo].digest sio.string
111
+ @meta_signature = @signer.sign digest
112
+ io.write sio.string
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ ##
119
+ # Adds data.tar.gz.sig and metadata.gz.sig to the gem-format tar files if
120
+ # a LibGems::Security::Signer was sent to initialize.
121
+
122
+ def add_signatures
123
+ if @data_signature then
124
+ @tar_writer.add_file 'data.tar.gz.sig', 0644 do |io|
125
+ io.write @data_signature
126
+ end
127
+ end
128
+
129
+ if @meta_signature then
130
+ @tar_writer.add_file 'metadata.gz.sig', 0644 do |io|
131
+ io.write @meta_signature
132
+ end
133
+ end
134
+ end
135
+
136
+ ##
137
+ # Closes the TarOutput.
138
+
139
+ def close
140
+ @tar_writer.close
141
+ end
142
+
143
+ end
144
+
@@ -0,0 +1,106 @@
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 LibGems::Package::TarReader
11
+
12
+ include LibGems::Package
13
+
14
+ ##
15
+ # Raised if the tar IO is not seekable
16
+
17
+ class UnexpectedEOF < StandardError; end
18
+
19
+ ##
20
+ # Creates a new TarReader on +io+ and yields it to the block, if given.
21
+
22
+ def self.new(io)
23
+ reader = super
24
+
25
+ return reader unless block_given?
26
+
27
+ begin
28
+ yield reader
29
+ ensure
30
+ reader.close
31
+ end
32
+
33
+ nil
34
+ end
35
+
36
+ ##
37
+ # Creates a new tar file reader on +io+ which needs to respond to #pos,
38
+ # #eof?, #read, #getc and #pos=
39
+
40
+ def initialize(io)
41
+ @io = io
42
+ @init_pos = io.pos
43
+ end
44
+
45
+ ##
46
+ # Close the tar file
47
+
48
+ def close
49
+ end
50
+
51
+ ##
52
+ # Iterates over files in the tarball yielding each entry
53
+
54
+ def each
55
+ loop do
56
+ return if @io.eof?
57
+
58
+ header = LibGems::Package::TarHeader.from @io
59
+ return if header.empty?
60
+
61
+ entry = LibGems::Package::TarReader::Entry.new header, @io
62
+ size = entry.header.size
63
+
64
+ yield entry
65
+
66
+ skip = (512 - (size % 512)) % 512
67
+ pending = size - entry.bytes_read
68
+
69
+ begin
70
+ # avoid reading...
71
+ @io.seek pending, IO::SEEK_CUR
72
+ pending = 0
73
+ rescue Errno::EINVAL, NameError
74
+ while pending > 0 do
75
+ bytes_read = @io.read([pending, 4096].min).size
76
+ raise UnexpectedEOF if @io.eof?
77
+ pending -= bytes_read
78
+ end
79
+ end
80
+
81
+ @io.read skip # discard trailing zeros
82
+
83
+ # make sure nobody can use #read, #getc or #rewind anymore
84
+ entry.close
85
+ end
86
+ end
87
+
88
+ alias each_entry each
89
+
90
+ ##
91
+ # NOTE: Do not call #rewind during #each
92
+
93
+ def rewind
94
+ if @init_pos == 0 then
95
+ raise LibGems::Package::NonSeekableIO unless @io.respond_to? :rewind
96
+ @io.rewind
97
+ else
98
+ raise LibGems::Package::NonSeekableIO unless @io.respond_to? :pos=
99
+ @io.pos = @init_pos
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ require 'libgems/package/tar_reader/entry'
106
+