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,135 @@
1
+ #--
2
+ # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+ require 'uri'
8
+ require 'libgems'
9
+
10
+ ##
11
+ # Mixin methods for local and remote LibGems::Command options.
12
+
13
+ module LibGems::LocalRemoteOptions
14
+
15
+ ##
16
+ # Allows OptionParser to handle HTTP URIs.
17
+
18
+ def accept_uri_http
19
+ OptionParser.accept URI::HTTP do |value|
20
+ begin
21
+ uri = URI.parse value
22
+ rescue URI::InvalidURIError
23
+ raise OptionParser::InvalidArgument, value
24
+ end
25
+
26
+ unless ['http', 'https', 'file'].include?(uri.scheme)
27
+ raise OptionParser::InvalidArgument, value
28
+ end
29
+
30
+ value
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Add local/remote options to the command line parser.
36
+
37
+ def add_local_remote_options
38
+ add_option(:"Local/Remote", '-l', '--local',
39
+ 'Restrict operations to the LOCAL domain') do |value, options|
40
+ options[:domain] = :local
41
+ end
42
+
43
+ add_option(:"Local/Remote", '-r', '--remote',
44
+ 'Restrict operations to the REMOTE domain') do |value, options|
45
+ options[:domain] = :remote
46
+ end
47
+
48
+ add_option(:"Local/Remote", '-b', '--both',
49
+ 'Allow LOCAL and REMOTE operations') do |value, options|
50
+ options[:domain] = :both
51
+ end
52
+
53
+ add_bulk_threshold_option
54
+ add_source_option
55
+ add_proxy_option
56
+ add_update_sources_option
57
+ end
58
+
59
+ ##
60
+ # Add the --bulk-threshold option
61
+
62
+ def add_bulk_threshold_option
63
+ add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT',
64
+ "Threshold for switching to bulk",
65
+ "synchronization (default #{LibGems.configuration.bulk_threshold})") do
66
+ |value, options|
67
+ LibGems.configuration.bulk_threshold = value.to_i
68
+ end
69
+ end
70
+
71
+ ##
72
+ # Add the --http-proxy option
73
+
74
+ def add_proxy_option
75
+ accept_uri_http
76
+
77
+ add_option(:"Local/Remote", '-p', '--[no-]http-proxy [URL]', URI::HTTP,
78
+ 'Use HTTP proxy for remote operations') do |value, options|
79
+ options[:http_proxy] = (value == false) ? :no_proxy : value
80
+ LibGems.configuration[:http_proxy] = options[:http_proxy]
81
+ end
82
+ end
83
+
84
+ ##
85
+ # Add the --source option
86
+
87
+ def add_source_option
88
+ accept_uri_http
89
+
90
+ add_option(:"Local/Remote", '--source URL', URI::HTTP,
91
+ 'Use URL as the remote source for gems') do |source, options|
92
+ source << '/' if source !~ /\/\z/
93
+
94
+ if options[:added_source] then
95
+ LibGems.sources << source unless LibGems.sources.include?(source)
96
+ else
97
+ options[:added_source] = true
98
+ LibGems.sources.replace [source]
99
+ end
100
+ end
101
+ end
102
+
103
+ ##
104
+ # Add the --update-sources option
105
+
106
+ def add_update_sources_option
107
+ add_option(:"Local/Remote", '-u', '--[no-]update-sources',
108
+ 'Update local source cache') do |value, options|
109
+ LibGems.configuration.update_sources = value
110
+ end
111
+ end
112
+
113
+ ##
114
+ # Is fetching of local and remote information enabled?
115
+
116
+ def both?
117
+ options[:domain] == :both
118
+ end
119
+
120
+ ##
121
+ # Is local fetching enabled?
122
+
123
+ def local?
124
+ options[:domain] == :local || options[:domain] == :both
125
+ end
126
+
127
+ ##
128
+ # Is remote fetching enabled?
129
+
130
+ def remote?
131
+ options[:domain] == :remote || options[:domain] == :both
132
+ end
133
+
134
+ end
135
+
@@ -0,0 +1,153 @@
1
+ #--
2
+ # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+ require 'libgems'
8
+
9
+ ##
10
+ # The format class knows the guts of the RubyGem .gem file format and provides
11
+ # the capability to read gem files
12
+
13
+ class LibGems::OldFormat
14
+
15
+ attr_accessor :spec, :file_entries, :gem_path
16
+
17
+ ##
18
+ # Constructs an instance of a Format object, representing the gem's data
19
+ # structure.
20
+ #
21
+ # gem:: [String] The file name of the gem
22
+
23
+ def initialize(gem_path)
24
+ require 'fileutils'
25
+ require 'zlib'
26
+ LibGems.load_yaml
27
+
28
+ @gem_path = gem_path
29
+ end
30
+
31
+ ##
32
+ # Reads the named gem file and returns a Format object, representing the
33
+ # data from the gem file
34
+ #
35
+ # file_path:: [String] Path to the gem file
36
+
37
+ def self.from_file_by_path(file_path)
38
+ unless File.exist?(file_path)
39
+ raise LibGems::Exception, "Cannot load gem file [#{file_path}]"
40
+ end
41
+
42
+ File.open(file_path, 'rb') do |file|
43
+ from_io(file, file_path)
44
+ end
45
+ end
46
+
47
+ ##
48
+ # Reads a gem from an io stream and returns a Format object, representing
49
+ # the data from the gem file
50
+ #
51
+ # io:: [IO] Stream from which to read the gem
52
+
53
+ def self.from_io(io, gem_path="(io)")
54
+ format = self.new(gem_path)
55
+ skip_ruby(io)
56
+ format.spec = read_spec(io)
57
+ format.file_entries = []
58
+ read_files_from_gem(io) do |entry, file_data|
59
+ format.file_entries << [entry, file_data]
60
+ end
61
+ format
62
+ end
63
+
64
+ private
65
+
66
+ ##
67
+ # Skips the Ruby self-install header. After calling this method, the
68
+ # IO index will be set after the Ruby code.
69
+ #
70
+ # file:: [IO] The IO to process (skip the Ruby code)
71
+
72
+ def self.skip_ruby(file)
73
+ end_seen = false
74
+ loop {
75
+ line = file.gets
76
+ if(line == nil || line.chomp == "__END__") then
77
+ end_seen = true
78
+ break
79
+ end
80
+ }
81
+
82
+ if end_seen == false then
83
+ raise LibGems::Exception.new("Failed to find end of ruby script while reading gem")
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Reads the specification YAML from the supplied IO and constructs
89
+ # a LibGems::Specification from it. After calling this method, the
90
+ # IO index will be set after the specification header.
91
+ #
92
+ # file:: [IO] The IO to process
93
+
94
+ def self.read_spec(file)
95
+ yaml = ''
96
+
97
+ read_until_dashes file do |line|
98
+ yaml << line
99
+ end
100
+
101
+ LibGems::Specification.from_yaml yaml
102
+ rescue YAML::Error => e
103
+ raise LibGems::Exception, "Failed to parse gem specification out of gem file"
104
+ rescue ArgumentError => e
105
+ raise LibGems::Exception, "Failed to parse gem specification out of gem file"
106
+ end
107
+
108
+ ##
109
+ # Reads lines from the supplied IO until a end-of-yaml (---) is
110
+ # reached
111
+ #
112
+ # file:: [IO] The IO to process
113
+ # block:: [String] The read line
114
+
115
+ def self.read_until_dashes(file)
116
+ while((line = file.gets) && line.chomp.strip != "---") do
117
+ yield line
118
+ end
119
+ end
120
+
121
+ ##
122
+ # Reads the embedded file data from a gem file, yielding an entry
123
+ # containing metadata about the file and the file contents themselves
124
+ # for each file that's archived in the gem.
125
+ # NOTE: Many of these methods should be extracted into some kind of
126
+ # LibGems file read/writer
127
+ #
128
+ # gem_file:: [IO] The IO to process
129
+
130
+ def self.read_files_from_gem(gem_file)
131
+ errstr = "Error reading files from gem"
132
+ header_yaml = ''
133
+ begin
134
+ self.read_until_dashes(gem_file) do |line|
135
+ header_yaml << line
136
+ end
137
+ header = YAML.load(header_yaml)
138
+ raise LibGems::Exception, errstr unless header
139
+
140
+ header.each do |entry|
141
+ file_data = ''
142
+ self.read_until_dashes(gem_file) do |line|
143
+ file_data << line
144
+ end
145
+ yield [entry, Zlib::Inflate.inflate(file_data.strip.unpack("m")[0])]
146
+ end
147
+ rescue Zlib::DataError
148
+ raise LibGems::Exception, errstr
149
+ end
150
+ end
151
+
152
+ end
153
+
@@ -0,0 +1,97 @@
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
+ require 'fileutils'
8
+ require 'find'
9
+ require 'stringio'
10
+ require 'yaml'
11
+ require 'zlib'
12
+
13
+ require 'libgems/security'
14
+ require 'libgems/specification'
15
+
16
+ ##
17
+ # Wrapper for FileUtils meant to provide logging and additional operations if
18
+ # needed.
19
+
20
+ class LibGems::FileOperations
21
+
22
+ def initialize(logger = nil)
23
+ @logger = logger
24
+ end
25
+
26
+ def method_missing(meth, *args, &block)
27
+ case
28
+ when FileUtils.respond_to?(meth)
29
+ @logger.log "#{meth}: #{args}" if @logger
30
+ FileUtils.send meth, *args, &block
31
+ when LibGems::FileOperations.respond_to?(meth)
32
+ @logger.log "#{meth}: #{args}" if @logger
33
+ LibGems::FileOperations.send meth, *args, &block
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ module LibGems::Package
42
+
43
+ class Error < StandardError; end
44
+ class NonSeekableIO < Error; end
45
+ class ClosedIO < Error; end
46
+ class BadCheckSum < Error; end
47
+ class TooLongFileName < Error; end
48
+ class FormatError < Error; end
49
+
50
+ def self.open(io, mode = "r", signer = nil, &block)
51
+ tar_type = case mode
52
+ when 'r' then TarInput
53
+ when 'w' then TarOutput
54
+ else
55
+ raise "Unknown Package open mode"
56
+ end
57
+
58
+ tar_type.open(io, signer, &block)
59
+ end
60
+
61
+ def self.pack(src, destname, signer = nil)
62
+ TarOutput.open(destname, signer) do |outp|
63
+ dir_class.chdir(src) do
64
+ outp.metadata = (file_class.read("RPA/metadata") rescue nil)
65
+ find_class.find('.') do |entry|
66
+ case
67
+ when file_class.file?(entry)
68
+ entry.sub!(%r{\./}, "")
69
+ next if entry =~ /\ARPA\//
70
+ stat = File.stat(entry)
71
+ outp.add_file_simple(entry, stat.mode, stat.size) do |os|
72
+ file_class.open(entry, "rb") do |f|
73
+ os.write(f.read(4096)) until f.eof?
74
+ end
75
+ end
76
+ when file_class.dir?(entry)
77
+ entry.sub!(%r{\./}, "")
78
+ next if entry == "RPA"
79
+ outp.mkdir(entry, file_class.stat(entry).mode)
80
+ else
81
+ raise "Don't know how to pack this yet!"
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+ require 'libgems/package/f_sync_dir'
91
+ require 'libgems/package/tar_header'
92
+ require 'libgems/package/tar_input'
93
+ require 'libgems/package/tar_output'
94
+ require 'libgems/package/tar_reader'
95
+ require 'libgems/package/tar_reader/entry'
96
+ require 'libgems/package/tar_writer'
97
+
@@ -0,0 +1,23 @@
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
+ module LibGems::Package::FSyncDir
8
+
9
+ private
10
+
11
+ ##
12
+ # make sure this hits the disc
13
+
14
+ def fsync_dir(dirname)
15
+ dir = open dirname, 'r'
16
+ dir.fsync
17
+ rescue # ignore IOError if it's an unpatched (old) Ruby
18
+ ensure
19
+ dir.close if dir rescue nil
20
+ end
21
+
22
+ end
23
+
@@ -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 LibGems::Package::TarHeader
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
+