superp-rubyzip 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|