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