rubyzip 1.0.0 → 2.4.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.
- checksums.yaml +6 -14
- data/README.md +231 -46
- data/Rakefile +13 -5
- data/TODO +0 -1
- data/lib/zip/central_directory.rb +64 -29
- data/lib/zip/compressor.rb +1 -2
- data/lib/zip/constants.rb +59 -5
- data/lib/zip/crypto/decrypted_io.rb +40 -0
- data/lib/zip/crypto/encryption.rb +11 -0
- data/lib/zip/crypto/null_encryption.rb +43 -0
- data/lib/zip/crypto/traditional_encryption.rb +99 -0
- data/lib/zip/decompressor.rb +22 -4
- data/lib/zip/deflater.rb +11 -6
- data/lib/zip/dos_time.rb +27 -16
- data/lib/zip/entry.rb +299 -163
- data/lib/zip/entry_set.rb +22 -20
- data/lib/zip/errors.rb +17 -6
- data/lib/zip/extra_field/generic.rb +15 -14
- data/lib/zip/extra_field/ntfs.rb +94 -0
- data/lib/zip/extra_field/old_unix.rb +46 -0
- data/lib/zip/extra_field/universal_time.rb +46 -16
- data/lib/zip/extra_field/unix.rb +10 -9
- data/lib/zip/extra_field/zip64.rb +46 -6
- data/lib/zip/extra_field/zip64_placeholder.rb +15 -0
- data/lib/zip/extra_field.rb +32 -18
- data/lib/zip/file.rb +260 -160
- data/lib/zip/filesystem.rb +297 -276
- data/lib/zip/inflater.rb +23 -34
- data/lib/zip/input_stream.rb +130 -82
- data/lib/zip/ioextras/abstract_input_stream.rb +36 -22
- data/lib/zip/ioextras/abstract_output_stream.rb +4 -6
- data/lib/zip/ioextras.rb +4 -6
- data/lib/zip/null_compressor.rb +2 -2
- data/lib/zip/null_decompressor.rb +4 -12
- data/lib/zip/null_input_stream.rb +2 -1
- data/lib/zip/output_stream.rb +75 -45
- data/lib/zip/pass_thru_compressor.rb +6 -6
- data/lib/zip/pass_thru_decompressor.rb +14 -24
- data/lib/zip/streamable_directory.rb +3 -3
- data/lib/zip/streamable_stream.rb +21 -16
- data/lib/zip/version.rb +1 -1
- data/lib/zip.rb +42 -3
- data/samples/example.rb +30 -40
- data/samples/example_filesystem.rb +16 -18
- data/samples/example_recursive.rb +33 -28
- data/samples/gtk_ruby_zip.rb +84 -0
- data/samples/qtzip.rb +25 -34
- data/samples/write_simple.rb +10 -13
- data/samples/zipfind.rb +38 -45
- metadata +129 -28
- data/NEWS +0 -182
- data/samples/gtkRubyzip.rb +0 -86
data/lib/zip/filesystem.rb
CHANGED
|
@@ -1,115 +1,111 @@
|
|
|
1
1
|
require 'zip'
|
|
2
2
|
|
|
3
3
|
module Zip
|
|
4
|
-
|
|
5
|
-
#
|
|
6
|
-
# a zip archive that is similar to ruby's builtin File and Dir
|
|
4
|
+
# The ZipFileSystem API provides an API for accessing entries in
|
|
5
|
+
# a zip archive that is similar to ruby's builtin File and Dir
|
|
7
6
|
# classes.
|
|
8
7
|
#
|
|
9
|
-
# Requiring 'zip/
|
|
10
|
-
# making the methods in this module available on
|
|
8
|
+
# Requiring 'zip/filesystem' includes this module in Zip::File
|
|
9
|
+
# making the methods in this module available on Zip::File objects.
|
|
11
10
|
#
|
|
12
|
-
# Using this API the following example creates a new zip file
|
|
11
|
+
# Using this API the following example creates a new zip file
|
|
13
12
|
# <code>my.zip</code> containing a normal entry with the name
|
|
14
13
|
# <code>first.txt</code>, a directory entry named <code>mydir</code>
|
|
15
14
|
# and finally another normal entry named <code>second.txt</code>
|
|
16
15
|
#
|
|
17
16
|
# require 'zip/filesystem'
|
|
18
|
-
#
|
|
19
|
-
# Zip::File.open("my.zip", Zip::
|
|
17
|
+
#
|
|
18
|
+
# Zip::File.open("my.zip", Zip::File::CREATE) {
|
|
20
19
|
# |zipfile|
|
|
21
20
|
# zipfile.file.open("first.txt", "w") { |f| f.puts "Hello world" }
|
|
22
21
|
# zipfile.dir.mkdir("mydir")
|
|
23
22
|
# zipfile.file.open("mydir/second.txt", "w") { |f| f.puts "Hello again" }
|
|
24
23
|
# }
|
|
25
24
|
#
|
|
26
|
-
# Reading is as easy as writing, as the following example shows. The
|
|
25
|
+
# Reading is as easy as writing, as the following example shows. The
|
|
27
26
|
# example writes the contents of <code>first.txt</code> from zip archive
|
|
28
27
|
# <code>my.zip</code> to standard out.
|
|
29
28
|
#
|
|
30
29
|
# require 'zip/filesystem'
|
|
31
|
-
#
|
|
30
|
+
#
|
|
32
31
|
# Zip::File.open("my.zip") {
|
|
33
32
|
# |zipfile|
|
|
34
33
|
# puts zipfile.file.read("first.txt")
|
|
35
34
|
# }
|
|
36
35
|
|
|
37
36
|
module FileSystem
|
|
38
|
-
|
|
39
37
|
def initialize # :nodoc:
|
|
40
|
-
|
|
41
|
-
@
|
|
42
|
-
@
|
|
43
|
-
@
|
|
44
|
-
@
|
|
38
|
+
mapped_zip = ZipFileNameMapper.new(self)
|
|
39
|
+
@zip_fs_dir = ZipFsDir.new(mapped_zip)
|
|
40
|
+
@zip_fs_file = ZipFsFile.new(mapped_zip)
|
|
41
|
+
@zip_fs_dir.file = @zip_fs_file
|
|
42
|
+
@zip_fs_file.dir = @zip_fs_dir
|
|
45
43
|
end
|
|
46
44
|
|
|
47
45
|
# Returns a ZipFsDir which is much like ruby's builtin Dir (class)
|
|
48
|
-
# object, except it works on the
|
|
46
|
+
# object, except it works on the Zip::File on which this method is
|
|
49
47
|
# invoked
|
|
50
48
|
def dir
|
|
51
|
-
@
|
|
49
|
+
@zip_fs_dir
|
|
52
50
|
end
|
|
53
51
|
|
|
54
52
|
# Returns a ZipFsFile which is much like ruby's builtin File (class)
|
|
55
|
-
# object, except it works on the
|
|
53
|
+
# object, except it works on the Zip::File on which this method is
|
|
56
54
|
# invoked
|
|
57
55
|
def file
|
|
58
|
-
@
|
|
56
|
+
@zip_fs_file
|
|
59
57
|
end
|
|
60
58
|
|
|
61
59
|
# Instances of this class are normally accessed via the accessor
|
|
62
|
-
#
|
|
63
|
-
# builtin File (class) object, except it works on
|
|
60
|
+
# Zip::File::file. An instance of ZipFsFile behaves like ruby's
|
|
61
|
+
# builtin File (class) object, except it works on Zip::File entries.
|
|
64
62
|
#
|
|
65
63
|
# The individual methods are not documented due to their
|
|
66
64
|
# similarity with the methods in File
|
|
67
65
|
class ZipFsFile
|
|
68
|
-
|
|
69
66
|
attr_writer :dir
|
|
70
|
-
#
|
|
67
|
+
# protected :dir
|
|
71
68
|
|
|
72
69
|
class ZipFsStat
|
|
73
|
-
|
|
74
70
|
class << self
|
|
75
|
-
|
|
76
71
|
def delegate_to_fs_file(*methods)
|
|
77
72
|
methods.each do |method|
|
|
78
|
-
|
|
73
|
+
class_eval <<-END_EVAL, __FILE__, __LINE__ + 1
|
|
79
74
|
def #{method} # def file?
|
|
80
|
-
@
|
|
75
|
+
@zip_fs_file.#{method}(@entry_name) # @zip_fs_file.file?(@entry_name)
|
|
81
76
|
end # end
|
|
82
|
-
|
|
77
|
+
END_EVAL
|
|
83
78
|
end
|
|
84
79
|
end
|
|
85
|
-
|
|
86
80
|
end
|
|
87
81
|
|
|
88
|
-
def initialize(
|
|
89
|
-
@
|
|
90
|
-
@
|
|
82
|
+
def initialize(zip_fs_file, entry_name)
|
|
83
|
+
@zip_fs_file = zip_fs_file
|
|
84
|
+
@entry_name = entry_name
|
|
91
85
|
end
|
|
92
86
|
|
|
93
|
-
def kind_of?(
|
|
94
|
-
super ||
|
|
87
|
+
def kind_of?(type)
|
|
88
|
+
super || type == ::File::Stat
|
|
95
89
|
end
|
|
96
90
|
|
|
97
91
|
delegate_to_fs_file :file?, :directory?, :pipe?, :chardev?, :symlink?,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
:socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime,
|
|
93
|
+
:writable_real?, :executable?, :executable_real?, :sticky?, :owned?,
|
|
94
|
+
:grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime
|
|
101
95
|
|
|
102
|
-
def blocks
|
|
96
|
+
def blocks
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
103
99
|
|
|
104
100
|
def get_entry
|
|
105
|
-
@
|
|
101
|
+
@zip_fs_file.__send__(:get_entry, @entry_name)
|
|
106
102
|
end
|
|
107
103
|
private :get_entry
|
|
108
104
|
|
|
109
105
|
def gid
|
|
110
106
|
e = get_entry
|
|
111
|
-
if e.extra.member?
|
|
112
|
-
e.extra[
|
|
107
|
+
if e.extra.member? 'IUnix'
|
|
108
|
+
e.extra['IUnix'].gid || 0
|
|
113
109
|
else
|
|
114
110
|
0
|
|
115
111
|
end
|
|
@@ -117,306 +113,316 @@ module Zip
|
|
|
117
113
|
|
|
118
114
|
def uid
|
|
119
115
|
e = get_entry
|
|
120
|
-
if e.extra.member?
|
|
121
|
-
e.extra[
|
|
116
|
+
if e.extra.member? 'IUnix'
|
|
117
|
+
e.extra['IUnix'].uid || 0
|
|
122
118
|
else
|
|
123
119
|
0
|
|
124
120
|
end
|
|
125
121
|
end
|
|
126
122
|
|
|
127
|
-
def ino
|
|
123
|
+
def ino
|
|
124
|
+
0
|
|
125
|
+
end
|
|
128
126
|
|
|
129
|
-
def dev
|
|
127
|
+
def dev
|
|
128
|
+
0
|
|
129
|
+
end
|
|
130
130
|
|
|
131
|
-
def rdev
|
|
131
|
+
def rdev
|
|
132
|
+
0
|
|
133
|
+
end
|
|
132
134
|
|
|
133
|
-
def rdev_major
|
|
135
|
+
def rdev_major
|
|
136
|
+
0
|
|
137
|
+
end
|
|
134
138
|
|
|
135
|
-
def rdev_minor
|
|
139
|
+
def rdev_minor
|
|
140
|
+
0
|
|
141
|
+
end
|
|
136
142
|
|
|
137
143
|
def ftype
|
|
138
144
|
if file?
|
|
139
|
-
|
|
145
|
+
'file'
|
|
140
146
|
elsif directory?
|
|
141
|
-
|
|
147
|
+
'directory'
|
|
142
148
|
else
|
|
143
|
-
raise StandardError,
|
|
149
|
+
raise StandardError, 'Unknown file type'
|
|
144
150
|
end
|
|
145
151
|
end
|
|
146
152
|
|
|
147
|
-
def nlink
|
|
153
|
+
def nlink
|
|
154
|
+
1
|
|
155
|
+
end
|
|
148
156
|
|
|
149
|
-
def blksize
|
|
157
|
+
def blksize
|
|
158
|
+
nil
|
|
159
|
+
end
|
|
150
160
|
|
|
151
161
|
def mode
|
|
152
162
|
e = get_entry
|
|
153
163
|
if e.fstype == 3
|
|
154
164
|
e.external_file_attributes >> 16
|
|
155
165
|
else
|
|
156
|
-
|
|
166
|
+
33_206 # 33206 is equivalent to -rw-rw-rw-
|
|
157
167
|
end
|
|
158
168
|
end
|
|
159
169
|
end
|
|
160
170
|
|
|
161
|
-
def initialize(
|
|
162
|
-
@
|
|
171
|
+
def initialize(mapped_zip)
|
|
172
|
+
@mapped_zip = mapped_zip
|
|
163
173
|
end
|
|
164
174
|
|
|
165
|
-
def get_entry(
|
|
166
|
-
|
|
167
|
-
raise Errno::ENOENT, "No such file or directory - #{
|
|
175
|
+
def get_entry(filename)
|
|
176
|
+
unless exists?(filename)
|
|
177
|
+
raise Errno::ENOENT, "No such file or directory - #{filename}"
|
|
168
178
|
end
|
|
169
|
-
|
|
179
|
+
|
|
180
|
+
@mapped_zip.find_entry(filename)
|
|
170
181
|
end
|
|
171
182
|
private :get_entry
|
|
172
183
|
|
|
173
|
-
def unix_mode_cmp(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
false
|
|
179
|
-
end
|
|
184
|
+
def unix_mode_cmp(filename, mode)
|
|
185
|
+
e = get_entry(filename)
|
|
186
|
+
e.fstype == 3 && ((e.external_file_attributes >> 16) & mode) != 0
|
|
187
|
+
rescue Errno::ENOENT
|
|
188
|
+
false
|
|
180
189
|
end
|
|
181
190
|
private :unix_mode_cmp
|
|
182
191
|
|
|
183
|
-
def exists?(
|
|
184
|
-
expand_path(
|
|
192
|
+
def exists?(filename)
|
|
193
|
+
expand_path(filename) == '/' || !@mapped_zip.find_entry(filename).nil?
|
|
185
194
|
end
|
|
186
|
-
alias
|
|
195
|
+
alias exist? exists?
|
|
187
196
|
|
|
188
197
|
# Permissions not implemented, so if the file exists it is accessible
|
|
189
|
-
alias owned?
|
|
190
|
-
alias grpowned?
|
|
198
|
+
alias owned? exists?
|
|
199
|
+
alias grpowned? exists?
|
|
191
200
|
|
|
192
|
-
def readable?(
|
|
193
|
-
unix_mode_cmp(
|
|
201
|
+
def readable?(filename)
|
|
202
|
+
unix_mode_cmp(filename, 0o444)
|
|
194
203
|
end
|
|
195
|
-
alias readable_real?
|
|
204
|
+
alias readable_real? readable?
|
|
196
205
|
|
|
197
|
-
def writable?(
|
|
198
|
-
unix_mode_cmp(
|
|
206
|
+
def writable?(filename)
|
|
207
|
+
unix_mode_cmp(filename, 0o222)
|
|
199
208
|
end
|
|
200
|
-
alias writable_real?
|
|
209
|
+
alias writable_real? writable?
|
|
201
210
|
|
|
202
|
-
def executable?(
|
|
203
|
-
unix_mode_cmp(
|
|
211
|
+
def executable?(filename)
|
|
212
|
+
unix_mode_cmp(filename, 0o111)
|
|
204
213
|
end
|
|
205
214
|
alias executable_real? executable?
|
|
206
215
|
|
|
207
|
-
def setuid?(
|
|
208
|
-
unix_mode_cmp(
|
|
216
|
+
def setuid?(filename)
|
|
217
|
+
unix_mode_cmp(filename, 0o4000)
|
|
209
218
|
end
|
|
210
219
|
|
|
211
|
-
def setgid?(
|
|
212
|
-
unix_mode_cmp(
|
|
220
|
+
def setgid?(filename)
|
|
221
|
+
unix_mode_cmp(filename, 0o2000)
|
|
213
222
|
end
|
|
214
223
|
|
|
215
|
-
def sticky?(
|
|
216
|
-
unix_mode_cmp(
|
|
224
|
+
def sticky?(filename)
|
|
225
|
+
unix_mode_cmp(filename, 0o1000)
|
|
217
226
|
end
|
|
218
227
|
|
|
219
228
|
def umask(*args)
|
|
220
229
|
::File.umask(*args)
|
|
221
230
|
end
|
|
222
231
|
|
|
223
|
-
def truncate(
|
|
224
|
-
raise StandardError,
|
|
232
|
+
def truncate(_filename, _len)
|
|
233
|
+
raise StandardError, 'truncate not supported'
|
|
225
234
|
end
|
|
226
235
|
|
|
227
|
-
def directory?(
|
|
228
|
-
entry = @
|
|
229
|
-
expand_path(
|
|
236
|
+
def directory?(filename)
|
|
237
|
+
entry = @mapped_zip.find_entry(filename)
|
|
238
|
+
expand_path(filename) == '/' || (!entry.nil? && entry.directory?)
|
|
230
239
|
end
|
|
231
240
|
|
|
232
|
-
def open(
|
|
233
|
-
|
|
234
|
-
case
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
def open(filename, mode = 'r', permissions = 0o644, &block)
|
|
242
|
+
mode = mode.delete('b') # ignore b option
|
|
243
|
+
case mode
|
|
244
|
+
when 'r'
|
|
245
|
+
@mapped_zip.get_input_stream(filename, &block)
|
|
246
|
+
when 'w'
|
|
247
|
+
@mapped_zip.get_output_stream(filename, permissions, &block)
|
|
248
|
+
else
|
|
249
|
+
raise StandardError, "openmode '#{mode} not supported" unless mode == 'r'
|
|
241
250
|
end
|
|
242
251
|
end
|
|
243
252
|
|
|
244
|
-
def new(
|
|
245
|
-
open(
|
|
253
|
+
def new(filename, mode = 'r')
|
|
254
|
+
self.open(filename, mode)
|
|
246
255
|
end
|
|
247
256
|
|
|
248
|
-
def size(
|
|
249
|
-
@
|
|
257
|
+
def size(filename)
|
|
258
|
+
@mapped_zip.get_entry(filename).size
|
|
250
259
|
end
|
|
251
260
|
|
|
252
261
|
# Returns nil for not found and nil for directories
|
|
253
|
-
def size?(
|
|
254
|
-
entry = @
|
|
255
|
-
|
|
262
|
+
def size?(filename)
|
|
263
|
+
entry = @mapped_zip.find_entry(filename)
|
|
264
|
+
entry.nil? || entry.directory? ? nil : entry.size
|
|
256
265
|
end
|
|
257
266
|
|
|
258
|
-
def chown(
|
|
259
|
-
filenames.each
|
|
260
|
-
e = get_entry(
|
|
261
|
-
unless e.extra.member?(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
e.extra["IUnix"].gid = groupInt
|
|
266
|
-
}
|
|
267
|
+
def chown(owner, group, *filenames)
|
|
268
|
+
filenames.each do |filename|
|
|
269
|
+
e = get_entry(filename)
|
|
270
|
+
e.extra.create('IUnix') unless e.extra.member?('IUnix')
|
|
271
|
+
e.extra['IUnix'].uid = owner
|
|
272
|
+
e.extra['IUnix'].gid = group
|
|
273
|
+
end
|
|
267
274
|
filenames.size
|
|
268
275
|
end
|
|
269
276
|
|
|
270
|
-
def chmod
|
|
271
|
-
filenames.each
|
|
272
|
-
e = get_entry(
|
|
277
|
+
def chmod(mode, *filenames)
|
|
278
|
+
filenames.each do |filename|
|
|
279
|
+
e = get_entry(filename)
|
|
273
280
|
e.fstype = 3 # force convertion filesystem type to unix
|
|
274
|
-
e.unix_perms =
|
|
275
|
-
e.external_file_attributes =
|
|
281
|
+
e.unix_perms = mode
|
|
282
|
+
e.external_file_attributes = mode << 16
|
|
276
283
|
e.dirty = true
|
|
277
|
-
|
|
284
|
+
end
|
|
278
285
|
filenames.size
|
|
279
286
|
end
|
|
280
287
|
|
|
281
|
-
def zero?(
|
|
282
|
-
sz = size(
|
|
283
|
-
sz
|
|
288
|
+
def zero?(filename)
|
|
289
|
+
sz = size(filename)
|
|
290
|
+
sz.nil? || sz == 0
|
|
284
291
|
rescue Errno::ENOENT
|
|
285
292
|
false
|
|
286
293
|
end
|
|
287
294
|
|
|
288
|
-
def file?(
|
|
289
|
-
entry = @
|
|
290
|
-
entry
|
|
291
|
-
end
|
|
295
|
+
def file?(filename)
|
|
296
|
+
entry = @mapped_zip.find_entry(filename)
|
|
297
|
+
!entry.nil? && entry.file?
|
|
298
|
+
end
|
|
292
299
|
|
|
293
|
-
def dirname(
|
|
294
|
-
::File.dirname(
|
|
300
|
+
def dirname(filename)
|
|
301
|
+
::File.dirname(filename)
|
|
295
302
|
end
|
|
296
303
|
|
|
297
|
-
def basename(
|
|
298
|
-
::File.basename(
|
|
304
|
+
def basename(filename)
|
|
305
|
+
::File.basename(filename)
|
|
299
306
|
end
|
|
300
307
|
|
|
301
|
-
def split(
|
|
302
|
-
::File.split(
|
|
308
|
+
def split(filename)
|
|
309
|
+
::File.split(filename)
|
|
303
310
|
end
|
|
304
311
|
|
|
305
312
|
def join(*fragments)
|
|
306
313
|
::File.join(*fragments)
|
|
307
314
|
end
|
|
308
315
|
|
|
309
|
-
def utime(
|
|
310
|
-
|
|
311
|
-
get_entry(
|
|
312
|
-
|
|
316
|
+
def utime(modified_time, *filenames)
|
|
317
|
+
filenames.each do |filename|
|
|
318
|
+
get_entry(filename).time = modified_time
|
|
319
|
+
end
|
|
313
320
|
end
|
|
314
321
|
|
|
315
|
-
def mtime(
|
|
316
|
-
@
|
|
322
|
+
def mtime(filename)
|
|
323
|
+
@mapped_zip.get_entry(filename).mtime
|
|
317
324
|
end
|
|
318
325
|
|
|
319
|
-
def atime(
|
|
320
|
-
e = get_entry(
|
|
321
|
-
if e.extra.member?
|
|
322
|
-
e.extra[
|
|
323
|
-
|
|
324
|
-
|
|
326
|
+
def atime(filename)
|
|
327
|
+
e = get_entry(filename)
|
|
328
|
+
if e.extra.member? 'UniversalTime'
|
|
329
|
+
e.extra['UniversalTime'].atime
|
|
330
|
+
elsif e.extra.member? 'NTFS'
|
|
331
|
+
e.extra['NTFS'].atime
|
|
325
332
|
end
|
|
326
333
|
end
|
|
327
334
|
|
|
328
|
-
def ctime(
|
|
329
|
-
e = get_entry(
|
|
330
|
-
if e.extra.member?
|
|
331
|
-
e.extra[
|
|
332
|
-
|
|
333
|
-
|
|
335
|
+
def ctime(filename)
|
|
336
|
+
e = get_entry(filename)
|
|
337
|
+
if e.extra.member? 'UniversalTime'
|
|
338
|
+
e.extra['UniversalTime'].ctime
|
|
339
|
+
elsif e.extra.member? 'NTFS'
|
|
340
|
+
e.extra['NTFS'].ctime
|
|
334
341
|
end
|
|
335
342
|
end
|
|
336
343
|
|
|
337
|
-
def pipe?(
|
|
344
|
+
def pipe?(_filename)
|
|
338
345
|
false
|
|
339
346
|
end
|
|
340
347
|
|
|
341
|
-
def blockdev?(
|
|
348
|
+
def blockdev?(_filename)
|
|
342
349
|
false
|
|
343
350
|
end
|
|
344
351
|
|
|
345
|
-
def chardev?(
|
|
352
|
+
def chardev?(_filename)
|
|
346
353
|
false
|
|
347
354
|
end
|
|
348
355
|
|
|
349
|
-
def symlink?(
|
|
356
|
+
def symlink?(_filename)
|
|
350
357
|
false
|
|
351
358
|
end
|
|
352
359
|
|
|
353
|
-
def socket?(
|
|
360
|
+
def socket?(_filename)
|
|
354
361
|
false
|
|
355
362
|
end
|
|
356
363
|
|
|
357
|
-
def ftype(
|
|
358
|
-
@
|
|
364
|
+
def ftype(filename)
|
|
365
|
+
@mapped_zip.get_entry(filename).directory? ? 'directory' : 'file'
|
|
359
366
|
end
|
|
360
367
|
|
|
361
|
-
def readlink(
|
|
362
|
-
raise NotImplementedError,
|
|
368
|
+
def readlink(_filename)
|
|
369
|
+
raise NotImplementedError, 'The readlink() function is not implemented'
|
|
363
370
|
end
|
|
364
371
|
|
|
365
|
-
def symlink(
|
|
366
|
-
raise NotImplementedError,
|
|
372
|
+
def symlink(_filename, _symlink_name)
|
|
373
|
+
raise NotImplementedError, 'The symlink() function is not implemented'
|
|
367
374
|
end
|
|
368
375
|
|
|
369
|
-
def link(
|
|
370
|
-
raise NotImplementedError,
|
|
376
|
+
def link(_filename, _symlink_name)
|
|
377
|
+
raise NotImplementedError, 'The link() function is not implemented'
|
|
371
378
|
end
|
|
372
379
|
|
|
373
380
|
def pipe
|
|
374
|
-
raise NotImplementedError,
|
|
381
|
+
raise NotImplementedError, 'The pipe() function is not implemented'
|
|
375
382
|
end
|
|
376
383
|
|
|
377
|
-
def stat(
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
ZipFsStat.new(self, fileName)
|
|
384
|
+
def stat(filename)
|
|
385
|
+
raise Errno::ENOENT, filename unless exists?(filename)
|
|
386
|
+
|
|
387
|
+
ZipFsStat.new(self, filename)
|
|
382
388
|
end
|
|
383
389
|
|
|
384
390
|
alias lstat stat
|
|
385
391
|
|
|
386
|
-
def readlines(
|
|
387
|
-
open(
|
|
392
|
+
def readlines(filename)
|
|
393
|
+
self.open(filename, &:readlines)
|
|
388
394
|
end
|
|
389
395
|
|
|
390
|
-
def read(
|
|
391
|
-
@
|
|
396
|
+
def read(filename)
|
|
397
|
+
@mapped_zip.read(filename)
|
|
392
398
|
end
|
|
393
399
|
|
|
394
|
-
def popen(*args, &
|
|
395
|
-
::File.popen(*args, &
|
|
400
|
+
def popen(*args, &a_proc)
|
|
401
|
+
::File.popen(*args, &a_proc)
|
|
396
402
|
end
|
|
397
403
|
|
|
398
|
-
def foreach(
|
|
399
|
-
open(
|
|
404
|
+
def foreach(filename, sep = $INPUT_RECORD_SEPARATOR, &a_proc)
|
|
405
|
+
self.open(filename) { |is| is.each_line(sep, &a_proc) }
|
|
400
406
|
end
|
|
401
407
|
|
|
402
408
|
def delete(*args)
|
|
403
|
-
args.each
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
raise Errno::EISDIR, "Is a directory - \"#{fileName}\""
|
|
409
|
+
args.each do |filename|
|
|
410
|
+
if directory?(filename)
|
|
411
|
+
raise Errno::EISDIR, "Is a directory - \"#{filename}\""
|
|
407
412
|
end
|
|
408
|
-
|
|
409
|
-
|
|
413
|
+
|
|
414
|
+
@mapped_zip.remove(filename)
|
|
415
|
+
end
|
|
410
416
|
end
|
|
411
417
|
|
|
412
|
-
def rename(
|
|
413
|
-
@
|
|
418
|
+
def rename(file_to_rename, new_name)
|
|
419
|
+
@mapped_zip.rename(file_to_rename, new_name) { true }
|
|
414
420
|
end
|
|
415
421
|
|
|
416
|
-
alias
|
|
422
|
+
alias unlink delete
|
|
417
423
|
|
|
418
|
-
def expand_path(
|
|
419
|
-
@
|
|
424
|
+
def expand_path(path)
|
|
425
|
+
@mapped_zip.expand_path(path)
|
|
420
426
|
end
|
|
421
427
|
end
|
|
422
428
|
|
|
@@ -427,187 +433,202 @@ module Zip
|
|
|
427
433
|
# The individual methods are not documented due to their
|
|
428
434
|
# similarity with the methods in Dir
|
|
429
435
|
class ZipFsDir
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
@mappedZip = mappedZip
|
|
436
|
+
def initialize(mapped_zip)
|
|
437
|
+
@mapped_zip = mapped_zip
|
|
433
438
|
end
|
|
434
439
|
|
|
435
440
|
attr_writer :file
|
|
436
441
|
|
|
437
|
-
def new(
|
|
438
|
-
ZipFsDirIterator.new(entries(
|
|
442
|
+
def new(directory_name)
|
|
443
|
+
ZipFsDirIterator.new(entries(directory_name))
|
|
439
444
|
end
|
|
440
445
|
|
|
441
|
-
def open(
|
|
442
|
-
|
|
446
|
+
def open(directory_name)
|
|
447
|
+
dir_iter = new(directory_name)
|
|
443
448
|
if block_given?
|
|
444
449
|
begin
|
|
445
|
-
yield(
|
|
450
|
+
yield(dir_iter)
|
|
446
451
|
return nil
|
|
447
452
|
ensure
|
|
448
|
-
|
|
453
|
+
dir_iter.close
|
|
449
454
|
end
|
|
450
455
|
end
|
|
451
|
-
|
|
456
|
+
dir_iter
|
|
452
457
|
end
|
|
453
458
|
|
|
454
|
-
def pwd
|
|
459
|
+
def pwd
|
|
460
|
+
@mapped_zip.pwd
|
|
461
|
+
end
|
|
455
462
|
alias getwd pwd
|
|
456
463
|
|
|
457
|
-
def chdir(
|
|
458
|
-
unless @file.stat(
|
|
459
|
-
raise Errno::EINVAL, "Invalid argument - #{
|
|
464
|
+
def chdir(directory_name)
|
|
465
|
+
unless @file.stat(directory_name).directory?
|
|
466
|
+
raise Errno::EINVAL, "Invalid argument - #{directory_name}"
|
|
460
467
|
end
|
|
461
|
-
|
|
468
|
+
|
|
469
|
+
@mapped_zip.pwd = @file.expand_path(directory_name)
|
|
462
470
|
end
|
|
463
471
|
|
|
464
|
-
def entries(
|
|
472
|
+
def entries(directory_name)
|
|
465
473
|
entries = []
|
|
466
|
-
foreach(
|
|
474
|
+
foreach(directory_name) { |e| entries << e }
|
|
467
475
|
entries
|
|
468
476
|
end
|
|
469
477
|
|
|
470
|
-
def glob(*args
|
|
471
|
-
@
|
|
478
|
+
def glob(*args, &block)
|
|
479
|
+
@mapped_zip.glob(*args, &block)
|
|
472
480
|
end
|
|
473
481
|
|
|
474
|
-
def foreach(
|
|
475
|
-
unless @file.stat(
|
|
476
|
-
raise Errno::ENOTDIR,
|
|
482
|
+
def foreach(directory_name)
|
|
483
|
+
unless @file.stat(directory_name).directory?
|
|
484
|
+
raise Errno::ENOTDIR, directory_name
|
|
477
485
|
end
|
|
478
|
-
|
|
486
|
+
|
|
487
|
+
path = @file.expand_path(directory_name)
|
|
479
488
|
path << '/' unless path.end_with?('/')
|
|
480
489
|
path = Regexp.escape(path)
|
|
481
|
-
|
|
482
|
-
@
|
|
483
|
-
|
|
484
|
-
match
|
|
485
|
-
|
|
486
|
-
}
|
|
490
|
+
subdir_entry_regex = Regexp.new("^#{path}([^/]+)$")
|
|
491
|
+
@mapped_zip.each do |filename|
|
|
492
|
+
match = subdir_entry_regex.match(filename)
|
|
493
|
+
yield(match[1]) unless match.nil?
|
|
494
|
+
end
|
|
487
495
|
end
|
|
488
496
|
|
|
489
|
-
def delete(
|
|
490
|
-
unless @file.stat(
|
|
491
|
-
raise Errno::EINVAL, "Invalid argument - #{
|
|
497
|
+
def delete(entry_name)
|
|
498
|
+
unless @file.stat(entry_name).directory?
|
|
499
|
+
raise Errno::EINVAL, "Invalid argument - #{entry_name}"
|
|
492
500
|
end
|
|
493
|
-
|
|
501
|
+
|
|
502
|
+
@mapped_zip.remove(entry_name)
|
|
494
503
|
end
|
|
495
|
-
alias rmdir
|
|
504
|
+
alias rmdir delete
|
|
496
505
|
alias unlink delete
|
|
497
506
|
|
|
498
|
-
def mkdir(
|
|
499
|
-
@
|
|
507
|
+
def mkdir(entry_name, permissions = 0o755)
|
|
508
|
+
@mapped_zip.mkdir(entry_name, permissions)
|
|
500
509
|
end
|
|
501
510
|
|
|
502
|
-
def chroot(*
|
|
503
|
-
raise NotImplementedError,
|
|
511
|
+
def chroot(*_args)
|
|
512
|
+
raise NotImplementedError, 'The chroot() function is not implemented'
|
|
504
513
|
end
|
|
505
|
-
|
|
506
514
|
end
|
|
507
515
|
|
|
508
516
|
class ZipFsDirIterator # :nodoc:all
|
|
509
517
|
include Enumerable
|
|
510
518
|
|
|
511
|
-
def initialize(
|
|
512
|
-
@
|
|
519
|
+
def initialize(filenames)
|
|
520
|
+
@filenames = filenames
|
|
513
521
|
@index = 0
|
|
514
522
|
end
|
|
515
523
|
|
|
516
524
|
def close
|
|
517
|
-
@
|
|
525
|
+
@filenames = nil
|
|
518
526
|
end
|
|
519
527
|
|
|
520
|
-
def each(&
|
|
521
|
-
raise IOError,
|
|
522
|
-
|
|
528
|
+
def each(&a_proc)
|
|
529
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
|
530
|
+
|
|
531
|
+
@filenames.each(&a_proc)
|
|
523
532
|
end
|
|
524
533
|
|
|
525
534
|
def read
|
|
526
|
-
raise IOError,
|
|
527
|
-
|
|
535
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
|
536
|
+
|
|
537
|
+
@filenames[(@index += 1) - 1]
|
|
528
538
|
end
|
|
529
539
|
|
|
530
540
|
def rewind
|
|
531
|
-
raise IOError,
|
|
541
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
|
542
|
+
|
|
532
543
|
@index = 0
|
|
533
544
|
end
|
|
534
545
|
|
|
535
|
-
def seek(
|
|
536
|
-
raise IOError,
|
|
537
|
-
|
|
546
|
+
def seek(position)
|
|
547
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
|
548
|
+
|
|
549
|
+
@index = position
|
|
538
550
|
end
|
|
539
551
|
|
|
540
552
|
def tell
|
|
541
|
-
raise IOError,
|
|
553
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
|
554
|
+
|
|
542
555
|
@index
|
|
543
556
|
end
|
|
544
557
|
end
|
|
545
558
|
|
|
546
|
-
# All access to
|
|
559
|
+
# All access to Zip::File from ZipFsFile and ZipFsDir goes through a
|
|
547
560
|
# ZipFileNameMapper, which has one responsibility: ensure
|
|
548
561
|
class ZipFileNameMapper # :nodoc:all
|
|
549
562
|
include Enumerable
|
|
550
563
|
|
|
551
|
-
def initialize(
|
|
552
|
-
@
|
|
553
|
-
@pwd =
|
|
564
|
+
def initialize(zip_file)
|
|
565
|
+
@zip_file = zip_file
|
|
566
|
+
@pwd = '/'
|
|
554
567
|
end
|
|
555
568
|
|
|
556
569
|
attr_accessor :pwd
|
|
557
570
|
|
|
558
|
-
def find_entry(
|
|
559
|
-
@
|
|
571
|
+
def find_entry(filename)
|
|
572
|
+
@zip_file.find_entry(expand_to_entry(filename))
|
|
560
573
|
end
|
|
561
574
|
|
|
562
|
-
def get_entry(
|
|
563
|
-
@
|
|
575
|
+
def get_entry(filename)
|
|
576
|
+
@zip_file.get_entry(expand_to_entry(filename))
|
|
564
577
|
end
|
|
565
578
|
|
|
566
|
-
def get_input_stream(
|
|
567
|
-
@
|
|
579
|
+
def get_input_stream(filename, &a_proc)
|
|
580
|
+
@zip_file.get_input_stream(expand_to_entry(filename), &a_proc)
|
|
568
581
|
end
|
|
569
582
|
|
|
570
|
-
def get_output_stream(
|
|
571
|
-
@
|
|
583
|
+
def get_output_stream(filename, permissions = nil, &a_proc)
|
|
584
|
+
@zip_file.get_output_stream(
|
|
585
|
+
expand_to_entry(filename), permissions, &a_proc
|
|
586
|
+
)
|
|
572
587
|
end
|
|
573
588
|
|
|
574
|
-
def
|
|
575
|
-
@
|
|
589
|
+
def glob(pattern, *flags, &block)
|
|
590
|
+
@zip_file.glob(expand_to_entry(pattern), *flags, &block)
|
|
576
591
|
end
|
|
577
592
|
|
|
578
|
-
def
|
|
579
|
-
@
|
|
593
|
+
def read(filename)
|
|
594
|
+
@zip_file.read(expand_to_entry(filename))
|
|
580
595
|
end
|
|
581
596
|
|
|
582
|
-
def
|
|
583
|
-
@
|
|
584
|
-
&continueOnExistsProc)
|
|
597
|
+
def remove(filename)
|
|
598
|
+
@zip_file.remove(expand_to_entry(filename))
|
|
585
599
|
end
|
|
586
600
|
|
|
587
|
-
def
|
|
588
|
-
@
|
|
601
|
+
def rename(filename, new_name, &continue_on_exists_proc)
|
|
602
|
+
@zip_file.rename(
|
|
603
|
+
expand_to_entry(filename),
|
|
604
|
+
expand_to_entry(new_name),
|
|
605
|
+
&continue_on_exists_proc
|
|
606
|
+
)
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
def mkdir(filename, permissions = 0o755)
|
|
610
|
+
@zip_file.mkdir(expand_to_entry(filename), permissions)
|
|
589
611
|
end
|
|
590
612
|
|
|
591
613
|
# Turns entries into strings and adds leading /
|
|
592
614
|
# and removes trailing slash on directories
|
|
593
615
|
def each
|
|
594
|
-
@
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
616
|
+
@zip_file.each do |e|
|
|
617
|
+
yield('/' + e.to_s.chomp('/'))
|
|
618
|
+
end
|
|
598
619
|
end
|
|
599
620
|
|
|
600
|
-
def expand_path(
|
|
601
|
-
expanded =
|
|
602
|
-
expanded.gsub!(/\/\.(\/|$)/,
|
|
603
|
-
expanded.gsub!(/[^\/]+\/\.\.(\/|$)/,
|
|
604
|
-
expanded.empty? ?
|
|
621
|
+
def expand_path(path)
|
|
622
|
+
expanded = path.start_with?('/') ? path.dup : ::File.join(@pwd, path)
|
|
623
|
+
expanded.gsub!(/\/\.(\/|$)/, '')
|
|
624
|
+
expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, '')
|
|
625
|
+
expanded.empty? ? '/' : expanded
|
|
605
626
|
end
|
|
606
627
|
|
|
607
628
|
private
|
|
608
629
|
|
|
609
|
-
def expand_to_entry(
|
|
610
|
-
expand_path(
|
|
630
|
+
def expand_to_entry(path)
|
|
631
|
+
expand_path(path)[1..-1]
|
|
611
632
|
end
|
|
612
633
|
end
|
|
613
634
|
end
|