superp-rubyzip 0.1.0
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 +15 -0
- data/NEWS +176 -0
- data/README.md +175 -0
- data/Rakefile +13 -0
- data/TODO +16 -0
- data/lib/zip.rb +52 -0
- data/lib/zip/central_directory.rb +135 -0
- data/lib/zip/compressor.rb +10 -0
- data/lib/zip/constants.rb +61 -0
- data/lib/zip/decompressor.rb +13 -0
- data/lib/zip/deflater.rb +29 -0
- data/lib/zip/dos_time.rb +49 -0
- data/lib/zip/entry.rb +609 -0
- data/lib/zip/entry_set.rb +86 -0
- data/lib/zip/errors.rb +8 -0
- data/lib/zip/extra_field.rb +90 -0
- data/lib/zip/extra_field/generic.rb +43 -0
- data/lib/zip/extra_field/universal_time.rb +47 -0
- data/lib/zip/extra_field/unix.rb +39 -0
- data/lib/zip/file.rb +419 -0
- data/lib/zip/filesystem.rb +622 -0
- data/lib/zip/inflater.rb +65 -0
- data/lib/zip/input_stream.rb +145 -0
- data/lib/zip/ioextras.rb +186 -0
- data/lib/zip/null_compressor.rb +15 -0
- data/lib/zip/null_decompressor.rb +27 -0
- data/lib/zip/null_input_stream.rb +9 -0
- data/lib/zip/output_stream.rb +175 -0
- data/lib/zip/pass_thru_compressor.rb +23 -0
- data/lib/zip/pass_thru_decompressor.rb +41 -0
- data/lib/zip/streamable_directory.rb +15 -0
- data/lib/zip/streamable_stream.rb +47 -0
- data/lib/zip/version.rb +3 -0
- data/samples/example.rb +91 -0
- data/samples/example_filesystem.rb +33 -0
- data/samples/example_recursive.rb +49 -0
- data/samples/gtkRubyzip.rb +86 -0
- data/samples/qtzip.rb +101 -0
- data/samples/write_simple.rb +13 -0
- data/samples/zipfind.rb +74 -0
- metadata +82 -0
data/lib/zip/inflater.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module Zip
|
2
|
+
class Inflater < Decompressor #:nodoc:all
|
3
|
+
def initialize(input_stream)
|
4
|
+
super
|
5
|
+
@zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
6
|
+
@output_buffer = ''
|
7
|
+
@has_returned_empty_string = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def sysread(number_of_bytes = nil, buf = nil)
|
11
|
+
readEverything = number_of_bytes.nil?
|
12
|
+
while (readEverything || @output_buffer.bytesize < number_of_bytes)
|
13
|
+
break if internal_input_finished?
|
14
|
+
@output_buffer << internal_produce_input(buf)
|
15
|
+
end
|
16
|
+
return value_when_finished if @output_buffer.bytesize == 0 && input_finished?
|
17
|
+
end_index = number_of_bytes.nil? ? @output_buffer.bytesize : number_of_bytes
|
18
|
+
@output_buffer.slice!(0...end_index)
|
19
|
+
end
|
20
|
+
|
21
|
+
def produce_input
|
22
|
+
if (@output_buffer.empty?)
|
23
|
+
internal_produce_input
|
24
|
+
else
|
25
|
+
@output_buffer.slice!(0...(@output_buffer.length))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# to be used with produce_input, not read (as read may still have more data cached)
|
30
|
+
# is data cached anywhere other than @outputBuffer? the comment above may be wrong
|
31
|
+
def input_finished?
|
32
|
+
@output_buffer.empty? && internal_input_finished?
|
33
|
+
end
|
34
|
+
|
35
|
+
alias :eof :input_finished?
|
36
|
+
alias :eof? :input_finished?
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def internal_produce_input(buf = nil)
|
41
|
+
retried = 0
|
42
|
+
begin
|
43
|
+
@zlib_inflater.inflate(@input_stream.read(Decompressor::CHUNK_SIZE, buf))
|
44
|
+
rescue Zlib::BufError
|
45
|
+
raise if retried >= 5 # how many times should we retry?
|
46
|
+
retried += 1
|
47
|
+
retry
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def internal_input_finished?
|
52
|
+
@zlib_inflater.finished?
|
53
|
+
end
|
54
|
+
|
55
|
+
def value_when_finished # mimic behaviour of ruby File object.
|
56
|
+
return if @has_returned_empty_string
|
57
|
+
@has_returned_empty_string = true
|
58
|
+
''
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
64
|
+
# rubyzip is free software; you can redistribute it and/or
|
65
|
+
# modify it under the terms of the ruby license.
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Zip
|
2
|
+
# ZipInputStream is the basic class for reading zip entries in a
|
3
|
+
# zip file. It is possible to create a ZipInputStream object directly,
|
4
|
+
# passing the zip file name to the constructor, but more often than not
|
5
|
+
# the ZipInputStream will be obtained from a ZipFile (perhaps using the
|
6
|
+
# ZipFileSystem interface) object for a particular entry in the zip
|
7
|
+
# archive.
|
8
|
+
#
|
9
|
+
# A ZipInputStream inherits IOExtras::AbstractInputStream in order
|
10
|
+
# to provide an IO-like interface for reading from a single zip
|
11
|
+
# entry. Beyond methods for mimicking an IO-object it contains
|
12
|
+
# the method get_next_entry for iterating through the entries of
|
13
|
+
# an archive. get_next_entry returns a ZipEntry object that describes
|
14
|
+
# the zip entry the ZipInputStream is currently reading from.
|
15
|
+
#
|
16
|
+
# Example that creates a zip archive with ZipOutputStream and reads it
|
17
|
+
# back again with a ZipInputStream.
|
18
|
+
#
|
19
|
+
# require 'zip/zip'
|
20
|
+
#
|
21
|
+
# Zip::ZipOutputStream::open("my.zip") {
|
22
|
+
# |io|
|
23
|
+
#
|
24
|
+
# io.put_next_entry("first_entry.txt")
|
25
|
+
# io.write "Hello world!"
|
26
|
+
#
|
27
|
+
# io.put_next_entry("adir/first_entry.txt")
|
28
|
+
# io.write "Hello again!"
|
29
|
+
# }
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# Zip::ZipInputStream::open("my.zip") {
|
33
|
+
# |io|
|
34
|
+
#
|
35
|
+
# while (entry = io.get_next_entry)
|
36
|
+
# puts "Contents of #{entry.name}: '#{io.read}'"
|
37
|
+
# end
|
38
|
+
# }
|
39
|
+
#
|
40
|
+
# java.util.zip.ZipInputStream is the original inspiration for this
|
41
|
+
# class.
|
42
|
+
|
43
|
+
class InputStream
|
44
|
+
include ::Zip::IOExtras::AbstractInputStream
|
45
|
+
|
46
|
+
# Opens the indicated zip file. An exception is thrown
|
47
|
+
# if the specified offset in the specified filename is
|
48
|
+
# not a local zip entry header.
|
49
|
+
def initialize(filename, offset = 0, io = nil)
|
50
|
+
super()
|
51
|
+
if (io.nil?)
|
52
|
+
@archiveIO = ::File.open(filename, "rb")
|
53
|
+
@archiveIO.seek(offset, IO::SEEK_SET)
|
54
|
+
else
|
55
|
+
@archiveIO = io
|
56
|
+
end
|
57
|
+
@decompressor = NullDecompressor.instance
|
58
|
+
@currentEntry = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def close
|
62
|
+
@archiveIO.close
|
63
|
+
end
|
64
|
+
|
65
|
+
# Same as #initialize but if a block is passed the opened
|
66
|
+
# stream is passed to the block and closed when the block
|
67
|
+
# returns.
|
68
|
+
def InputStream.open(filename)
|
69
|
+
return new(filename) unless block_given?
|
70
|
+
|
71
|
+
zio = new(filename)
|
72
|
+
yield zio
|
73
|
+
ensure
|
74
|
+
zio.close if zio
|
75
|
+
end
|
76
|
+
|
77
|
+
def InputStream.open_buffer(io)
|
78
|
+
return new('',0,io) unless block_given?
|
79
|
+
zio = new('',0,io)
|
80
|
+
yield zio
|
81
|
+
ensure
|
82
|
+
zio.close if zio
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns a ZipEntry object. It is necessary to call this
|
86
|
+
# method on a newly created ZipInputStream before reading from
|
87
|
+
# the first entry in the archive. Returns nil when there are
|
88
|
+
# no more entries.
|
89
|
+
|
90
|
+
def get_next_entry
|
91
|
+
@archiveIO.seek(@currentEntry.next_header_offset, IO::SEEK_SET) if @currentEntry
|
92
|
+
open_entry
|
93
|
+
end
|
94
|
+
|
95
|
+
# Rewinds the stream to the beginning of the current entry
|
96
|
+
def rewind
|
97
|
+
return if @currentEntry.nil?
|
98
|
+
@lineno = 0
|
99
|
+
@pos = 0
|
100
|
+
@archiveIO.seek(@currentEntry.local_header_offset,
|
101
|
+
IO::SEEK_SET)
|
102
|
+
open_entry
|
103
|
+
end
|
104
|
+
|
105
|
+
# Modeled after IO.sysread
|
106
|
+
def sysread(numberOfBytes = nil, buf = nil)
|
107
|
+
@decompressor.sysread(numberOfBytes, buf)
|
108
|
+
end
|
109
|
+
|
110
|
+
def eof
|
111
|
+
@output_buffer.empty? && @decompressor.eof
|
112
|
+
end
|
113
|
+
alias :eof? :eof
|
114
|
+
|
115
|
+
protected
|
116
|
+
|
117
|
+
def open_entry
|
118
|
+
@currentEntry = Entry.read_local_entry(@archiveIO)
|
119
|
+
if @currentEntry.nil?
|
120
|
+
@decompressor = NullDecompressor.instance
|
121
|
+
elsif @currentEntry.compression_method == Entry::STORED
|
122
|
+
@decompressor = PassThruDecompressor.new(@archiveIO, @currentEntry.size)
|
123
|
+
elsif @currentEntry.compression_method == Entry::DEFLATED
|
124
|
+
@decompressor = Inflater.new(@archiveIO)
|
125
|
+
else
|
126
|
+
raise ZipCompressionMethodError,
|
127
|
+
"Unsupported compression method #{@currentEntry.compression_method}"
|
128
|
+
end
|
129
|
+
flush
|
130
|
+
return @currentEntry
|
131
|
+
end
|
132
|
+
|
133
|
+
def produce_input
|
134
|
+
@decompressor.produce_input
|
135
|
+
end
|
136
|
+
|
137
|
+
def input_finished?
|
138
|
+
@decompressor.input_finished?
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
144
|
+
# rubyzip is free software; you can redistribute it and/or
|
145
|
+
# modify it under the terms of the ruby license.
|
data/lib/zip/ioextras.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
module Zip
|
2
|
+
module IOExtras #:nodoc:
|
3
|
+
|
4
|
+
CHUNK_SIZE = 131072
|
5
|
+
|
6
|
+
RANGE_ALL = 0..-1
|
7
|
+
|
8
|
+
def self.copy_stream(ostream, istream)
|
9
|
+
s = ''
|
10
|
+
ostream.write(istream.read(CHUNK_SIZE, s)) until istream.eof?
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.copy_stream_n(ostream, istream, nbytes)
|
14
|
+
s = ''
|
15
|
+
toread = nbytes
|
16
|
+
while (toread > 0 && !istream.eof?)
|
17
|
+
tr = toread > CHUNK_SIZE ? CHUNK_SIZE : toread
|
18
|
+
ostream.write(istream.read(tr, s))
|
19
|
+
toread -= tr
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# Implements kind_of? in order to pretend to be an IO object
|
25
|
+
module FakeIO
|
26
|
+
def kind_of?(object)
|
27
|
+
object == IO || super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Implements many of the convenience methods of IO
|
32
|
+
# such as gets, getc, readline and readlines
|
33
|
+
# depends on: input_finished?, produce_input and read
|
34
|
+
module AbstractInputStream
|
35
|
+
include Enumerable
|
36
|
+
include FakeIO
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
super
|
40
|
+
@lineno = 0
|
41
|
+
@pos = 0
|
42
|
+
@output_buffer = ""
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_accessor :lineno
|
46
|
+
attr_reader :pos
|
47
|
+
|
48
|
+
def read(numberOfBytes = nil, buf = nil)
|
49
|
+
tbuf = nil
|
50
|
+
|
51
|
+
if @output_buffer.bytesize > 0
|
52
|
+
if numberOfBytes <= @output_buffer.bytesize
|
53
|
+
tbuf = @output_buffer.slice!(0, numberOfBytes)
|
54
|
+
else
|
55
|
+
numberOfBytes -= @output_buffer.bytesize if (numberOfBytes)
|
56
|
+
rbuf = sysread(numberOfBytes, buf)
|
57
|
+
tbuf = @output_buffer
|
58
|
+
tbuf << rbuf if (rbuf)
|
59
|
+
@output_buffer = ""
|
60
|
+
end
|
61
|
+
else
|
62
|
+
tbuf = sysread(numberOfBytes, buf)
|
63
|
+
end
|
64
|
+
|
65
|
+
@pos += tbuf.length
|
66
|
+
|
67
|
+
return nil unless (tbuf)
|
68
|
+
|
69
|
+
if buf
|
70
|
+
buf.replace(tbuf)
|
71
|
+
else
|
72
|
+
buf = tbuf
|
73
|
+
end
|
74
|
+
|
75
|
+
buf
|
76
|
+
end
|
77
|
+
|
78
|
+
def readlines(aSepString = $/)
|
79
|
+
retVal = []
|
80
|
+
each_line(aSepString) { |line| retVal << line }
|
81
|
+
retVal
|
82
|
+
end
|
83
|
+
|
84
|
+
def gets(aSepString = $/, numberOfBytes = nil)
|
85
|
+
@lineno = @lineno.next
|
86
|
+
|
87
|
+
if numberOfBytes.respond_to?(:to_int)
|
88
|
+
numberOfBytes = numberOfBytes.to_int
|
89
|
+
aSepString = aSepString.to_str if aSepString
|
90
|
+
elsif aSepString.respond_to?(:to_int)
|
91
|
+
numberOfBytes = aSepString.to_int
|
92
|
+
aSepString = $/
|
93
|
+
else
|
94
|
+
numberOfBytes = nil
|
95
|
+
aSepString = aSepString.to_str if aSepString
|
96
|
+
end
|
97
|
+
|
98
|
+
return read(numberOfBytes) if aSepString.nil?
|
99
|
+
aSepString = "#{$/}#{$/}" if aSepString.empty?
|
100
|
+
|
101
|
+
bufferIndex = 0
|
102
|
+
overLimit = (numberOfBytes && @output_buffer.bytesize >= numberOfBytes)
|
103
|
+
while ((matchIndex = @output_buffer.index(aSepString, bufferIndex)) == nil && !overLimit)
|
104
|
+
bufferIndex = [bufferIndex, @output_buffer.bytesize - aSepString.bytesize].max
|
105
|
+
if input_finished?
|
106
|
+
return @output_buffer.empty? ? nil : flush
|
107
|
+
end
|
108
|
+
@output_buffer << produce_input
|
109
|
+
overLimit = (numberOfBytes && @output_buffer.bytesize >= numberOfBytes)
|
110
|
+
end
|
111
|
+
sepIndex = [matchIndex + aSepString.bytesize, numberOfBytes || @output_buffer.bytesize].min
|
112
|
+
@pos += sepIndex
|
113
|
+
return @output_buffer.slice!(0...sepIndex)
|
114
|
+
end
|
115
|
+
|
116
|
+
def flush
|
117
|
+
retVal = @output_buffer
|
118
|
+
@output_buffer=""
|
119
|
+
return retVal
|
120
|
+
end
|
121
|
+
|
122
|
+
def readline(aSepString = $/)
|
123
|
+
retVal = gets(aSepString)
|
124
|
+
raise EOFError if retVal == nil
|
125
|
+
retVal
|
126
|
+
end
|
127
|
+
|
128
|
+
def each_line(aSepString = $/)
|
129
|
+
while true
|
130
|
+
yield readline(aSepString)
|
131
|
+
end
|
132
|
+
rescue EOFError
|
133
|
+
end
|
134
|
+
|
135
|
+
alias_method :each, :each_line
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# Implements many of the output convenience methods of IO.
|
140
|
+
# relies on <<
|
141
|
+
module AbstractOutputStream
|
142
|
+
include FakeIO
|
143
|
+
|
144
|
+
def write(data)
|
145
|
+
self << data
|
146
|
+
data.to_s.bytesize
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
def print(*params)
|
151
|
+
self << params.join($,) << $\.to_s
|
152
|
+
end
|
153
|
+
|
154
|
+
def printf(aFormatString, *params)
|
155
|
+
self << sprintf(aFormatString, *params)
|
156
|
+
end
|
157
|
+
|
158
|
+
def putc(anObject)
|
159
|
+
self << case anObject
|
160
|
+
when Fixnum then
|
161
|
+
anObject.chr
|
162
|
+
when String then
|
163
|
+
anObject
|
164
|
+
else
|
165
|
+
raise TypeError, "putc: Only Fixnum and String supported"
|
166
|
+
end
|
167
|
+
anObject
|
168
|
+
end
|
169
|
+
|
170
|
+
def puts(*params)
|
171
|
+
params << "\n" if params.empty?
|
172
|
+
params.flatten.each do |element|
|
173
|
+
val = element.to_s
|
174
|
+
self << val
|
175
|
+
self << "\n" unless val[-1, 1] == "\n"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
end # IOExtras namespace module
|
182
|
+
end
|
183
|
+
|
184
|
+
# Copyright (C) 2002-2004 Thomas Sondergaard
|
185
|
+
# rubyzip is free software; you can redistribute it and/or
|
186
|
+
# modify it under the terms of the ruby license.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Zip
|
2
|
+
class NullCompressor < Compressor #:nodoc:all
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def <<(data)
|
6
|
+
raise IOError, "closed stream"
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :size, :compressed_size
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
14
|
+
# rubyzip is free software; you can redistribute it and/or
|
15
|
+
# modify it under the terms of the ruby license.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Zip
|
2
|
+
class NullDecompressor #:nodoc:all
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def sysread(numberOfBytes = nil, buf = nil)
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def produce_input
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def input_finished?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def eof
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
alias :eof? :eof
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
26
|
+
# rubyzip is free software; you can redistribute it and/or
|
27
|
+
# modify it under the terms of the ruby license.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Zip
|
2
|
+
class NullInputStream < NullDecompressor #:nodoc:all
|
3
|
+
include ::Zip::IOExtras::AbstractInputStream
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
8
|
+
# rubyzip is free software; you can redistribute it and/or
|
9
|
+
# modify it under the terms of the ruby license.
|