archive-tar-minitar 0.5.2 → 0.6

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 80d2815fe493d7e3488c6e77d6ce05ade935177e
4
+ data.tar.gz: ed88af9dcdd1595fa101bb7614f93afe77375509
5
+ SHA512:
6
+ metadata.gz: 88e3b38e10d3c2e6656a5f161718978e301c03ed3e57d5203c2a4489411f4a7a8ad0fcc5c1f527e958852707f42228a448ee9387b1e2146326cf9e1aa4cb7e35
7
+ data.tar.gz: 8d19b2a2c40cc282842016cb7d07c8e8b6e9092fe6c3c96c1904e17ddc570d6dff60e2329652311e652fd285cb805d98bfb6f7a45abd1f0be31fda4df250603c
@@ -0,0 +1,3 @@
1
+ # coding: utf-8
2
+
3
+ require 'archive/tar/minitar'
metadata CHANGED
@@ -1,73 +1,96 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: archive-tar-minitar
3
- version: !ruby/object:Gem::Version
4
- version: 0.5.2
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.6'
5
5
  platform: ruby
6
- authors:
7
- - Austin Ziegler, Mauricio Ferna'ndez
6
+ authors:
7
+ - Austin Ziegler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
+ date: 2017-02-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitar
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitar-cli
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.6'
41
+ description: |-
42
+ 'archive-tar-minitar' has been deprecated; just install 'minitar'. The minitar library is a pure-Ruby library that provides the ability to deal
43
+ with POSIX tar(1) archive files.
11
44
 
12
- date: 2008-02-26 00:00:00 -05:00
13
- default_executable:
14
- dependencies: []
45
+ This is release 0.6, providing a number of bug fixes including a directory
46
+ traversal vulnerability, CVE-2016-10173. This release starts the migration and
47
+ modernization of the code:
15
48
 
16
- description: Archive::Tar::Minitar is a pure-Ruby library and command-line utility that provides the ability to deal with POSIX tar(1) archive files. The implementation is based heavily on Mauricio Ferna'ndez's implementation in rpa-base, but has been reorganised to promote reuse in other projects.
17
- email: minitar@halostatue.ca
18
- executables:
19
- - minitar
20
- extensions: []
49
+ * the licence has been changed to match the modern Ruby licensing scheme
50
+ (Ruby and Simplified BSD instead of Ruby and GNU GPL);
51
+ * the +minitar+ command-line program has been separated into the
52
+ +minitar-cli+ gem; and
53
+ * the +archive-tar-minitar+ gem now points to the +minitar+ and +minitar-cli+
54
+ gems and discourages its installation.
21
55
 
22
- extra_rdoc_files:
23
- - README
24
- - ChangeLog
25
- - Install
26
- files:
27
- - bin
28
- - bin/minitar
29
- - ChangeLog
30
- - Install
31
- - lib
32
- - lib/archive
33
- - lib/archive/tar
34
- - lib/archive/tar/minitar
35
- - lib/archive/tar/minitar/command.rb
36
- - lib/archive/tar/minitar.rb
37
- - Rakefile
38
- - README
39
- - tests
40
- - tests/tc_tar.rb
41
- - tests/testall.rb
42
- has_rdoc: true
43
- homepage: http://rubyforge.org/projects/ruwiki/
44
- post_install_message:
45
- rdoc_options:
46
- - --title
47
- - Archive::Tar::MiniTar -- A POSIX tarchive library
48
- - --main
49
- - README
50
- - --line-numbers
51
- require_paths:
56
+ Some of these changes may break existing programs that depend on the internal
57
+ structure of the minitar library, but every effort has been made to ensure
58
+ compatibility; inasmuch as is possible, this compatibility will be maintained
59
+ through the release of minitar 1.0 (which will have strong breaking changes).
60
+
61
+ minitar (previously called Archive::Tar::Minitar) is based heavily on code
62
+ originally written by Mauricio Julio Fernández Pradier for the rpa-base
63
+ project.
64
+ email:
65
+ - halostatue@gmail.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - lib/archive-tar-minitar.rb
71
+ homepage: https://github.com/halostatue/minitar/
72
+ licenses:
73
+ - Ruby
74
+ - BSD-2-Clause
75
+ metadata: {}
76
+ post_install_message: "'archive-tar-minitar' has been deprecated; just install 'minitar'."
77
+ rdoc_options: []
78
+ require_paths:
52
79
  - lib
53
- required_ruby_version: !ruby/object:Gem::Requirement
54
- requirements:
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
55
82
  - - ">="
56
- - !ruby/object:Gem::Version
57
- version: 1.8.2
58
- version:
59
- required_rubygems_version: !ruby/object:Gem::Requirement
60
- requirements:
83
+ - !ruby/object:Gem::Version
84
+ version: '1.8'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
61
87
  - - ">="
62
- - !ruby/object:Gem::Version
63
- version: "0"
64
- version:
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
65
90
  requirements: []
66
-
67
- rubyforge_project: ruwiki
68
- rubygems_version: 1.0.1
91
+ rubyforge_project:
92
+ rubygems_version: 2.5.1
69
93
  signing_key:
70
- specification_version: 2
71
- summary: Provides POSIX tarchive management from Ruby programs.
72
- test_files:
73
- - tests/testall.rb
94
+ specification_version: 4
95
+ summary: "'archive-tar-minitar' has been deprecated; just install 'minitar'."
96
+ test_files: []
data/ChangeLog DELETED
@@ -1,14 +0,0 @@
1
- Revision history for Ruby library Archive::Tar::Minitar. Unless explicitly
2
- noted otherwise, all changes are produced by Austin Ziegler
3
- <austin@rubyforge.org>.
4
-
5
- == 0.5.2
6
- * Fixed a Ruby 1.9 compatibility error.
7
-
8
- == 0.5.1
9
- * Fixed a variable name error.
10
-
11
- == Archive::Tar::Minitar 0.5.0
12
- * Initial release. Does files and directories. Command does create, extract,
13
- * and list.
14
-
data/Install DELETED
@@ -1,6 +0,0 @@
1
- Installing this package is as simple as:
2
-
3
- % ruby install.rb
4
-
5
- Alternatively, you can use the RubyGem version of Archive::Tar::Minitar
6
- available as archive-tar-minitar-0.5.2.gem from the usual sources.
data/README DELETED
@@ -1,66 +0,0 @@
1
- Archive::Tar::Minitar README
2
- ============================
3
- Archive::Tar::Minitar is a pure-Ruby library and command-line utility that
4
- provides the ability to deal with POSIX tar(1) archive files. The
5
- implementation is based heavily on Mauricio Ferna'ndez's implementation in
6
- rpa-base, but has been reorganised to promote reuse in other projects.
7
-
8
- This release is version 0.5.2, offering a Ruby 1.9 compatibility bugfix over
9
- version 0.5.1. The library can only handle files and directories at this
10
- point. A future version will be expanded to handle symbolic links and hard
11
- links in a portable manner. The command line utility, minitar, can only create
12
- archives, extract from archives, and list archive contents.
13
-
14
- Using this library is easy. The simplest case is:
15
-
16
- require 'zlib'
17
- require 'archive/tar/minitar'
18
- include Archive::Tar
19
-
20
- # Packs everything that matches Find.find('tests')
21
- File.open('test.tar', 'wb') { |tar| Minitar.pack('tests', tar) }
22
- # Unpacks 'test.tar' to 'x', creating 'x' if necessary.
23
- Minitar.unpack('test.tar', 'x')
24
-
25
- A gzipped tar can be written with:
26
-
27
- tgz = Zlib::GzipWriter.new(File.open('test.tgz', 'wb'))
28
- # Warning: tgz will be closed!
29
- Minitar.pack('tests', tgz)
30
-
31
- tgz = Zlib::GzipReader.new(File.open('test.tgz', 'rb'))
32
- # Warning: tgz will be closed!
33
- Minitar.unpack(tgz, 'x')
34
-
35
- As the case above shows, one need not write to a file. However, it will
36
- sometimes require that one dive a little deeper into the API, as in the case
37
- of StringIO objects. Note that I'm not providing a block with Minitar::Output,
38
- as Minitar::Output#close automatically closes both the Output object and the
39
- wrapped data stream object.
40
-
41
- begin
42
- sgz = Zlib::GzipWriter.new(StringIO.new(""))
43
- tar = Output.new(sgz)
44
- Find.find('tests') do |entry|
45
- Minitar.pack_file(entry, tar)
46
- end
47
- ensure
48
- # Closes both tar and sgz.
49
- tar.close
50
- end
51
-
52
- Copyright
53
- =========
54
- # Copyright 2004 Mauricio Julio Ferna'ndez Pradier and Austin Ziegler
55
- #
56
- # This program is based on and incorporates parts of RPA::Package from
57
- # rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and has been
58
- # adapted to be more generic by Austin.
59
- #
60
- # 'minitar' contains an adaptation of Ruby/ProgressBar by Satoru
61
- # Takabayashi <satoru@namazu.org>, copyright 2001 - 2004.
62
- #
63
- # This program is free software. It may be redistributed and/or modified
64
- # under the terms of the GPL version 2 (or later) or Ruby's licence.
65
- #
66
- # $Id: README 213 2008-02-26 22:32:11Z austin $
data/Rakefile DELETED
@@ -1,113 +0,0 @@
1
- #! /usr/bin/env rake
2
- $LOAD_PATH.unshift('lib')
3
-
4
- require 'rubygems'
5
- require 'rake/gempackagetask'
6
- require 'rake/contrib/rubyforgepublisher'
7
- require 'archive/tar/minitar'
8
- require 'zlib'
9
-
10
- DISTDIR = "archive-tar-minitar-#{Archive::Tar::Minitar::VERSION}"
11
- TARDIST = "../#{DISTDIR}.tar.gz"
12
-
13
- DATE_RE = %r<(\d{4})[./-]?(\d{2})[./-]?(\d{2})(?:[\sT]?(\d{2})[:.]?(\d{2})[:.]?(\d{2})?)?>
14
-
15
- if ENV['RELEASE_DATE']
16
- year, month, day, hour, minute, second = DATE_RE.match(ENV['RELEASE_DATE']).captures
17
- year ||= 0
18
- month ||= 0
19
- day ||= 0
20
- hour ||= 0
21
- minute ||= 0
22
- second ||= 0
23
- ReleaseDate = Time.mktime(year, month, day, hour, minute, second)
24
- else
25
- ReleaseDate = nil
26
- end
27
-
28
- task :test do |t|
29
- require 'test/unit/testsuite'
30
- require 'test/unit/ui/console/testrunner'
31
-
32
- runner = Test::Unit::UI::Console::TestRunner
33
-
34
- $LOAD_PATH.unshift('tests')
35
- Dir['tests/tc_*.rb'].each do |testcase|
36
- load testcase
37
- end
38
-
39
- suite = Test::Unit::TestSuite.new
40
-
41
- ObjectSpace.each_object(Class) do |testcase|
42
- suite << testcase.suite if testcase < Test::Unit::TestCase
43
- end
44
-
45
- runner.run(suite)
46
- end
47
-
48
- spec = eval(File.read("archive-tar-minitar.gemspec"))
49
- desc "Build the RubyGem for Archive::Tar::Minitar."
50
- task :gem => [ :test ]
51
- Rake::GemPackageTask.new(spec) do |g|
52
- g.need_tar = false
53
- g.need_zip = false
54
- g.package_dir = ".."
55
- end
56
-
57
- desc "Build an Archive::Tar::Minitar .tar.gz distribution."
58
- task :tar => [ TARDIST ]
59
- file TARDIST do |t|
60
- current = File.basename(Dir.pwd)
61
- Dir.chdir("..") do
62
- begin
63
- files = Dir["#{current}/**/*"].select { |dd| dd !~ %r{(?:/CVS/?|~$)} }
64
- files.map! do |dd|
65
- ddnew = dd.gsub(/^#{current}/, DISTDIR)
66
- mtime = ReleaseDate || File.stat(dd).mtime
67
- if File.directory?(dd)
68
- { :name => ddnew, :mode => 0755, :dir => true, :mtime => mtime }
69
- else
70
- if dd =~ %r{bin/}
71
- mode = 0755
72
- else
73
- mode = 0644
74
- end
75
- data = File.read(dd)
76
- { :name => ddnew, :mode => mode, :data => data, :size => data.size,
77
- :mtime => mtime }
78
- end
79
- end
80
-
81
- ff = File.open(t.name.gsub(%r{^\.\./}o, ''), "wb")
82
- gz = Zlib::GzipWriter.new(ff)
83
- tw = Archive::Tar::Minitar::Writer.new(gz)
84
-
85
- files.each do |entry|
86
- if entry[:dir]
87
- tw.mkdir(entry[:name], entry)
88
- else
89
- tw.add_file_simple(entry[:name], entry) { |os| os.write(entry[:data]) }
90
- end
91
- end
92
- ensure
93
- tw.close if tw
94
- gz.close if gz
95
- end
96
- end
97
- end
98
- task TARDIST => [ :test ]
99
-
100
- def sign(file)
101
- sh %(gpg -ba #{file})
102
- end
103
-
104
- task :signtar => [ :tar ] do
105
- sign TARDIST
106
- end
107
- task :signgem => [ :gem ] do
108
- sign "../#{DISTDIR}.gem"
109
- end
110
-
111
- desc "Build everything."
112
- task :default => [ :signtar, :signgem ] do
113
- end
data/bin/minitar DELETED
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #--
3
- # Archive::Tar::Minitar 0.5.2
4
- # Copyright 2004 Mauricio Julio Ferna'ndez Pradier and Austin Ziegler
5
- #
6
- # This program is based on and incorporates parts of RPA::Package from
7
- # rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and has been
8
- # adapted to be more generic by Austin.
9
- #
10
- # It is licensed under the GNU General Public Licence or Ruby's licence.
11
- #
12
- # $Id: minitar 213 2008-02-26 22:32:11Z austin $
13
- #++
14
-
15
- # 1) Try to load Archive::Tar::Minitar from the gem.
16
- # 2) Try to load Archive::Tar::Minitar from $LOAD_PATH.
17
- begin
18
- require 'rubygems'
19
- require_gem 'archive-tar-minitar', '= 0.5.2'
20
- rescue LoadError
21
- nil
22
- end
23
-
24
- require 'archive/tar/minitar'
25
- require 'archive/tar/minitar/command'
26
-
27
- exit Archive::Tar::Minitar::Command.run(ARGV)
@@ -1,979 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #--
3
- # Archive::Tar::Minitar 0.5.2
4
- # Copyright 2004 Mauricio Julio Ferna'ndez Pradier and Austin Ziegler
5
- #
6
- # This program is based on and incorporates parts of RPA::Package from
7
- # rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and has been
8
- # adapted to be more generic by Austin.
9
- #
10
- # It is licensed under the GNU General Public Licence or Ruby's licence.
11
- #
12
- # $Id: minitar.rb 213 2008-02-26 22:32:11Z austin $
13
- #++
14
-
15
- module Archive; end
16
- module Archive::Tar; end
17
-
18
- # = Archive::Tar::PosixHeader
19
- # Implements the POSIX tar header as a Ruby class. The structure of
20
- # the POSIX tar header is:
21
- #
22
- # struct tarfile_entry_posix
23
- # { // pack/unpack
24
- # char name[100]; // ASCII (+ Z unless filled) a100/Z100
25
- # char mode[8]; // 0 padded, octal, null a8 /A8
26
- # char uid[8]; // ditto a8 /A8
27
- # char gid[8]; // ditto a8 /A8
28
- # char size[12]; // 0 padded, octal, null a12 /A12
29
- # char mtime[12]; // 0 padded, octal, null a12 /A12
30
- # char checksum[8]; // 0 padded, octal, null, space a8 /A8
31
- # char typeflag[1]; // see below a /a
32
- # char linkname[100]; // ASCII + (Z unless filled) a100/Z100
33
- # char magic[6]; // "ustar\0" a6 /A6
34
- # char version[2]; // "00" a2 /A2
35
- # char uname[32]; // ASCIIZ a32 /Z32
36
- # char gname[32]; // ASCIIZ a32 /Z32
37
- # char devmajor[8]; // 0 padded, octal, null a8 /A8
38
- # char devminor[8]; // 0 padded, octal, null a8 /A8
39
- # char prefix[155]; // ASCII (+ Z unless filled) a155/Z155
40
- # };
41
- #
42
- # The +typeflag+ may be one of the following known values:
43
- #
44
- # <tt>"0"</tt>:: Regular file. NULL should be treated as a synonym, for
45
- # compatibility purposes.
46
- # <tt>"1"</tt>:: Hard link.
47
- # <tt>"2"</tt>:: Symbolic link.
48
- # <tt>"3"</tt>:: Character device node.
49
- # <tt>"4"</tt>:: Block device node.
50
- # <tt>"5"</tt>:: Directory.
51
- # <tt>"6"</tt>:: FIFO node.
52
- # <tt>"7"</tt>:: Reserved.
53
- #
54
- # POSIX indicates that "A POSIX-compliant implementation must treat any
55
- # unrecognized typeflag value as a regular file."
56
- class Archive::Tar::PosixHeader
57
- FIELDS = %w(name mode uid gid size mtime checksum typeflag linkname) +
58
- %w(magic version uname gname devmajor devminor prefix)
59
-
60
- FIELDS.each { |field| attr_reader field.intern }
61
-
62
- HEADER_PACK_FORMAT = "a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155"
63
- HEADER_UNPACK_FORMAT = "Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155"
64
-
65
- # Creates a new PosixHeader from a data stream.
66
- def self.new_from_stream(stream)
67
- data = stream.read(512)
68
- fields = data.unpack(HEADER_UNPACK_FORMAT)
69
- name = fields.shift
70
- mode = fields.shift.oct
71
- uid = fields.shift.oct
72
- gid = fields.shift.oct
73
- size = fields.shift.oct
74
- mtime = fields.shift.oct
75
- checksum = fields.shift.oct
76
- typeflag = fields.shift
77
- linkname = fields.shift
78
- magic = fields.shift
79
- version = fields.shift.oct
80
- uname = fields.shift
81
- gname = fields.shift
82
- devmajor = fields.shift.oct
83
- devminor = fields.shift.oct
84
- prefix = fields.shift
85
-
86
- empty = (data == "\0" * 512)
87
-
88
- new(:name => name, :mode => mode, :uid => uid, :gid => gid,
89
- :size => size, :mtime => mtime, :checksum => checksum,
90
- :typeflag => typeflag, :magic => magic, :version => version,
91
- :uname => uname, :gname => gname, :devmajor => devmajor,
92
- :devminor => devminor, :prefix => prefix, :empty => empty)
93
- end
94
-
95
- # Creates a new PosixHeader. A PosixHeader cannot be created unless the
96
- # #name, #size, #prefix, and #mode are provided.
97
- def initialize(vals)
98
- unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
99
- raise ArgumentError
100
- end
101
-
102
- vals[:mtime] ||= 0
103
- vals[:checksum] ||= ""
104
- vals[:typeflag] ||= "0"
105
- vals[:magic] ||= "ustar"
106
- vals[:version] ||= "00"
107
-
108
- FIELDS.each do |field|
109
- instance_variable_set("@#{field}", vals[field.intern])
110
- end
111
- @empty = vals[:empty]
112
- end
113
-
114
- def empty?
115
- @empty
116
- end
117
-
118
- def to_s
119
- update_checksum
120
- header(@checksum)
121
- end
122
-
123
- # Update the checksum field.
124
- def update_checksum
125
- hh = header(" " * 8)
126
- @checksum = oct(calculate_checksum(hh), 6)
127
- end
128
-
129
- private
130
- def oct(num, len)
131
- if num.nil?
132
- "\0" * (len + 1)
133
- else
134
- "%0#{len}o" % num
135
- end
136
- end
137
-
138
- def calculate_checksum(hdr)
139
- hdr.unpack("C*").inject { |aa, bb| aa + bb }
140
- end
141
-
142
- def header(chksum)
143
- arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
144
- oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
145
- uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
146
- str = arr.pack(HEADER_PACK_FORMAT)
147
- str + "\0" * ((512 - str.size) % 512)
148
- end
149
- end
150
-
151
- require 'fileutils'
152
- require 'find'
153
-
154
- # = Archive::Tar::Minitar 0.5.2
155
- # Archive::Tar::Minitar is a pure-Ruby library and command-line
156
- # utility that provides the ability to deal with POSIX tar(1) archive
157
- # files. The implementation is based heavily on Mauricio Ferna'ndez's
158
- # implementation in rpa-base, but has been reorganised to promote
159
- # reuse in other projects.
160
- #
161
- # This tar class performs a subset of all tar (POSIX tape archive)
162
- # operations. We can only deal with typeflags 0, 1, 2, and 5 (see
163
- # Archive::Tar::PosixHeader). All other typeflags will be treated as
164
- # normal files.
165
- #
166
- # NOTE::: support for typeflags 1 and 2 is not yet implemented in this
167
- # version.
168
- #
169
- # This release is version 0.5.2. The library can only handle files and
170
- # directories at this point. A future version will be expanded to
171
- # handle symbolic links and hard links in a portable manner. The
172
- # command line utility, minitar, can only create archives, extract
173
- # from archives, and list archive contents.
174
- #
175
- # == Synopsis
176
- # Using this library is easy. The simplest case is:
177
- #
178
- # require 'zlib'
179
- # require 'archive/tar/minitar'
180
- # include Archive::Tar
181
- #
182
- # # Packs everything that matches Find.find('tests')
183
- # File.open('test.tar', 'wb') { |tar| Minitar.pack('tests', tar) }
184
- # # Unpacks 'test.tar' to 'x', creating 'x' if necessary.
185
- # Minitar.unpack('test.tar', 'x')
186
- #
187
- # A gzipped tar can be written with:
188
- #
189
- # tgz = Zlib::GzipWriter.new(File.open('test.tgz', 'wb'))
190
- # # Warning: tgz will be closed!
191
- # Minitar.pack('tests', tgz)
192
- #
193
- # tgz = Zlib::GzipReader.new(File.open('test.tgz', 'rb'))
194
- # # Warning: tgz will be closed!
195
- # Minitar.unpack(tgz, 'x')
196
- #
197
- # As the case above shows, one need not write to a file. However, it
198
- # will sometimes require that one dive a little deeper into the API,
199
- # as in the case of StringIO objects. Note that I'm not providing a
200
- # block with Minitar::Output, as Minitar::Output#close automatically
201
- # closes both the Output object and the wrapped data stream object.
202
- #
203
- # begin
204
- # sgz = Zlib::GzipWriter.new(StringIO.new(""))
205
- # tar = Output.new(sgz)
206
- # Find.find('tests') do |entry|
207
- # Minitar.pack_file(entry, tar)
208
- # end
209
- # ensure
210
- # # Closes both tar and sgz.
211
- # tar.close
212
- # end
213
- #
214
- # == Copyright
215
- # Copyright 2004 Mauricio Julio Ferna'ndez Pradier and Austin Ziegler
216
- #
217
- # This program is based on and incorporates parts of RPA::Package from
218
- # rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and
219
- # has been adapted to be more generic by Austin.
220
- #
221
- # 'minitar' contains an adaptation of Ruby/ProgressBar by Satoru
222
- # Takabayashi <satoru@namazu.org>, copyright 2001 - 2004.
223
- #
224
- # This program is free software. It may be redistributed and/or
225
- # modified under the terms of the GPL version 2 (or later) or Ruby's
226
- # licence.
227
- module Archive::Tar::Minitar
228
- VERSION = "0.5.2"
229
-
230
- # The exception raised when a wrapped data stream class is expected to
231
- # respond to #rewind or #pos but does not.
232
- class NonSeekableStream < StandardError; end
233
- # The exception raised when a block is required for proper operation of
234
- # the method.
235
- class BlockRequired < ArgumentError; end
236
- # The exception raised when operations are performed on a stream that has
237
- # previously been closed.
238
- class ClosedStream < StandardError; end
239
- # The exception raised when a filename exceeds 256 bytes in length,
240
- # the maximum supported by the standard Tar format.
241
- class FileNameTooLong < StandardError; end
242
- # The exception raised when a data stream ends before the amount of data
243
- # expected in the archive's PosixHeader.
244
- class UnexpectedEOF < StandardError; end
245
-
246
- # The class that writes a tar format archive to a data stream.
247
- class Writer
248
- # A stream wrapper that can only be written to. Any attempt to read
249
- # from this restricted stream will result in a NameError being thrown.
250
- class RestrictedStream
251
- def initialize(anIO)
252
- @io = anIO
253
- end
254
-
255
- def write(data)
256
- @io.write(data)
257
- end
258
- end
259
-
260
- # A RestrictedStream that also has a size limit.
261
- class BoundedStream < Archive::Tar::Minitar::Writer::RestrictedStream
262
- # The exception raised when the user attempts to write more data to
263
- # a BoundedStream than has been allocated.
264
- class FileOverflow < RuntimeError; end
265
-
266
- # The maximum number of bytes that may be written to this data
267
- # stream.
268
- attr_reader :limit
269
- # The current total number of bytes written to this data stream.
270
- attr_reader :written
271
-
272
- def initialize(io, limit)
273
- @io = io
274
- @limit = limit
275
- @written = 0
276
- end
277
-
278
- def write(data)
279
- raise FileOverflow if (data.size + @written) > @limit
280
- @io.write(data)
281
- @written += data.size
282
- data.size
283
- end
284
- end
285
-
286
- # With no associated block, +Writer::open+ is a synonym for
287
- # +Writer::new+. If the optional code block is given, it will be
288
- # passed the new _writer_ as an argument and the Writer object will
289
- # automatically be closed when the block terminates. In this instance,
290
- # +Writer::open+ returns the value of the block.
291
- def self.open(anIO)
292
- writer = Writer.new(anIO)
293
-
294
- return writer unless block_given?
295
-
296
- begin
297
- res = yield writer
298
- ensure
299
- writer.close
300
- end
301
-
302
- res
303
- end
304
-
305
- # Creates and returns a new Writer object.
306
- def initialize(anIO)
307
- @io = anIO
308
- @closed = false
309
- end
310
-
311
- # Adds a file to the archive as +name+. +opts+ must contain the
312
- # following values:
313
- #
314
- # <tt>:mode</tt>:: The Unix file permissions mode value.
315
- # <tt>:size</tt>:: The size, in bytes.
316
- #
317
- # +opts+ may contain the following values:
318
- #
319
- # <tt>:uid</tt>: The Unix file owner user ID number.
320
- # <tt>:gid</tt>: The Unix file owner group ID number.
321
- # <tt>:mtime</tt>:: The *integer* modification time value.
322
- #
323
- # It will not be possible to add more than <tt>opts[:size]</tt> bytes
324
- # to the file.
325
- def add_file_simple(name, opts = {}) # :yields BoundedStream:
326
- raise Archive::Tar::Minitar::BlockRequired unless block_given?
327
- raise Archive::Tar::ClosedStream if @closed
328
-
329
- name, prefix = split_name(name)
330
-
331
- header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime],
332
- :size => opts[:size], :gid => opts[:gid], :uid => opts[:uid],
333
- :prefix => prefix }
334
- header = Archive::Tar::PosixHeader.new(header).to_s
335
- @io.write(header)
336
-
337
- os = BoundedStream.new(@io, opts[:size])
338
- yield os
339
- # FIXME: what if an exception is raised in the block?
340
-
341
- min_padding = opts[:size] - os.written
342
- @io.write("\0" * min_padding)
343
- remainder = (512 - (opts[:size] % 512)) % 512
344
- @io.write("\0" * remainder)
345
- end
346
-
347
- # Adds a file to the archive as +name+. +opts+ must contain the
348
- # following value:
349
- #
350
- # <tt>:mode</tt>:: The Unix file permissions mode value.
351
- #
352
- # +opts+ may contain the following values:
353
- #
354
- # <tt>:uid</tt>: The Unix file owner user ID number.
355
- # <tt>:gid</tt>: The Unix file owner group ID number.
356
- # <tt>:mtime</tt>:: The *integer* modification time value.
357
- #
358
- # The file's size will be determined from the amount of data written
359
- # to the stream.
360
- #
361
- # For #add_file to be used, the Archive::Tar::Minitar::Writer must be
362
- # wrapping a stream object that is seekable (e.g., it responds to
363
- # #pos=). Otherwise, #add_file_simple must be used.
364
- #
365
- # +opts+ may be modified during the writing to the stream.
366
- def add_file(name, opts = {}) # :yields RestrictedStream, +opts+:
367
- raise Archive::Tar::Minitar::BlockRequired unless block_given?
368
- raise Archive::Tar::Minitar::ClosedStream if @closed
369
- raise Archive::Tar::Minitar::NonSeekableStream unless @io.respond_to?(:pos=)
370
-
371
- name, prefix = split_name(name)
372
- init_pos = @io.pos
373
- @io.write("\0" * 512) # placeholder for the header
374
-
375
- yield RestrictedStream.new(@io), opts
376
- # FIXME: what if an exception is raised in the block?
377
-
378
- size = @io.pos - (init_pos + 512)
379
- remainder = (512 - (size % 512)) % 512
380
- @io.write("\0" * remainder)
381
-
382
- final_pos = @io.pos
383
- @io.pos = init_pos
384
-
385
- header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime],
386
- :size => size, :gid => opts[:gid], :uid => opts[:uid],
387
- :prefix => prefix }
388
- header = Archive::Tar::PosixHeader.new(header).to_s
389
- @io.write(header)
390
- @io.pos = final_pos
391
- end
392
-
393
- # Creates a directory in the tar.
394
- def mkdir(name, opts = {})
395
- raise ClosedStream if @closed
396
- name, prefix = split_name(name)
397
- header = { :name => name, :mode => opts[:mode], :typeflag => "5",
398
- :size => 0, :gid => opts[:gid], :uid => opts[:uid],
399
- :mtime => opts[:mtime], :prefix => prefix }
400
- header = Archive::Tar::PosixHeader.new(header).to_s
401
- @io.write(header)
402
- nil
403
- end
404
-
405
- # Passes the #flush method to the wrapped stream, used for buffered
406
- # streams.
407
- def flush
408
- raise ClosedStream if @closed
409
- @io.flush if @io.respond_to?(:flush)
410
- end
411
-
412
- # Closes the Writer.
413
- def close
414
- return if @closed
415
- @io.write("\0" * 1024)
416
- @closed = true
417
- end
418
-
419
- private
420
- def split_name(name)
421
- raise FileNameTooLong if name.size > 256
422
- if name.size <= 100
423
- prefix = ""
424
- else
425
- parts = name.split(/\//)
426
- newname = parts.pop
427
-
428
- nxt = ""
429
-
430
- loop do
431
- nxt = parts.pop
432
- break if newname.size + 1 + nxt.size > 100
433
- newname = "#{nxt}/#{newname}"
434
- end
435
-
436
- prefix = (parts + [nxt]).join("/")
437
-
438
- name = newname
439
-
440
- raise FileNameTooLong if name.size > 100 || prefix.size > 155
441
- end
442
- return name, prefix
443
- end
444
- end
445
-
446
- # The class that reads a tar format archive from a data stream. The data
447
- # stream may be sequential or random access, but certain features only work
448
- # with random access data streams.
449
- class Reader
450
- # This marks the EntryStream closed for reading without closing the
451
- # actual data stream.
452
- module InvalidEntryStream
453
- def read(len = nil); raise ClosedStream; end
454
- def getc; raise ClosedStream; end
455
- def rewind; raise ClosedStream; end
456
- end
457
-
458
- # EntryStreams are pseudo-streams on top of the main data stream.
459
- class EntryStream
460
- Archive::Tar::PosixHeader::FIELDS.each do |field|
461
- attr_reader field.intern
462
- end
463
-
464
- def initialize(header, anIO)
465
- @io = anIO
466
- @name = header.name
467
- @mode = header.mode
468
- @uid = header.uid
469
- @gid = header.gid
470
- @size = header.size
471
- @mtime = header.mtime
472
- @checksum = header.checksum
473
- @typeflag = header.typeflag
474
- @linkname = header.linkname
475
- @magic = header.magic
476
- @version = header.version
477
- @uname = header.uname
478
- @gname = header.gname
479
- @devmajor = header.devmajor
480
- @devminor = header.devminor
481
- @prefix = header.prefix
482
- @read = 0
483
- @orig_pos = @io.pos
484
- end
485
-
486
- # Reads +len+ bytes (or all remaining data) from the entry. Returns
487
- # +nil+ if there is no more data to read.
488
- def read(len = nil)
489
- return nil if @read >= @size
490
- len ||= @size - @read
491
- max_read = [len, @size - @read].min
492
- ret = @io.read(max_read)
493
- @read += ret.size
494
- ret
495
- end
496
-
497
- # Reads one byte from the entry. Returns +nil+ if there is no more data
498
- # to read.
499
- def getc
500
- return nil if @read >= @size
501
- ret = @io.getc
502
- @read += 1 if ret
503
- ret
504
- end
505
-
506
- # Returns +true+ if the entry represents a directory.
507
- def directory?
508
- @typeflag == "5"
509
- end
510
- alias_method :directory, :directory?
511
-
512
- # Returns +true+ if the entry represents a plain file.
513
- def file?
514
- @typeflag == "0"
515
- end
516
- alias_method :file, :file?
517
-
518
- # Returns +true+ if the current read pointer is at the end of the
519
- # EntryStream data.
520
- def eof?
521
- @read >= @size
522
- end
523
-
524
- # Returns the current read pointer in the EntryStream.
525
- def pos
526
- @read
527
- end
528
-
529
- # Sets the current read pointer to the beginning of the EntryStream.
530
- def rewind
531
- raise NonSeekableStream unless @io.respond_to?(:pos=)
532
- @io.pos = @orig_pos
533
- @read = 0
534
- end
535
-
536
- def bytes_read
537
- @read
538
- end
539
-
540
- # Returns the full and proper name of the entry.
541
- def full_name
542
- if @prefix != ""
543
- File.join(@prefix, @name)
544
- else
545
- @name
546
- end
547
- end
548
-
549
- # Closes the entry.
550
- def close
551
- invalidate
552
- end
553
-
554
- private
555
- def invalidate
556
- extend InvalidEntryStream
557
- end
558
- end
559
-
560
- # With no associated block, +Reader::open+ is a synonym for
561
- # +Reader::new+. If the optional code block is given, it will be passed
562
- # the new _writer_ as an argument and the Reader object will
563
- # automatically be closed when the block terminates. In this instance,
564
- # +Reader::open+ returns the value of the block.
565
- def self.open(anIO)
566
- reader = Reader.new(anIO)
567
-
568
- return reader unless block_given?
569
-
570
- begin
571
- res = yield reader
572
- ensure
573
- reader.close
574
- end
575
-
576
- res
577
- end
578
-
579
- # Creates and returns a new Reader object.
580
- def initialize(anIO)
581
- @io = anIO
582
- @init_pos = anIO.pos
583
- end
584
-
585
- # Iterates through each entry in the data stream.
586
- def each(&block)
587
- each_entry(&block)
588
- end
589
-
590
- # Resets the read pointer to the beginning of data stream. Do not call
591
- # this during a #each or #each_entry iteration. This only works with
592
- # random access data streams that respond to #rewind and #pos.
593
- def rewind
594
- if @init_pos == 0
595
- raise NonSeekableStream unless @io.respond_to?(:rewind)
596
- @io.rewind
597
- else
598
- raise NonSeekableStream unless @io.respond_to?(:pos=)
599
- @io.pos = @init_pos
600
- end
601
- end
602
-
603
- # Iterates through each entry in the data stream.
604
- def each_entry
605
- loop do
606
- return if @io.eof?
607
-
608
- header = Archive::Tar::PosixHeader.new_from_stream(@io)
609
- return if header.empty?
610
-
611
- entry = EntryStream.new(header, @io)
612
- size = entry.size
613
-
614
- yield entry
615
-
616
- skip = (512 - (size % 512)) % 512
617
-
618
- if @io.respond_to?(:seek)
619
- # avoid reading...
620
- @io.seek(size - entry.bytes_read, IO::SEEK_CUR)
621
- else
622
- pending = size - entry.bytes_read
623
- while pending > 0
624
- bread = @io.read([pending, 4096].min).size
625
- raise UnexpectedEOF if @io.eof?
626
- pending -= bread
627
- end
628
- end
629
- @io.read(skip) # discard trailing zeros
630
- # make sure nobody can use #read, #getc or #rewind anymore
631
- entry.close
632
- end
633
- end
634
-
635
- def close
636
- end
637
- end
638
-
639
- # Wraps a Archive::Tar::Minitar::Reader with convenience methods and
640
- # wrapped stream management; Input only works with random access data
641
- # streams. See Input::new for details.
642
- class Input
643
- include Enumerable
644
-
645
- # With no associated block, +Input::open+ is a synonym for
646
- # +Input::new+. If the optional code block is given, it will be passed
647
- # the new _writer_ as an argument and the Input object will
648
- # automatically be closed when the block terminates. In this instance,
649
- # +Input::open+ returns the value of the block.
650
- def self.open(input)
651
- stream = Input.new(input)
652
- return stream unless block_given?
653
-
654
- begin
655
- res = yield stream
656
- ensure
657
- stream.close
658
- end
659
-
660
- res
661
- end
662
-
663
- # Creates a new Input object. If +input+ is a stream object that responds
664
- # to #read), then it will simply be wrapped. Otherwise, one will be
665
- # created and opened using Kernel#open. When Input#close is called, the
666
- # stream object wrapped will be closed.
667
- def initialize(input)
668
- if input.respond_to?(:read)
669
- @io = input
670
- else
671
- @io = open(input, "rb")
672
- end
673
- @tarreader = Archive::Tar::Minitar::Reader.new(@io)
674
- end
675
-
676
- # Iterates through each entry and rewinds to the beginning of the stream
677
- # when finished.
678
- def each(&block)
679
- @tarreader.each { |entry| yield entry }
680
- ensure
681
- @tarreader.rewind
682
- end
683
-
684
- # Extracts the current +entry+ to +destdir+. If a block is provided, it
685
- # yields an +action+ Symbol, the full name of the file being extracted
686
- # (+name+), and a Hash of statistical information (+stats+).
687
- #
688
- # The +action+ will be one of:
689
- # <tt>:dir</tt>:: The +entry+ is a directory.
690
- # <tt>:file_start</tt>:: The +entry+ is a file; the extract of the
691
- # file is just beginning.
692
- # <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract
693
- # of the +entry+.
694
- # <tt>:file_done</tt>:: Yielded when the +entry+ is completed.
695
- #
696
- # The +stats+ hash contains the following keys:
697
- # <tt>:current</tt>:: The current total number of bytes read in the
698
- # +entry+.
699
- # <tt>:currinc</tt>:: The current number of bytes read in this read
700
- # cycle.
701
- # <tt>:entry</tt>:: The entry being extracted; this is a
702
- # Reader::EntryStream, with all methods thereof.
703
- def extract_entry(destdir, entry) # :yields action, name, stats:
704
- stats = {
705
- :current => 0,
706
- :currinc => 0,
707
- :entry => entry
708
- }
709
-
710
- if entry.directory?
711
- dest = File.join(destdir, entry.full_name)
712
-
713
- yield :dir, entry.full_name, stats if block_given?
714
-
715
- if Archive::Tar::Minitar.dir?(dest)
716
- begin
717
- FileUtils.chmod(entry.mode, dest)
718
- rescue Exception
719
- nil
720
- end
721
- else
722
- FileUtils.mkdir_p(dest, :mode => entry.mode)
723
- FileUtils.chmod(entry.mode, dest)
724
- end
725
-
726
- fsync_dir(dest)
727
- fsync_dir(File.join(dest, ".."))
728
- return
729
- else # it's a file
730
- destdir = File.join(destdir, File.dirname(entry.full_name))
731
- FileUtils.mkdir_p(destdir, :mode => 0755)
732
-
733
- destfile = File.join(destdir, File.basename(entry.full_name))
734
- FileUtils.chmod(0600, destfile) rescue nil # Errno::ENOENT
735
-
736
- yield :file_start, entry.full_name, stats if block_given?
737
-
738
- File.open(destfile, "wb", entry.mode) do |os|
739
- loop do
740
- data = entry.read(4096)
741
- break unless data
742
-
743
- stats[:currinc] = os.write(data)
744
- stats[:current] += stats[:currinc]
745
-
746
- yield :file_progress, entry.full_name, stats if block_given?
747
- end
748
- os.fsync
749
- end
750
-
751
- FileUtils.chmod(entry.mode, destfile)
752
- fsync_dir(File.dirname(destfile))
753
- fsync_dir(File.join(File.dirname(destfile), ".."))
754
-
755
- yield :file_done, entry.full_name, stats if block_given?
756
- end
757
- end
758
-
759
- # Returns the Reader object for direct access.
760
- def tar
761
- @tarreader
762
- end
763
-
764
- # Closes the Reader object and the wrapped data stream.
765
- def close
766
- @io.close
767
- @tarreader.close
768
- end
769
-
770
- private
771
- def fsync_dir(dirname)
772
- # make sure this hits the disc
773
- dir = open(dirname, 'rb')
774
- dir.fsync
775
- rescue # ignore IOError if it's an unpatched (old) Ruby
776
- nil
777
- ensure
778
- dir.close if dir rescue nil
779
- end
780
- end
781
-
782
- # Wraps a Archive::Tar::Minitar::Writer with convenience methods and
783
- # wrapped stream management; Output only works with random access data
784
- # streams. See Output::new for details.
785
- class Output
786
- # With no associated block, +Output::open+ is a synonym for
787
- # +Output::new+. If the optional code block is given, it will be passed
788
- # the new _writer_ as an argument and the Output object will
789
- # automatically be closed when the block terminates. In this instance,
790
- # +Output::open+ returns the value of the block.
791
- def self.open(output)
792
- stream = Output.new(output)
793
- return stream unless block_given?
794
-
795
- begin
796
- res = yield stream
797
- ensure
798
- stream.close
799
- end
800
-
801
- res
802
- end
803
-
804
- # Creates a new Output object. If +output+ is a stream object that
805
- # responds to #read), then it will simply be wrapped. Otherwise, one will
806
- # be created and opened using Kernel#open. When Output#close is called,
807
- # the stream object wrapped will be closed.
808
- def initialize(output)
809
- if output.respond_to?(:write)
810
- @io = output
811
- else
812
- @io = ::File.open(output, "wb")
813
- end
814
- @tarwriter = Archive::Tar::Minitar::Writer.new(@io)
815
- end
816
-
817
- # Returns the Writer object for direct access.
818
- def tar
819
- @tarwriter
820
- end
821
-
822
- # Closes the Writer object and the wrapped data stream.
823
- def close
824
- @tarwriter.close
825
- @io.close
826
- end
827
- end
828
-
829
- class << self
830
- # Tests if +path+ refers to a directory. Fixes an apparently
831
- # corrupted <tt>stat()</tt> call on Windows.
832
- def dir?(path)
833
- File.directory?((path[-1] == ?/) ? path : "#{path}/")
834
- end
835
-
836
- # A convenience method for wrapping Archive::Tar::Minitar::Input.open
837
- # (mode +r+) and Archive::Tar::Minitar::Output.open (mode +w+). No other
838
- # modes are currently supported.
839
- def open(dest, mode = "r", &block)
840
- case mode
841
- when "r"
842
- Input.open(dest, &block)
843
- when "w"
844
- Output.open(dest, &block)
845
- else
846
- raise "Unknown open mode for Archive::Tar::Minitar.open."
847
- end
848
- end
849
-
850
- # A convenience method to packs the file provided. +entry+ may either be
851
- # a filename (in which case various values for the file (see below) will
852
- # be obtained from <tt>File#stat(entry)</tt> or a Hash with the fields:
853
- #
854
- # <tt>:name</tt>:: The filename to be packed into the tarchive.
855
- # *REQUIRED*.
856
- # <tt>:mode</tt>:: The mode to be applied.
857
- # <tt>:uid</tt>:: The user owner of the file. (Ignored on Windows.)
858
- # <tt>:gid</tt>:: The group owner of the file. (Ignored on Windows.)
859
- # <tt>:mtime</tt>:: The modification Time of the file.
860
- #
861
- # During packing, if a block is provided, #pack_file yields an +action+
862
- # Symol, the full name of the file being packed, and a Hash of
863
- # statistical information, just as with
864
- # Archive::Tar::Minitar::Input#extract_entry.
865
- #
866
- # The +action+ will be one of:
867
- # <tt>:dir</tt>:: The +entry+ is a directory.
868
- # <tt>:file_start</tt>:: The +entry+ is a file; the extract of the
869
- # file is just beginning.
870
- # <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract
871
- # of the +entry+.
872
- # <tt>:file_done</tt>:: Yielded when the +entry+ is completed.
873
- #
874
- # The +stats+ hash contains the following keys:
875
- # <tt>:current</tt>:: The current total number of bytes read in the
876
- # +entry+.
877
- # <tt>:currinc</tt>:: The current number of bytes read in this read
878
- # cycle.
879
- # <tt>:name</tt>:: The filename to be packed into the tarchive.
880
- # *REQUIRED*.
881
- # <tt>:mode</tt>:: The mode to be applied.
882
- # <tt>:uid</tt>:: The user owner of the file. (+nil+ on Windows.)
883
- # <tt>:gid</tt>:: The group owner of the file. (+nil+ on Windows.)
884
- # <tt>:mtime</tt>:: The modification Time of the file.
885
- def pack_file(entry, outputter) #:yields action, name, stats:
886
- outputter = outputter.tar if outputter.kind_of?(Archive::Tar::Minitar::Output)
887
-
888
- stats = {}
889
-
890
- if entry.kind_of?(Hash)
891
- name = entry[:name]
892
-
893
- entry.each { |kk, vv| stats[kk] = vv unless vv.nil? }
894
- else
895
- name = entry
896
- end
897
-
898
- name = name.sub(%r{\./}, '')
899
- stat = File.stat(name)
900
- stats[:mode] ||= stat.mode
901
- stats[:mtime] ||= stat.mtime
902
- stats[:size] = stat.size
903
-
904
- if RUBY_PLATFORM =~ /win32/
905
- stats[:uid] = nil
906
- stats[:gid] = nil
907
- else
908
- stats[:uid] ||= stat.uid
909
- stats[:gid] ||= stat.gid
910
- end
911
-
912
- case
913
- when File.file?(name)
914
- outputter.add_file_simple(name, stats) do |os|
915
- stats[:current] = 0
916
- yield :file_start, name, stats if block_given?
917
- File.open(name, "rb") do |ff|
918
- until ff.eof?
919
- stats[:currinc] = os.write(ff.read(4096))
920
- stats[:current] += stats[:currinc]
921
- yield :file_progress, name, stats if block_given?
922
- end
923
- end
924
- yield :file_done, name, stats if block_given?
925
- end
926
- when dir?(name)
927
- yield :dir, name, stats if block_given?
928
- outputter.mkdir(name, stats)
929
- else
930
- raise "Don't yet know how to pack this type of file."
931
- end
932
- end
933
-
934
- # A convenience method to pack files specified by +src+ into +dest+. If
935
- # +src+ is an Array, then each file detailed therein will be packed into
936
- # the resulting Archive::Tar::Minitar::Output stream; if +recurse_dirs+
937
- # is true, then directories will be recursed.
938
- #
939
- # If +src+ is an Array, it will be treated as the argument to Find.find;
940
- # all files matching will be packed.
941
- def pack(src, dest, recurse_dirs = true, &block)
942
- Output.open(dest) do |outp|
943
- if src.kind_of?(Array)
944
- src.each do |entry|
945
- pack_file(entry, outp, &block)
946
- if dir?(entry) and recurse_dirs
947
- Dir["#{entry}/**/**"].each do |ee|
948
- pack_file(ee, outp, &block)
949
- end
950
- end
951
- end
952
- else
953
- Find.find(src) do |entry|
954
- pack_file(entry, outp, &block)
955
- end
956
- end
957
- end
958
- end
959
-
960
- # A convenience method to unpack files from +src+ into the directory
961
- # specified by +dest+. Only those files named explicitly in +files+
962
- # will be extracted.
963
- def unpack(src, dest, files = [], &block)
964
- Input.open(src) do |inp|
965
- if File.exist?(dest) and (not dir?(dest))
966
- raise "Can't unpack to a non-directory."
967
- elsif not File.exist?(dest)
968
- FileUtils.mkdir_p(dest)
969
- end
970
-
971
- inp.each do |entry|
972
- if files.empty? or files.include?(entry.full_name)
973
- inp.extract_entry(dest, entry, &block)
974
- end
975
- end
976
- end
977
- end
978
- end
979
- end