rubyzip 0.5.12 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubyzip might be problematic. Click here for more details.
- data/ChangeLog +151 -17
- data/NEWS +13 -0
- data/README +2 -0
- data/Rakefile +2 -3
- data/TODO +2 -3
- data/lib/download_quizzes.rb +119 -0
- data/lib/quiz1/t/solutions/Bill Guindon/solitaire.rb +205 -0
- data/lib/quiz1/t/solutions/Carlos/solitaire.rb +111 -0
- data/lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb +111 -0
- data/lib/quiz1/t/solutions/Florian Gross/solitaire.rb +301 -0
- data/lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb +268 -0
- data/lib/quiz1/t/solutions/James Edward Gray II/solitaire.rb +132 -0
- data/lib/quiz1/t/solutions/Jamis Buck/bin/main.rb +13 -0
- data/lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb +230 -0
- data/lib/quiz1/t/solutions/Jamis Buck/lib/cli.rb +24 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_deck.rb +30 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_key-stream.rb +19 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_keying-algorithms.rb +31 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_solitaire-cipher.rb +66 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_unkeyed-algorithm.rb +17 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tests.rb +2 -0
- data/lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb +204 -0
- data/lib/quiz1/t/solutions/Jim Menard/test.rb +47 -0
- data/lib/quiz1/t/solutions/Moses Hohman/cipher.rb +97 -0
- data/lib/quiz1/t/solutions/Moses Hohman/deck.rb +140 -0
- data/lib/quiz1/t/solutions/Moses Hohman/solitaire.rb +14 -0
- data/lib/quiz1/t/solutions/Moses Hohman/test_cipher.rb +68 -0
- data/lib/quiz1/t/solutions/Moses Hohman/test_deck.rb +146 -0
- data/lib/quiz1/t/solutions/Moses Hohman/test_util.rb +38 -0
- data/lib/quiz1/t/solutions/Moses Hohman/testsuite.rb +5 -0
- data/lib/quiz1/t/solutions/Moses Hohman/util.rb +27 -0
- data/lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb +151 -0
- data/lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb +198 -0
- data/lib/zip/ioextras.rb +17 -15
- data/lib/zip/zip.rb +394 -112
- data/lib/zip/zipfilesystem.rb +2 -2
- data/test/gentestfiles.rb +3 -1
- data/test/zipfilesystemtest.rb +2 -2
- data/test/ziptest.rb +34 -29
- metadata +35 -12
- data/samples/zipdialogui.rb +0 -80
- data/test/data/file2.txt.other +0 -0
- data/test/zlibtest.rb +0 -26
data/lib/zip/zip.rb
CHANGED
@@ -2,6 +2,7 @@ require 'delegate'
|
|
2
2
|
require 'singleton'
|
3
3
|
require 'tempfile'
|
4
4
|
require 'ftools'
|
5
|
+
require 'stringio'
|
5
6
|
require 'zlib'
|
6
7
|
require 'zip/stdrubyext'
|
7
8
|
require 'zip/ioextras'
|
@@ -19,10 +20,12 @@ end
|
|
19
20
|
|
20
21
|
module Zip
|
21
22
|
|
22
|
-
VERSION = '0.
|
23
|
+
VERSION = '0.9.1'
|
23
24
|
|
24
25
|
RUBY_MINOR_VERSION = RUBY_VERSION.split(".")[1].to_i
|
25
26
|
|
27
|
+
RUNNING_ON_WINDOWS = /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
|
28
|
+
|
26
29
|
# Ruby 1.7.x compatibility
|
27
30
|
# In ruby 1.6.x and 1.8.0 reading from an empty stream returns
|
28
31
|
# an empty string the first time and then nil.
|
@@ -104,9 +107,10 @@ module Zip
|
|
104
107
|
# method on a newly created ZipInputStream before reading from
|
105
108
|
# the first entry in the archive. Returns nil when there are
|
106
109
|
# no more entries.
|
110
|
+
|
107
111
|
def get_next_entry
|
108
112
|
@archiveIO.seek(@currentEntry.next_header_offset,
|
109
|
-
|
113
|
+
IO::SEEK_SET) if @currentEntry
|
110
114
|
open_entry
|
111
115
|
end
|
112
116
|
|
@@ -119,11 +123,16 @@ module Zip
|
|
119
123
|
open_entry
|
120
124
|
end
|
121
125
|
|
122
|
-
# Modeled after IO.
|
126
|
+
# Modeled after IO.sysread
|
123
127
|
def sysread(numberOfBytes = nil, buf = nil)
|
124
128
|
@decompressor.sysread(numberOfBytes, buf)
|
125
129
|
end
|
126
130
|
|
131
|
+
def eof
|
132
|
+
@outputBuffer.empty? && @decompressor.eof
|
133
|
+
end
|
134
|
+
alias :eof? :eof
|
135
|
+
|
127
136
|
protected
|
128
137
|
|
129
138
|
def open_entry
|
@@ -146,7 +155,7 @@ module Zip
|
|
146
155
|
def produce_input
|
147
156
|
@decompressor.produce_input
|
148
157
|
end
|
149
|
-
|
158
|
+
|
150
159
|
def input_finished?
|
151
160
|
@decompressor.input_finished?
|
152
161
|
end
|
@@ -170,12 +179,11 @@ module Zip
|
|
170
179
|
@hasReturnedEmptyString = ! EMPTY_FILE_RETURNS_EMPTY_STRING_FIRST
|
171
180
|
end
|
172
181
|
|
173
|
-
# BUG: do something with buf
|
174
182
|
def sysread(numberOfBytes = nil, buf = nil)
|
175
183
|
readEverything = (numberOfBytes == nil)
|
176
184
|
while (readEverything || @outputBuffer.length < numberOfBytes)
|
177
185
|
break if internal_input_finished?
|
178
|
-
@outputBuffer << internal_produce_input
|
186
|
+
@outputBuffer << internal_produce_input(buf)
|
179
187
|
end
|
180
188
|
return value_when_finished if @outputBuffer.length==0 && input_finished?
|
181
189
|
endIndex= numberOfBytes==nil ? @outputBuffer.length : numberOfBytes
|
@@ -191,14 +199,24 @@ module Zip
|
|
191
199
|
end
|
192
200
|
|
193
201
|
# to be used with produce_input, not read (as read may still have more data cached)
|
202
|
+
# is data cached anywhere other than @outputBuffer? the comment above may be wrong
|
194
203
|
def input_finished?
|
195
204
|
@outputBuffer.empty? && internal_input_finished?
|
196
205
|
end
|
206
|
+
alias :eof :input_finished?
|
207
|
+
alias :eof? :input_finished?
|
197
208
|
|
198
209
|
private
|
199
210
|
|
200
|
-
def internal_produce_input
|
201
|
-
|
211
|
+
def internal_produce_input(buf = nil)
|
212
|
+
retried = 0
|
213
|
+
begin
|
214
|
+
@zlibInflater.inflate(@inputStream.read(Decompressor::CHUNK_SIZE, buf))
|
215
|
+
rescue Zlib::BufError
|
216
|
+
raise if (retried >= 5) # how many times should we retry?
|
217
|
+
retried += 1
|
218
|
+
retry
|
219
|
+
end
|
202
220
|
end
|
203
221
|
|
204
222
|
def internal_input_finished?
|
@@ -244,6 +262,8 @@ module Zip
|
|
244
262
|
def input_finished?
|
245
263
|
(@readSoFar >= @charsToRead)
|
246
264
|
end
|
265
|
+
alias :eof :input_finished?
|
266
|
+
alias :eof? :input_finished?
|
247
267
|
end
|
248
268
|
|
249
269
|
class NullDecompressor #:nodoc:all
|
@@ -259,6 +279,11 @@ module Zip
|
|
259
279
|
def input_finished?
|
260
280
|
true
|
261
281
|
end
|
282
|
+
|
283
|
+
def eof
|
284
|
+
true
|
285
|
+
end
|
286
|
+
alias :eof? :eof
|
262
287
|
end
|
263
288
|
|
264
289
|
class NullInputStream < NullDecompressor #:nodoc:all
|
@@ -268,9 +293,61 @@ module Zip
|
|
268
293
|
class ZipEntry
|
269
294
|
STORED = 0
|
270
295
|
DEFLATED = 8
|
296
|
+
|
297
|
+
FSTYPE_FAT = 0
|
298
|
+
FSTYPE_AMIGA = 1
|
299
|
+
FSTYPE_VMS = 2
|
300
|
+
FSTYPE_UNIX = 3
|
301
|
+
FSTYPE_VM_CMS = 4
|
302
|
+
FSTYPE_ATARI = 5
|
303
|
+
FSTYPE_HPFS = 6
|
304
|
+
FSTYPE_MAC = 7
|
305
|
+
FSTYPE_Z_SYSTEM = 8
|
306
|
+
FSTYPE_CPM = 9
|
307
|
+
FSTYPE_TOPS20 = 10
|
308
|
+
FSTYPE_NTFS = 11
|
309
|
+
FSTYPE_QDOS = 12
|
310
|
+
FSTYPE_ACORN = 13
|
311
|
+
FSTYPE_VFAT = 14
|
312
|
+
FSTYPE_MVS = 15
|
313
|
+
FSTYPE_BEOS = 16
|
314
|
+
FSTYPE_TANDEM = 17
|
315
|
+
FSTYPE_THEOS = 18
|
316
|
+
FSTYPE_MAC_OSX = 19
|
317
|
+
FSTYPE_ATHEOS = 30
|
318
|
+
|
319
|
+
FSTYPES = {
|
320
|
+
FSTYPE_FAT => 'FAT'.freeze,
|
321
|
+
FSTYPE_AMIGA => 'Amiga'.freeze,
|
322
|
+
FSTYPE_VMS => 'VMS (Vax or Alpha AXP)'.freeze,
|
323
|
+
FSTYPE_UNIX => 'Unix'.freeze,
|
324
|
+
FSTYPE_VM_CMS => 'VM/CMS'.freeze,
|
325
|
+
FSTYPE_ATARI => 'Atari ST'.freeze,
|
326
|
+
FSTYPE_HPFS => 'OS/2 or NT HPFS'.freeze,
|
327
|
+
FSTYPE_MAC => 'Macintosh'.freeze,
|
328
|
+
FSTYPE_Z_SYSTEM => 'Z-System'.freeze,
|
329
|
+
FSTYPE_CPM => 'CP/M'.freeze,
|
330
|
+
FSTYPE_TOPS20 => 'TOPS-20'.freeze,
|
331
|
+
FSTYPE_NTFS => 'NTFS'.freeze,
|
332
|
+
FSTYPE_QDOS => 'SMS/QDOS'.freeze,
|
333
|
+
FSTYPE_ACORN => 'Acorn RISC OS'.freeze,
|
334
|
+
FSTYPE_VFAT => 'Win32 VFAT'.freeze,
|
335
|
+
FSTYPE_MVS => 'MVS'.freeze,
|
336
|
+
FSTYPE_BEOS => 'BeOS'.freeze,
|
337
|
+
FSTYPE_TANDEM => 'Tandem NSK'.freeze,
|
338
|
+
FSTYPE_THEOS => 'Theos'.freeze,
|
339
|
+
FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze,
|
340
|
+
FSTYPE_ATHEOS => 'AtheOS'.freeze,
|
341
|
+
}.freeze
|
271
342
|
|
272
343
|
attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
|
273
|
-
:name, :size, :localHeaderOffset, :zipfile, :fstype, :externalFileAttributes
|
344
|
+
:name, :size, :localHeaderOffset, :zipfile, :fstype, :externalFileAttributes, :gp_flags, :header_signature
|
345
|
+
|
346
|
+
attr_accessor :follow_symlinks
|
347
|
+
attr_accessor :restore_times, :restore_permissions, :restore_ownership
|
348
|
+
attr_accessor :unix_uid, :unix_gid, :unix_perms
|
349
|
+
|
350
|
+
attr_reader :ftype, :filepath # :nodoc:
|
274
351
|
|
275
352
|
def initialize(zipfile = "", name = "", comment = "", extra = "",
|
276
353
|
compressed_size = 0, crc = 0,
|
@@ -284,11 +361,37 @@ module Zip
|
|
284
361
|
@internalFileAttributes = 1
|
285
362
|
@externalFileAttributes = 0
|
286
363
|
@version = 52 # this library's version
|
287
|
-
@
|
364
|
+
@ftype = nil # unspecified or unknown
|
365
|
+
@filepath = nil
|
366
|
+
if Zip::RUNNING_ON_WINDOWS
|
367
|
+
@fstype = FSTYPE_FAT
|
368
|
+
else
|
369
|
+
@fstype = FSTYPE_UNIX
|
370
|
+
end
|
288
371
|
@zipfile, @comment, @compressed_size, @crc, @extra, @compression_method,
|
289
372
|
@name, @size = zipfile, comment, compressed_size, crc,
|
290
373
|
extra, compression_method, name, size
|
291
374
|
@time = time
|
375
|
+
|
376
|
+
@follow_symlinks = false
|
377
|
+
|
378
|
+
@restore_times = true
|
379
|
+
@restore_permissions = false
|
380
|
+
@restore_ownership = false
|
381
|
+
|
382
|
+
# BUG: need an extra field to support uid/gid's
|
383
|
+
@unix_uid = nil
|
384
|
+
@unix_gid = nil
|
385
|
+
@unix_perms = nil
|
386
|
+
# @posix_acl = nil
|
387
|
+
# @ntfs_acl = nil
|
388
|
+
|
389
|
+
if name_is_directory?
|
390
|
+
@ftype = :directory
|
391
|
+
else
|
392
|
+
@ftype = :file
|
393
|
+
end
|
394
|
+
|
292
395
|
unless ZipExtraField === @extra
|
293
396
|
@extra = ZipExtraField.new(@extra.to_s)
|
294
397
|
end
|
@@ -313,13 +416,27 @@ module Zip
|
|
313
416
|
@time = aTime
|
314
417
|
end
|
315
418
|
|
419
|
+
# Returns +true+ if the entry is a directory.
|
316
420
|
def directory?
|
317
|
-
|
421
|
+
raise ZipInternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
|
422
|
+
@ftype == :directory
|
318
423
|
end
|
319
424
|
alias :is_directory :directory?
|
320
425
|
|
426
|
+
# Returns +true+ if the entry is a file.
|
321
427
|
def file?
|
322
|
-
|
428
|
+
raise ZipInternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
|
429
|
+
@ftype == :file
|
430
|
+
end
|
431
|
+
|
432
|
+
# Returns +true+ if the entry is a symlink.
|
433
|
+
def symlink?
|
434
|
+
raise ZipInternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
|
435
|
+
@ftype == :link
|
436
|
+
end
|
437
|
+
|
438
|
+
def name_is_directory? #:nodoc:all
|
439
|
+
(%r{\/$} =~ @name) != nil
|
323
440
|
end
|
324
441
|
|
325
442
|
def local_entry_offset #:nodoc:all
|
@@ -338,24 +455,42 @@ module Zip
|
|
338
455
|
def next_header_offset #:nodoc:all
|
339
456
|
local_entry_offset + self.compressed_size
|
340
457
|
end
|
341
|
-
|
458
|
+
|
459
|
+
# Extracts entry to file destPath (defaults to @name).
|
460
|
+
def extract(destPath = @name, &onExistsProc)
|
461
|
+
onExistsProc ||= proc { false }
|
462
|
+
|
463
|
+
if directory?
|
464
|
+
create_directory(destPath, &onExistsProc)
|
465
|
+
elsif file?
|
466
|
+
write_file(destPath, &onExistsProc)
|
467
|
+
elsif symlink?
|
468
|
+
create_symlink(destPath, &onExistsProc)
|
469
|
+
else
|
470
|
+
raise RuntimeError, "unknown file type #{self.inspect}"
|
471
|
+
end
|
472
|
+
|
473
|
+
self
|
474
|
+
end
|
475
|
+
|
342
476
|
def to_s
|
343
477
|
@name
|
344
478
|
end
|
345
479
|
|
346
480
|
protected
|
347
481
|
|
348
|
-
def ZipEntry.read_zip_short(io)
|
482
|
+
def ZipEntry.read_zip_short(io) # :nodoc:
|
349
483
|
io.read(2).unpack('v')[0]
|
350
484
|
end
|
351
485
|
|
352
|
-
def ZipEntry.read_zip_long(io)
|
486
|
+
def ZipEntry.read_zip_long(io) # :nodoc:
|
353
487
|
io.read(4).unpack('V')[0]
|
354
488
|
end
|
355
489
|
public
|
356
490
|
|
357
491
|
LOCAL_ENTRY_SIGNATURE = 0x04034b50
|
358
492
|
LOCAL_ENTRY_STATIC_HEADER_LENGTH = 30
|
493
|
+
LOCAL_ENTRY_TRAILING_DESCRIPTOR_LENGTH = 4+4+4
|
359
494
|
|
360
495
|
def read_local_entry(io) #:nodoc:all
|
361
496
|
@localHeaderOffset = io.tell
|
@@ -364,10 +499,10 @@ module Zip
|
|
364
499
|
raise ZipError, "Premature end of file. Not enough data for zip entry local header"
|
365
500
|
end
|
366
501
|
|
367
|
-
|
502
|
+
@header_signature ,
|
368
503
|
@version ,
|
369
504
|
@fstype ,
|
370
|
-
@
|
505
|
+
@gp_flags ,
|
371
506
|
@compression_method,
|
372
507
|
lastModTime ,
|
373
508
|
lastModDate ,
|
@@ -377,7 +512,7 @@ module Zip
|
|
377
512
|
nameLength ,
|
378
513
|
extraLength = staticSizedFieldsBuf.unpack('VCCvvvvVVVvv')
|
379
514
|
|
380
|
-
unless (
|
515
|
+
unless (@header_signature == LOCAL_ENTRY_SIGNATURE)
|
381
516
|
raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
|
382
517
|
end
|
383
518
|
set_time(lastModDate, lastModTime)
|
@@ -410,7 +545,7 @@ module Zip
|
|
410
545
|
io <<
|
411
546
|
[LOCAL_ENTRY_SIGNATURE ,
|
412
547
|
0 ,
|
413
|
-
0 , # @
|
548
|
+
0 , # @gp_flags ,
|
414
549
|
@compression_method ,
|
415
550
|
@time.to_binary_dos_time , # @lastModTime ,
|
416
551
|
@time.to_binary_dos_date , # @lastModDate ,
|
@@ -431,12 +566,12 @@ module Zip
|
|
431
566
|
unless (staticSizedFieldsBuf.size == CDIR_ENTRY_STATIC_HEADER_LENGTH)
|
432
567
|
raise ZipError, "Premature end of file. Not enough data for zip cdir entry header"
|
433
568
|
end
|
434
|
-
|
435
|
-
|
569
|
+
|
570
|
+
@header_signature ,
|
436
571
|
@version , # version of encoding software
|
437
572
|
@fstype , # filesystem type
|
438
573
|
@versionNeededToExtract,
|
439
|
-
@
|
574
|
+
@gp_flags ,
|
440
575
|
@compression_method ,
|
441
576
|
lastModTime ,
|
442
577
|
lastModDate ,
|
@@ -454,7 +589,7 @@ module Zip
|
|
454
589
|
@extra ,
|
455
590
|
@comment = staticSizedFieldsBuf.unpack('VCCvvvvvVVVvvvvvVV')
|
456
591
|
|
457
|
-
unless (
|
592
|
+
unless (@header_signature == CENTRAL_DIRECTORY_ENTRY_SIGNATURE)
|
458
593
|
raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
|
459
594
|
end
|
460
595
|
set_time(lastModDate, lastModTime)
|
@@ -469,6 +604,28 @@ module Zip
|
|
469
604
|
unless (@comment && @comment.length == commentLength)
|
470
605
|
raise ZipError, "Truncated cdir zip entry header"
|
471
606
|
end
|
607
|
+
|
608
|
+
case @fstype
|
609
|
+
when FSTYPE_UNIX
|
610
|
+
@unix_perms = (@externalFileAttributes >> 16) & 07777
|
611
|
+
|
612
|
+
case (@externalFileAttributes >> 28)
|
613
|
+
when 04
|
614
|
+
@ftype = :directory
|
615
|
+
when 010
|
616
|
+
@ftype = :file
|
617
|
+
when 012
|
618
|
+
@ftype = :link
|
619
|
+
else
|
620
|
+
raise ZipInternalError, "unknown file type #{'0%o' % (@externalFileAttributes >> 28)}"
|
621
|
+
end
|
622
|
+
else
|
623
|
+
if name_is_directory?
|
624
|
+
@ftype = :directory
|
625
|
+
else
|
626
|
+
@ftype = :file
|
627
|
+
end
|
628
|
+
end
|
472
629
|
end
|
473
630
|
|
474
631
|
def ZipEntry.read_c_dir_entry(io) #:nodoc:all
|
@@ -479,14 +636,65 @@ module Zip
|
|
479
636
|
return nil
|
480
637
|
end
|
481
638
|
|
639
|
+
def file_stat(path) # :nodoc:
|
640
|
+
if @follow_symlinks
|
641
|
+
return File::stat(path)
|
642
|
+
else
|
643
|
+
return File::lstat(path)
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
def get_extra_attributes_from_path(path) # :nodoc:
|
648
|
+
unless Zip::RUNNING_ON_WINDOWS
|
649
|
+
stat = file_stat(path)
|
650
|
+
@unix_uid = stat.uid
|
651
|
+
@unix_gid = stat.gid
|
652
|
+
@unix_perms = stat.mode & 07777
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
def set_extra_attributes_on_path(destPath) # :nodoc:
|
657
|
+
return unless (file? or directory?)
|
658
|
+
|
659
|
+
case @fstype
|
660
|
+
when FSTYPE_UNIX
|
661
|
+
# BUG: does not update timestamps into account
|
662
|
+
# ignore setuid/setgid bits by default. honor if @restore_ownership
|
663
|
+
unix_perms_mask = 01777
|
664
|
+
unix_perms_mask = 07777 if (@restore_ownership)
|
665
|
+
File::chmod(@unix_perms & unix_perms_mask, destPath) if (@restore_permissions && @unix_perms)
|
666
|
+
File::chown(@unix_uid, @unix_gid, destPath) if (@restore_ownership && @unix_uid && @unix_gid && Process::egid == 0)
|
667
|
+
# File::utimes()
|
668
|
+
end
|
669
|
+
end
|
482
670
|
|
483
671
|
def write_c_dir_entry(io) #:nodoc:all
|
672
|
+
case @fstype
|
673
|
+
when FSTYPE_UNIX
|
674
|
+
ft = nil
|
675
|
+
case @ftype
|
676
|
+
when :file
|
677
|
+
ft = 010
|
678
|
+
@unix_perms ||= 0644
|
679
|
+
when :directory
|
680
|
+
ft = 004
|
681
|
+
@unix_perms ||= 0755
|
682
|
+
when :symlink
|
683
|
+
ft = 012
|
684
|
+
@unix_perms ||= 0755
|
685
|
+
else
|
686
|
+
raise ZipInternalError, "unknown file type #{self.inspect}"
|
687
|
+
end
|
688
|
+
|
689
|
+
@externalFileAttributes = (ft << 12 | (@unix_perms & 07777)) << 16
|
690
|
+
end
|
691
|
+
|
484
692
|
io <<
|
485
693
|
[CENTRAL_DIRECTORY_ENTRY_SIGNATURE,
|
486
694
|
@version , # version of encoding software
|
487
695
|
@fstype , # filesystem type
|
488
696
|
0 , # @versionNeededToExtract ,
|
489
|
-
0 , # @
|
697
|
+
0 , # @gp_flags ,
|
490
698
|
@compression_method ,
|
491
699
|
@time.to_binary_dos_time , # @lastModTime ,
|
492
700
|
@time.to_binary_dos_date , # @lastModDate ,
|
@@ -510,14 +718,15 @@ module Zip
|
|
510
718
|
end
|
511
719
|
|
512
720
|
def == (other)
|
513
|
-
return false unless other.class ==
|
721
|
+
return false unless other.class == self.class
|
514
722
|
# Compares contents of local entry and exposed fields
|
515
723
|
(@compression_method == other.compression_method &&
|
516
724
|
@crc == other.crc &&
|
517
|
-
@compressed_size
|
725
|
+
@compressed_size == other.compressed_size &&
|
518
726
|
@size == other.size &&
|
519
727
|
@name == other.name &&
|
520
728
|
@extra == other.extra &&
|
729
|
+
@filepath == other.filepath &&
|
521
730
|
self.time.dos_equals(other.time))
|
522
731
|
end
|
523
732
|
|
@@ -525,23 +734,79 @@ module Zip
|
|
525
734
|
return to_s <=> other.to_s
|
526
735
|
end
|
527
736
|
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
if
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
737
|
+
# Returns an IO like object for the given ZipEntry.
|
738
|
+
# Warning: may behave weird with symlinks.
|
739
|
+
def get_input_stream(&aProc)
|
740
|
+
if @ftype == :directory
|
741
|
+
return yield(NullInputStream.instance) if block_given?
|
742
|
+
return NullInputStream.instance
|
743
|
+
elsif @filepath
|
744
|
+
case @ftype
|
745
|
+
when :file
|
746
|
+
return File.open(@filepath, "rb", &aProc)
|
747
|
+
|
748
|
+
when :symlink
|
749
|
+
linkpath = File::readlink(@filepath)
|
750
|
+
stringio = StringIO.new(linkpath)
|
751
|
+
return yield(stringio) if block_given?
|
752
|
+
return stringio
|
753
|
+
else
|
754
|
+
raise "unknown @ftype #{@ftype}"
|
755
|
+
end
|
537
756
|
else
|
538
|
-
|
757
|
+
zis = ZipInputStream.new(@zipfile, localHeaderOffset)
|
758
|
+
zis.get_next_entry
|
759
|
+
if block_given?
|
760
|
+
begin
|
761
|
+
return yield(zis)
|
762
|
+
ensure
|
763
|
+
zis.close
|
764
|
+
end
|
765
|
+
else
|
766
|
+
return zis
|
767
|
+
end
|
539
768
|
end
|
540
769
|
end
|
541
770
|
|
771
|
+
def gather_fileinfo_from_srcpath(srcPath) # :nodoc:
|
772
|
+
stat = file_stat(srcPath)
|
773
|
+
case stat.ftype
|
774
|
+
when 'file'
|
775
|
+
if name_is_directory?
|
776
|
+
raise ArgumentError,
|
777
|
+
"entry name '#{newEntry}' indicates directory entry, but "+
|
778
|
+
"'#{srcPath}' is not a directory"
|
779
|
+
end
|
780
|
+
@ftype = :file
|
781
|
+
when 'directory'
|
782
|
+
if ! name_is_directory?
|
783
|
+
@name += "/"
|
784
|
+
end
|
785
|
+
@ftype = :directory
|
786
|
+
when 'link'
|
787
|
+
if name_is_directory?
|
788
|
+
raise ArgumentError,
|
789
|
+
"entry name '#{newEntry}' indicates directory entry, but "+
|
790
|
+
"'#{srcPath}' is not a directory"
|
791
|
+
end
|
792
|
+
@ftype = :symlink
|
793
|
+
else
|
794
|
+
raise RuntimeError, "unknown file type: #{srcPath.inspect} #{stat.inspect}"
|
795
|
+
end
|
796
|
+
|
797
|
+
@filepath = srcPath
|
798
|
+
get_extra_attributes_from_path(@filepath)
|
799
|
+
end
|
542
800
|
|
543
801
|
def write_to_zip_output_stream(aZipOutputStream) #:nodoc:all
|
544
|
-
|
802
|
+
if @ftype == :directory
|
803
|
+
aZipOutputStream.put_next_entry(self)
|
804
|
+
elsif @filepath
|
805
|
+
aZipOutputStream.put_next_entry(self)
|
806
|
+
get_input_stream { |is| IOExtras.copy_stream(aZipOutputStream, is) }
|
807
|
+
else
|
808
|
+
aZipOutputStream.copy_raw_entry(self)
|
809
|
+
end
|
545
810
|
end
|
546
811
|
|
547
812
|
def parent_as_string
|
@@ -555,11 +820,75 @@ module Zip
|
|
555
820
|
end
|
556
821
|
|
557
822
|
private
|
823
|
+
|
558
824
|
def set_time(binaryDosDate, binaryDosTime)
|
559
825
|
@time = Time.parse_binary_dos_format(binaryDosDate, binaryDosTime)
|
560
826
|
rescue ArgumentError
|
561
827
|
puts "Invalid date/time in zip entry"
|
562
828
|
end
|
829
|
+
|
830
|
+
def write_file(destPath, continueOnExistsProc = proc { false })
|
831
|
+
if File.exists?(destPath) && ! yield(self, destPath)
|
832
|
+
raise ZipDestinationFileExistsError,
|
833
|
+
"Destination '#{destPath}' already exists"
|
834
|
+
end
|
835
|
+
File.open(destPath, "wb") do |os|
|
836
|
+
get_input_stream do |is|
|
837
|
+
set_extra_attributes_on_path(destPath)
|
838
|
+
|
839
|
+
buf = ''
|
840
|
+
while buf = is.sysread(Decompressor::CHUNK_SIZE, buf)
|
841
|
+
os << buf
|
842
|
+
end
|
843
|
+
end
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
847
|
+
def create_directory(destPath)
|
848
|
+
if File.directory? destPath
|
849
|
+
return
|
850
|
+
elsif File.exists? destPath
|
851
|
+
if block_given? && yield(self, destPath)
|
852
|
+
File.rm_f destPath
|
853
|
+
else
|
854
|
+
raise ZipDestinationFileExistsError,
|
855
|
+
"Cannot create directory '#{destPath}'. "+
|
856
|
+
"A file already exists with that name"
|
857
|
+
end
|
858
|
+
end
|
859
|
+
Dir.mkdir destPath
|
860
|
+
set_extra_attributes_on_path(destPath)
|
861
|
+
end
|
862
|
+
|
863
|
+
# BUG: create_symlink() does not use &onExistsProc
|
864
|
+
def create_symlink(destPath)
|
865
|
+
stat = nil
|
866
|
+
begin
|
867
|
+
stat = File::lstat(destPath)
|
868
|
+
rescue Errno::ENOENT
|
869
|
+
end
|
870
|
+
|
871
|
+
io = get_input_stream
|
872
|
+
linkto = io.read
|
873
|
+
|
874
|
+
if stat
|
875
|
+
if stat.symlink?
|
876
|
+
if File::readlink(destPath) == linkto
|
877
|
+
return
|
878
|
+
else
|
879
|
+
raise ZipDestinationFileExistsError,
|
880
|
+
"Cannot create symlink '#{destPath}'. "+
|
881
|
+
"A symlink already exists with that name"
|
882
|
+
end
|
883
|
+
else
|
884
|
+
raise ZipDestinationFileExistsError,
|
885
|
+
"Cannot create symlink '#{destPath}'. "+
|
886
|
+
"A file already exists with that name"
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
File::symlink(linkto, destPath)
|
891
|
+
end
|
563
892
|
end
|
564
893
|
|
565
894
|
|
@@ -625,7 +954,7 @@ module Zip
|
|
625
954
|
def put_next_entry(entry, level = Zlib::DEFAULT_COMPRESSION)
|
626
955
|
raise ZipError, "zip stream is closed" if @closed
|
627
956
|
newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@fileName, entry.to_s)
|
628
|
-
init_next_entry(newEntry)
|
957
|
+
init_next_entry(newEntry, level)
|
629
958
|
@currentEntry=newEntry
|
630
959
|
end
|
631
960
|
|
@@ -964,6 +1293,7 @@ module Zip
|
|
964
1293
|
class ZipDestinationFileExistsError < ZipError; end
|
965
1294
|
class ZipCompressionMethodError < ZipError; end
|
966
1295
|
class ZipEntryNameError < ZipError; end
|
1296
|
+
class ZipInternalError < ZipError; end
|
967
1297
|
|
968
1298
|
# ZipFile is modeled after java.util.zip.ZipFile from the Java SDK.
|
969
1299
|
# The most important methods are those inherited from
|
@@ -1014,6 +1344,13 @@ module Zip
|
|
1014
1344
|
|
1015
1345
|
attr_reader :name
|
1016
1346
|
|
1347
|
+
# default -> false
|
1348
|
+
attr_accessor :restore_ownership
|
1349
|
+
# default -> false
|
1350
|
+
attr_accessor :restore_permissions
|
1351
|
+
# default -> true
|
1352
|
+
attr_accessor :restore_times
|
1353
|
+
|
1017
1354
|
# Opens a zip archive. Pass true as the second parameter to create
|
1018
1355
|
# a new archive if it doesn't exist already.
|
1019
1356
|
def initialize(fileName, create = nil)
|
@@ -1029,6 +1366,10 @@ module Zip
|
|
1029
1366
|
end
|
1030
1367
|
@create = create
|
1031
1368
|
@storedEntries = @entrySet.dup
|
1369
|
+
|
1370
|
+
@restore_ownership = false
|
1371
|
+
@restore_permissions = false
|
1372
|
+
@restore_times = true
|
1032
1373
|
end
|
1033
1374
|
|
1034
1375
|
# Same as #new. If a block is passed the ZipFile object is passed
|
@@ -1099,11 +1440,8 @@ module Zip
|
|
1099
1440
|
continueOnExistsProc ||= proc { false }
|
1100
1441
|
check_entry_exists(entry, continueOnExistsProc, "add")
|
1101
1442
|
newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@name, entry.to_s)
|
1102
|
-
|
1103
|
-
|
1104
|
-
else
|
1105
|
-
@entrySet << ZipStreamableFile.new(newEntry, srcPath)
|
1106
|
-
end
|
1443
|
+
newEntry.gather_fileinfo_from_srcpath(srcPath)
|
1444
|
+
@entrySet << newEntry
|
1107
1445
|
end
|
1108
1446
|
|
1109
1447
|
# Removes the specified entry.
|
@@ -1129,11 +1467,7 @@ module Zip
|
|
1129
1467
|
def extract(entry, destPath, &onExistsProc)
|
1130
1468
|
onExistsProc ||= proc { false }
|
1131
1469
|
foundEntry = get_entry(entry)
|
1132
|
-
|
1133
|
-
create_directory(foundEntry, destPath, &onExistsProc)
|
1134
|
-
else
|
1135
|
-
write_file(foundEntry, destPath, &onExistsProc)
|
1136
|
-
end
|
1470
|
+
foundEntry.extract(destPath, &onExistsProc)
|
1137
1471
|
end
|
1138
1472
|
|
1139
1473
|
# Commits changes that has been made since the previous commit to
|
@@ -1180,34 +1514,23 @@ module Zip
|
|
1180
1514
|
unless selectedEntry
|
1181
1515
|
raise Errno::ENOENT, entry
|
1182
1516
|
end
|
1517
|
+
selectedEntry.restore_ownership = @restore_ownership
|
1518
|
+
selectedEntry.restore_permissions = @restore_permissions
|
1519
|
+
selectedEntry.restore_times = @restore_times
|
1520
|
+
|
1183
1521
|
return selectedEntry
|
1184
1522
|
end
|
1185
1523
|
|
1186
1524
|
# Creates a directory
|
1187
|
-
def mkdir(entryName, permissionInt =
|
1525
|
+
def mkdir(entryName, permissionInt = 0755)
|
1188
1526
|
if find_entry(entryName)
|
1189
1527
|
raise Errno::EEXIST, "File exists - #{entryName}"
|
1190
1528
|
end
|
1191
|
-
@entrySet << ZipStreamableDirectory.new(
|
1529
|
+
@entrySet << ZipStreamableDirectory.new(@name, entryName.to_s.ensure_end("/"), nil, permissionInt)
|
1192
1530
|
end
|
1193
1531
|
|
1194
1532
|
private
|
1195
1533
|
|
1196
|
-
def create_directory(entry, destPath)
|
1197
|
-
if File.directory? destPath
|
1198
|
-
return
|
1199
|
-
elsif File.exists? destPath
|
1200
|
-
if block_given? && yield(entry, destPath)
|
1201
|
-
File.rm_f destPath
|
1202
|
-
else
|
1203
|
-
raise ZipDestinationFileExistsError,
|
1204
|
-
"Cannot create directory '#{destPath}'. "+
|
1205
|
-
"A file already exists with that name"
|
1206
|
-
end
|
1207
|
-
end
|
1208
|
-
Dir.mkdir destPath
|
1209
|
-
end
|
1210
|
-
|
1211
1534
|
def is_directory(newEntry, srcPath)
|
1212
1535
|
srcPathIsDirectory = File.directory?(srcPath)
|
1213
1536
|
if newEntry.is_directory && ! srcPathIsDirectory
|
@@ -1232,17 +1555,6 @@ module Zip
|
|
1232
1555
|
end
|
1233
1556
|
end
|
1234
1557
|
|
1235
|
-
def write_file(entry, destPath, continueOnExistsProc = proc { false })
|
1236
|
-
if File.exists?(destPath) && ! yield(entry, destPath)
|
1237
|
-
raise ZipDestinationFileExistsError,
|
1238
|
-
"Destination '#{destPath}' already exists"
|
1239
|
-
end
|
1240
|
-
File.open(destPath, "wb") {
|
1241
|
-
|os|
|
1242
|
-
entry.get_input_stream { |is| os << is.read }
|
1243
|
-
}
|
1244
|
-
end
|
1245
|
-
|
1246
1558
|
def check_file(path)
|
1247
1559
|
unless File.readable? path
|
1248
1560
|
raise Errno::ENOENT, path
|
@@ -1266,43 +1578,13 @@ module Zip
|
|
1266
1578
|
|
1267
1579
|
end
|
1268
1580
|
|
1269
|
-
class
|
1270
|
-
def initialize(entry,
|
1271
|
-
super(entry)
|
1272
|
-
@delegate = entry
|
1273
|
-
@filepath = filepath
|
1274
|
-
end
|
1275
|
-
|
1276
|
-
def get_input_stream(&aProc)
|
1277
|
-
File.open(@filepath, "rb", &aProc)
|
1278
|
-
end
|
1279
|
-
|
1280
|
-
def write_to_zip_output_stream(aZipOutputStream)
|
1281
|
-
aZipOutputStream.put_next_entry(self)
|
1282
|
-
get_input_stream { |is| IOExtras.copy_stream(aZipOutputStream, is) }
|
1283
|
-
end
|
1284
|
-
|
1285
|
-
def == (other)
|
1286
|
-
return false unless other.class == ZipStreamableFile
|
1287
|
-
@filepath == other.filepath && super(other.delegate)
|
1288
|
-
end
|
1289
|
-
|
1290
|
-
protected
|
1291
|
-
attr_reader :filepath, :delegate
|
1292
|
-
end
|
1293
|
-
|
1294
|
-
class ZipStreamableDirectory < DelegateClass(ZipEntry) #:nodoc:all
|
1295
|
-
def initialize(entry)
|
1296
|
-
super(entry)
|
1297
|
-
end
|
1581
|
+
class ZipStreamableDirectory < ZipEntry
|
1582
|
+
def initialize(zipfile, entry, srcPath = nil, permissionInt = nil)
|
1583
|
+
super(zipfile, entry)
|
1298
1584
|
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
end
|
1303
|
-
|
1304
|
-
def write_to_zip_output_stream(aZipOutputStream)
|
1305
|
-
aZipOutputStream.put_next_entry(self)
|
1585
|
+
@ftype = :directory
|
1586
|
+
entry.get_extra_attributes_from_path(srcPath) if (srcPath)
|
1587
|
+
@unix_perms = permissionInt if (permissionInt)
|
1306
1588
|
end
|
1307
1589
|
end
|
1308
1590
|
|