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.

Files changed (43) hide show
  1. data/ChangeLog +151 -17
  2. data/NEWS +13 -0
  3. data/README +2 -0
  4. data/Rakefile +2 -3
  5. data/TODO +2 -3
  6. data/lib/download_quizzes.rb +119 -0
  7. data/lib/quiz1/t/solutions/Bill Guindon/solitaire.rb +205 -0
  8. data/lib/quiz1/t/solutions/Carlos/solitaire.rb +111 -0
  9. data/lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb +111 -0
  10. data/lib/quiz1/t/solutions/Florian Gross/solitaire.rb +301 -0
  11. data/lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb +268 -0
  12. data/lib/quiz1/t/solutions/James Edward Gray II/solitaire.rb +132 -0
  13. data/lib/quiz1/t/solutions/Jamis Buck/bin/main.rb +13 -0
  14. data/lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb +230 -0
  15. data/lib/quiz1/t/solutions/Jamis Buck/lib/cli.rb +24 -0
  16. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_deck.rb +30 -0
  17. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_key-stream.rb +19 -0
  18. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_keying-algorithms.rb +31 -0
  19. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_solitaire-cipher.rb +66 -0
  20. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_unkeyed-algorithm.rb +17 -0
  21. data/lib/quiz1/t/solutions/Jamis Buck/test/tests.rb +2 -0
  22. data/lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb +204 -0
  23. data/lib/quiz1/t/solutions/Jim Menard/test.rb +47 -0
  24. data/lib/quiz1/t/solutions/Moses Hohman/cipher.rb +97 -0
  25. data/lib/quiz1/t/solutions/Moses Hohman/deck.rb +140 -0
  26. data/lib/quiz1/t/solutions/Moses Hohman/solitaire.rb +14 -0
  27. data/lib/quiz1/t/solutions/Moses Hohman/test_cipher.rb +68 -0
  28. data/lib/quiz1/t/solutions/Moses Hohman/test_deck.rb +146 -0
  29. data/lib/quiz1/t/solutions/Moses Hohman/test_util.rb +38 -0
  30. data/lib/quiz1/t/solutions/Moses Hohman/testsuite.rb +5 -0
  31. data/lib/quiz1/t/solutions/Moses Hohman/util.rb +27 -0
  32. data/lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb +151 -0
  33. data/lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb +198 -0
  34. data/lib/zip/ioextras.rb +17 -15
  35. data/lib/zip/zip.rb +394 -112
  36. data/lib/zip/zipfilesystem.rb +2 -2
  37. data/test/gentestfiles.rb +3 -1
  38. data/test/zipfilesystemtest.rb +2 -2
  39. data/test/ziptest.rb +34 -29
  40. metadata +35 -12
  41. data/samples/zipdialogui.rb +0 -80
  42. data/test/data/file2.txt.other +0 -0
  43. 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.5.12'
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
- IO::SEEK_SET) if @currentEntry
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.read
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
- @zlibInflater.inflate(@inputStream.read(Decompressor::CHUNK_SIZE))
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
- @fstype = 0 # default is fat
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
- return (%r{\/$} =~ @name) != nil
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
- ! directory?
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
- localHeader ,
502
+ @header_signature ,
368
503
  @version ,
369
504
  @fstype ,
370
- @gpFlags ,
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 (localHeader == LOCAL_ENTRY_SIGNATURE)
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 , # @gpFlags ,
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
- cdirSignature ,
569
+
570
+ @header_signature ,
436
571
  @version , # version of encoding software
437
572
  @fstype , # filesystem type
438
573
  @versionNeededToExtract,
439
- @gpFlags ,
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 (cdirSignature == CENTRAL_DIRECTORY_ENTRY_SIGNATURE)
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 , # @gpFlags ,
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 == ZipEntry
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 == other.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
- def get_input_stream
529
- zis = ZipInputStream.new(@zipfile, localHeaderOffset)
530
- zis.get_next_entry
531
- if block_given?
532
- begin
533
- return yield(zis)
534
- ensure
535
- zis.close
536
- end
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
- return zis
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
- aZipOutputStream.copy_raw_entry(self)
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
- if is_directory(newEntry, srcPath)
1103
- @entrySet << ZipStreamableDirectory.new(newEntry)
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
- if foundEntry.is_directory
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 = 0) #permissionInt ignored
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(ZipEntry.new(name, entryName.to_s.ensure_end("/")))
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 ZipStreamableFile < DelegateClass(ZipEntry) #:nodoc:all
1270
- def initialize(entry, filepath)
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
- def get_input_stream(&aProc)
1300
- return yield(NullInputStream.instance) if block_given?
1301
- NullInputStream.instance
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