mezza-rubyzip 0.9.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.
@@ -0,0 +1,137 @@
1
+ module Zip
2
+ class ZipCentralDirectory
3
+ include Enumerable
4
+
5
+ END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50
6
+ MAX_END_OF_CENTRAL_DIRECTORY_STRUCTURE_SIZE = 65536 + 18
7
+ STATIC_EOCD_SIZE = 22
8
+
9
+ attr_reader :comment
10
+
11
+ # Returns an Enumerable containing the entries.
12
+ def entries
13
+ @entrySet.entries
14
+ end
15
+
16
+ def initialize(entries = ZipEntrySet.new, comment = "") #:nodoc:
17
+ super()
18
+ @entrySet = entries.kind_of?(ZipEntrySet) ? entries : ZipEntrySet.new(entries)
19
+ @comment = comment
20
+ end
21
+
22
+ def write_to_stream(io) #:nodoc:
23
+ offset = io.tell
24
+ @entrySet.each { |entry| entry.write_c_dir_entry(io) }
25
+ write_e_o_c_d(io, offset)
26
+ end
27
+
28
+ def write_e_o_c_d(io, offset) #:nodoc:
29
+ io <<
30
+ [END_OF_CENTRAL_DIRECTORY_SIGNATURE,
31
+ 0 , # @numberOfThisDisk
32
+ 0 , # @numberOfDiskWithStartOfCDir
33
+ @entrySet? @entrySet.size : 0 ,
34
+ @entrySet? @entrySet.size : 0 ,
35
+ cdir_size ,
36
+ offset ,
37
+ @comment ? @comment.length : 0 ].pack('VvvvvVVv')
38
+ io << @comment
39
+ end
40
+ private :write_e_o_c_d
41
+
42
+ def cdir_size #:nodoc:
43
+ # does not include eocd
44
+ @entrySet.inject(0) { |value, entry| entry.cdir_header_size + value }
45
+ end
46
+ private :cdir_size
47
+
48
+ def read_e_o_c_d(io) #:nodoc:
49
+ buf = get_e_o_c_d(io)
50
+ @numberOfThisDisk = ZipEntry::read_zip_short(buf)
51
+ @numberOfDiskWithStartOfCDir = ZipEntry::read_zip_short(buf)
52
+ @totalNumberOfEntriesInCDirOnThisDisk = ZipEntry::read_zip_short(buf)
53
+ @size = ZipEntry::read_zip_short(buf)
54
+ @sizeInBytes = ZipEntry::read_zip_long(buf)
55
+ @cdirOffset = ZipEntry::read_zip_long(buf)
56
+ commentLength = ZipEntry::read_zip_short(buf)
57
+ @comment = buf.read(commentLength)
58
+ raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0
59
+ end
60
+
61
+ def read_central_directory_entries(io) #:nodoc:
62
+ begin
63
+ io.seek(@cdirOffset, IO::SEEK_SET)
64
+ rescue Errno::EINVAL
65
+ raise ZipError, "Zip consistency problem while reading central directory entry"
66
+ end
67
+ @entrySet = ZipEntrySet.new
68
+ @size.times {
69
+ @entrySet << ZipEntry.read_c_dir_entry(io)
70
+ }
71
+ end
72
+
73
+ def read_from_stream(io) #:nodoc:
74
+ read_e_o_c_d(io)
75
+ read_central_directory_entries(io)
76
+ end
77
+
78
+ def get_e_o_c_d(io) #:nodoc:
79
+ begin
80
+ io.seek(-MAX_END_OF_CENTRAL_DIRECTORY_STRUCTURE_SIZE, IO::SEEK_END)
81
+ rescue Errno::EINVAL
82
+ io.seek(0, IO::SEEK_SET)
83
+ rescue Errno::EFBIG # FreeBSD 4.9 raise Errno::EFBIG instead of Errno::EINVAL
84
+ io.seek(0, IO::SEEK_SET)
85
+ end
86
+
87
+ # 'buf = io.read' substituted with lump of code to work around FreeBSD 4.5 issue
88
+ retried = false
89
+ buf = nil
90
+ begin
91
+ buf = io.read
92
+ rescue Errno::EFBIG # FreeBSD 4.5 may raise Errno::EFBIG
93
+ raise if (retried)
94
+ retried = true
95
+
96
+ io.seek(0, IO::SEEK_SET)
97
+ retry
98
+ end
99
+
100
+ sigIndex = buf.rindex([END_OF_CENTRAL_DIRECTORY_SIGNATURE].pack('V'))
101
+ raise ZipError, "Zip end of central directory signature not found" unless sigIndex
102
+ buf=buf.slice!((sigIndex+4)...(buf.size))
103
+ def buf.read(count)
104
+ slice!(0, count)
105
+ end
106
+ return buf
107
+ end
108
+
109
+ # For iterating over the entries.
110
+ def each(&proc)
111
+ @entrySet.each(&proc)
112
+ end
113
+
114
+ # Returns the number of entries in the central directory (and
115
+ # consequently in the zip archive).
116
+ def size
117
+ @entrySet.size
118
+ end
119
+
120
+ def ZipCentralDirectory.read_from_stream(io) #:nodoc:
121
+ cdir = new
122
+ cdir.read_from_stream(io)
123
+ return cdir
124
+ rescue ZipError
125
+ return nil
126
+ end
127
+
128
+ def == (other) #:nodoc:
129
+ return false unless other.kind_of?(ZipCentralDirectory)
130
+ @entrySet.entries.sort == other.entries.sort && comment == other.comment
131
+ end
132
+ end
133
+ end
134
+
135
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
136
+ # rubyzip is free software; you can redistribute it and/or
137
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,631 @@
1
+ module Zip
2
+ class ZipEntry
3
+ STORED = 0
4
+ DEFLATED = 8
5
+
6
+ FSTYPE_FAT = 0
7
+ FSTYPE_AMIGA = 1
8
+ FSTYPE_VMS = 2
9
+ FSTYPE_UNIX = 3
10
+ FSTYPE_VM_CMS = 4
11
+ FSTYPE_ATARI = 5
12
+ FSTYPE_HPFS = 6
13
+ FSTYPE_MAC = 7
14
+ FSTYPE_Z_SYSTEM = 8
15
+ FSTYPE_CPM = 9
16
+ FSTYPE_TOPS20 = 10
17
+ FSTYPE_NTFS = 11
18
+ FSTYPE_QDOS = 12
19
+ FSTYPE_ACORN = 13
20
+ FSTYPE_VFAT = 14
21
+ FSTYPE_MVS = 15
22
+ FSTYPE_BEOS = 16
23
+ FSTYPE_TANDEM = 17
24
+ FSTYPE_THEOS = 18
25
+ FSTYPE_MAC_OSX = 19
26
+ FSTYPE_ATHEOS = 30
27
+
28
+ FSTYPES = {
29
+ FSTYPE_FAT => 'FAT'.freeze,
30
+ FSTYPE_AMIGA => 'Amiga'.freeze,
31
+ FSTYPE_VMS => 'VMS (Vax or Alpha AXP)'.freeze,
32
+ FSTYPE_UNIX => 'Unix'.freeze,
33
+ FSTYPE_VM_CMS => 'VM/CMS'.freeze,
34
+ FSTYPE_ATARI => 'Atari ST'.freeze,
35
+ FSTYPE_HPFS => 'OS/2 or NT HPFS'.freeze,
36
+ FSTYPE_MAC => 'Macintosh'.freeze,
37
+ FSTYPE_Z_SYSTEM => 'Z-System'.freeze,
38
+ FSTYPE_CPM => 'CP/M'.freeze,
39
+ FSTYPE_TOPS20 => 'TOPS-20'.freeze,
40
+ FSTYPE_NTFS => 'NTFS'.freeze,
41
+ FSTYPE_QDOS => 'SMS/QDOS'.freeze,
42
+ FSTYPE_ACORN => 'Acorn RISC OS'.freeze,
43
+ FSTYPE_VFAT => 'Win32 VFAT'.freeze,
44
+ FSTYPE_MVS => 'MVS'.freeze,
45
+ FSTYPE_BEOS => 'BeOS'.freeze,
46
+ FSTYPE_TANDEM => 'Tandem NSK'.freeze,
47
+ FSTYPE_THEOS => 'Theos'.freeze,
48
+ FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze,
49
+ FSTYPE_ATHEOS => 'AtheOS'.freeze,
50
+ }.freeze
51
+
52
+ attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
53
+ :name, :size, :localHeaderOffset, :zipfile, :fstype, :externalFileAttributes, :gp_flags, :header_signature
54
+
55
+ attr_accessor :follow_symlinks
56
+ attr_accessor :restore_times, :restore_permissions, :restore_ownership
57
+ attr_accessor :unix_uid, :unix_gid, :unix_perms
58
+
59
+ attr_reader :ftype, :filepath # :nodoc:
60
+
61
+ # Returns the character encoding used for name and comment
62
+ def name_encoding
63
+ (@gp_flags & 0b100000000000) != 0 ? "utf8" : "CP437//"
64
+ end
65
+
66
+ # Returns the name in the encoding specified by enc
67
+ def name_in(enc)
68
+ Iconv.conv(enc, name_encoding, @name)
69
+ end
70
+
71
+ # Returns the name in the encoding specified by enc
72
+ def comment_in(enc)
73
+ Iconv.conv(enc, name_encoding, @name)
74
+ end
75
+
76
+ def initialize(zipfile = "", name = "", comment = "", extra = "",
77
+ compressed_size = 0, crc = 0,
78
+ compression_method = ZipEntry::DEFLATED, size = 0,
79
+ time = Time.now)
80
+ super()
81
+ if name.starts_with("/")
82
+ raise ZipEntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
83
+ end
84
+ @localHeaderOffset = 0
85
+ @local_header_size = 0
86
+ @internalFileAttributes = 1
87
+ @externalFileAttributes = 0
88
+ @version = 52 # this library's version
89
+ @ftype = nil # unspecified or unknown
90
+ @filepath = nil
91
+ if Zip::RUNNING_ON_WINDOWS
92
+ @fstype = FSTYPE_FAT
93
+ else
94
+ @fstype = FSTYPE_UNIX
95
+ end
96
+ @zipfile = zipfile
97
+ @comment = comment
98
+ @compressed_size = compressed_size
99
+ @crc = crc
100
+ @extra = extra
101
+ @compression_method = compression_method
102
+ @name = name
103
+ @size = size
104
+ @time = time
105
+
106
+ @follow_symlinks = false
107
+
108
+ @restore_times = true
109
+ @restore_permissions = false
110
+ @restore_ownership = false
111
+
112
+ # BUG: need an extra field to support uid/gid's
113
+ @unix_uid = nil
114
+ @unix_gid = nil
115
+ @unix_perms = nil
116
+ # @posix_acl = nil
117
+ # @ntfs_acl = nil
118
+
119
+ if name_is_directory?
120
+ @ftype = :directory
121
+ else
122
+ @ftype = :file
123
+ end
124
+
125
+ unless ZipExtraField === @extra
126
+ @extra = ZipExtraField.new(@extra.to_s)
127
+ end
128
+ end
129
+
130
+ def time
131
+ if @extra["UniversalTime"]
132
+ @extra["UniversalTime"].mtime
133
+ else
134
+ # Atandard time field in central directory has local time
135
+ # under archive creator. Then, we can't get timezone.
136
+ @time
137
+ end
138
+ end
139
+ alias :mtime :time
140
+
141
+ def time=(aTime)
142
+ unless @extra.member?("UniversalTime")
143
+ @extra.create("UniversalTime")
144
+ end
145
+ @extra["UniversalTime"].mtime = aTime
146
+ @time = aTime
147
+ end
148
+
149
+ # Returns +true+ if the entry is a directory.
150
+ def directory?
151
+ raise ZipInternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
152
+ @ftype == :directory
153
+ end
154
+ alias :is_directory :directory?
155
+
156
+ # Returns +true+ if the entry is a file.
157
+ def file?
158
+ raise ZipInternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
159
+ @ftype == :file
160
+ end
161
+
162
+ # Returns +true+ if the entry is a symlink.
163
+ def symlink?
164
+ raise ZipInternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
165
+ @ftype == :symlink
166
+ end
167
+
168
+ def name_is_directory? #:nodoc:all
169
+ (%r{\/$} =~ @name) != nil
170
+ end
171
+
172
+ def local_entry_offset #:nodoc:all
173
+ localHeaderOffset + @local_header_size
174
+ end
175
+
176
+ def calculate_local_header_size #:nodoc:all
177
+ LOCAL_ENTRY_STATIC_HEADER_LENGTH + (@name ? @name.size : 0) + (@extra ? @extra.local_size : 0)
178
+ end
179
+
180
+ def cdir_header_size #:nodoc:all
181
+ CDIR_ENTRY_STATIC_HEADER_LENGTH + (@name ? @name.size : 0) +
182
+ (@extra ? @extra.c_dir_size : 0) + (@comment ? @comment.size : 0)
183
+ end
184
+
185
+ def next_header_offset #:nodoc:all
186
+ local_entry_offset + self.compressed_size
187
+ end
188
+
189
+ # Extracts entry to file destPath (defaults to @name).
190
+ def extract(destPath = @name, &onExistsProc)
191
+ onExistsProc ||= proc { false }
192
+
193
+ if directory?
194
+ create_directory(destPath, &onExistsProc)
195
+ elsif file?
196
+ write_file(destPath, &onExistsProc)
197
+ elsif symlink?
198
+ create_symlink(destPath, &onExistsProc)
199
+ else
200
+ raise RuntimeError, "unknown file type #{self.inspect}"
201
+ end
202
+
203
+ self
204
+ end
205
+
206
+ def to_s
207
+ @name
208
+ end
209
+
210
+ protected
211
+
212
+ def ZipEntry.read_zip_short(io) # :nodoc:
213
+ io.read(2).unpack('v')[0]
214
+ end
215
+
216
+ def ZipEntry.read_zip_long(io) # :nodoc:
217
+ io.read(4).unpack('V')[0]
218
+ end
219
+ public
220
+
221
+ LOCAL_ENTRY_SIGNATURE = 0x04034b50
222
+ LOCAL_ENTRY_STATIC_HEADER_LENGTH = 30
223
+ LOCAL_ENTRY_TRAILING_DESCRIPTOR_LENGTH = 4+4+4
224
+ VERSION_NEEDED_TO_EXTRACT = 10
225
+
226
+ def read_local_entry(io) #:nodoc:all
227
+ @localHeaderOffset = io.tell
228
+ staticSizedFieldsBuf = io.read(LOCAL_ENTRY_STATIC_HEADER_LENGTH)
229
+ unless (staticSizedFieldsBuf.size==LOCAL_ENTRY_STATIC_HEADER_LENGTH)
230
+ raise ZipError, "Premature end of file. Not enough data for zip entry local header"
231
+ end
232
+
233
+ @header_signature ,
234
+ @version ,
235
+ @fstype ,
236
+ @gp_flags ,
237
+ @compression_method,
238
+ lastModTime ,
239
+ lastModDate ,
240
+ @crc ,
241
+ @compressed_size ,
242
+ @size ,
243
+ nameLength ,
244
+ extraLength = staticSizedFieldsBuf.unpack('VCCvvvvVVVvv')
245
+
246
+ unless (@header_signature == LOCAL_ENTRY_SIGNATURE)
247
+ raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
248
+ end
249
+ set_time(lastModDate, lastModTime)
250
+
251
+
252
+ @name = io.read(nameLength)
253
+ extra = io.read(extraLength)
254
+
255
+ if (extra && extra.length != extraLength)
256
+ raise ZipError, "Truncated local zip entry header"
257
+ else
258
+ if ZipExtraField === @extra
259
+ @extra.merge(extra)
260
+ else
261
+ @extra = ZipExtraField.new(extra)
262
+ end
263
+ end
264
+ @local_header_size = calculate_local_header_size
265
+ end
266
+
267
+ def ZipEntry.read_local_entry(io)
268
+ entry = new(io.path)
269
+ entry.read_local_entry(io)
270
+ return entry
271
+ rescue ZipError
272
+ return nil
273
+ end
274
+
275
+ def write_local_entry(io) #:nodoc:all
276
+ @localHeaderOffset = io.tell
277
+
278
+ io <<
279
+ [LOCAL_ENTRY_SIGNATURE ,
280
+ VERSION_NEEDED_TO_EXTRACT , # version needed to extract
281
+ 0 , # @gp_flags ,
282
+ @compression_method ,
283
+ @time.to_binary_dos_time , # @lastModTime ,
284
+ @time.to_binary_dos_date , # @lastModDate ,
285
+ @crc ,
286
+ @compressed_size ,
287
+ @size ,
288
+ @name ? @name.length : 0,
289
+ @extra? @extra.local_length : 0 ].pack('VvvvvvVVVvv')
290
+ io << @name
291
+ io << (@extra ? @extra.to_local_bin : "")
292
+ end
293
+
294
+ CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50
295
+ CDIR_ENTRY_STATIC_HEADER_LENGTH = 46
296
+
297
+ def read_c_dir_entry(io) #:nodoc:all
298
+ staticSizedFieldsBuf = io.read(CDIR_ENTRY_STATIC_HEADER_LENGTH)
299
+ unless (staticSizedFieldsBuf.size == CDIR_ENTRY_STATIC_HEADER_LENGTH)
300
+ raise ZipError, "Premature end of file. Not enough data for zip cdir entry header"
301
+ end
302
+
303
+ @header_signature ,
304
+ @version , # version of encoding software
305
+ @fstype , # filesystem type
306
+ @versionNeededToExtract,
307
+ @gp_flags ,
308
+ @compression_method ,
309
+ lastModTime ,
310
+ lastModDate ,
311
+ @crc ,
312
+ @compressed_size ,
313
+ @size ,
314
+ nameLength ,
315
+ extraLength ,
316
+ commentLength ,
317
+ diskNumberStart ,
318
+ @internalFileAttributes,
319
+ @externalFileAttributes,
320
+ @localHeaderOffset ,
321
+ @name ,
322
+ @extra ,
323
+ @comment = staticSizedFieldsBuf.unpack('VCCvvvvvVVVvvvvvVV')
324
+
325
+ unless (@header_signature == CENTRAL_DIRECTORY_ENTRY_SIGNATURE)
326
+ raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
327
+ end
328
+ set_time(lastModDate, lastModTime)
329
+
330
+ @name = io.read(nameLength)
331
+ if ZipExtraField === @extra
332
+ @extra.merge(io.read(extraLength))
333
+ else
334
+ @extra = ZipExtraField.new(io.read(extraLength))
335
+ end
336
+ @comment = io.read(commentLength)
337
+ unless (@comment && @comment.length == commentLength)
338
+ raise ZipError, "Truncated cdir zip entry header"
339
+ end
340
+
341
+ case @fstype
342
+ when FSTYPE_UNIX
343
+ @unix_perms = (@externalFileAttributes >> 16) & 07777
344
+
345
+ case (@externalFileAttributes >> 28)
346
+ when 04
347
+ @ftype = :directory
348
+ when 010
349
+ @ftype = :file
350
+ when 012
351
+ @ftype = :symlink
352
+ else
353
+ @ftype = :unknown
354
+ end
355
+ else
356
+ if name_is_directory?
357
+ @ftype = :directory
358
+ else
359
+ @ftype = :file
360
+ end
361
+ end
362
+ @local_header_size = calculate_local_header_size
363
+ end
364
+
365
+ def ZipEntry.read_c_dir_entry(io) #:nodoc:all
366
+ entry = new(io.path)
367
+ entry.read_c_dir_entry(io)
368
+ return entry
369
+ rescue ZipError
370
+ return nil
371
+ end
372
+
373
+ def file_stat(path) # :nodoc:
374
+ if @follow_symlinks
375
+ return ::File::stat(path)
376
+ else
377
+ return ::File::lstat(path)
378
+ end
379
+ end
380
+
381
+ def get_extra_attributes_from_path(path) # :nodoc:
382
+ unless Zip::RUNNING_ON_WINDOWS
383
+ stat = file_stat(path)
384
+ @unix_uid = stat.uid
385
+ @unix_gid = stat.gid
386
+ @unix_perms = stat.mode & 07777
387
+ end
388
+ end
389
+
390
+ def set_extra_attributes_on_path(destPath) # :nodoc:
391
+ return unless (file? or directory?)
392
+
393
+ case @fstype
394
+ when FSTYPE_UNIX
395
+ # BUG: does not update timestamps into account
396
+ # ignore setuid/setgid bits by default. honor if @restore_ownership
397
+ unix_perms_mask = 01777
398
+ unix_perms_mask = 07777 if (@restore_ownership)
399
+ FileUtils::chmod(@unix_perms & unix_perms_mask, destPath) if (@restore_permissions && @unix_perms)
400
+ FileUtils::chown(@unix_uid, @unix_gid, destPath) if (@restore_ownership && @unix_uid && @unix_gid && Process::egid == 0)
401
+ # File::utimes()
402
+ end
403
+ end
404
+
405
+ def write_c_dir_entry(io) #:nodoc:all
406
+ case @fstype
407
+ when FSTYPE_UNIX
408
+ ft = nil
409
+ case @ftype
410
+ when :file
411
+ ft = 010
412
+ @unix_perms ||= 0644
413
+ when :directory
414
+ ft = 004
415
+ @unix_perms ||= 0755
416
+ when :symlink
417
+ ft = 012
418
+ @unix_perms ||= 0755
419
+ end
420
+
421
+ if (!ft.nil?)
422
+ @externalFileAttributes = (ft << 12 | (@unix_perms & 07777)) << 16
423
+ end
424
+ end
425
+
426
+ io <<
427
+ [CENTRAL_DIRECTORY_ENTRY_SIGNATURE,
428
+ @version , # version of encoding software
429
+ @fstype , # filesystem type
430
+ VERSION_NEEDED_TO_EXTRACT , # @versionNeededToExtract ,
431
+ 0 , # @gp_flags ,
432
+ @compression_method ,
433
+ @time.to_binary_dos_time , # @lastModTime ,
434
+ @time.to_binary_dos_date , # @lastModDate ,
435
+ @crc ,
436
+ @compressed_size ,
437
+ @size ,
438
+ @name ? @name.length : 0 ,
439
+ @extra ? @extra.c_dir_length : 0 ,
440
+ @comment ? @comment.length : 0 ,
441
+ 0 , # disk number start
442
+ @internalFileAttributes , # file type (binary=0, text=1)
443
+ @externalFileAttributes , # native filesystem attributes
444
+ @localHeaderOffset ,
445
+ @name ,
446
+ @extra ,
447
+ @comment ].pack('VCCvvvvvVVVvvvvvVV')
448
+
449
+ io << @name
450
+ io << (@extra ? @extra.to_c_dir_bin : "")
451
+ io << @comment
452
+ end
453
+
454
+ def == (other)
455
+ return false unless other.class == self.class
456
+ # Compares contents of local entry and exposed fields
457
+ (@compression_method == other.compression_method &&
458
+ @crc == other.crc &&
459
+ @compressed_size == other.compressed_size &&
460
+ @size == other.size &&
461
+ @name == other.name &&
462
+ @extra == other.extra &&
463
+ @filepath == other.filepath &&
464
+ self.time.dos_equals(other.time))
465
+ end
466
+
467
+ def <=> (other)
468
+ return to_s <=> other.to_s
469
+ end
470
+
471
+ # Returns an IO like object for the given ZipEntry.
472
+ # Warning: may behave weird with symlinks.
473
+ def get_input_stream(&aProc)
474
+ if @ftype == :directory
475
+ return yield(NullInputStream.instance) if block_given?
476
+ return NullInputStream.instance
477
+ elsif @filepath
478
+ case @ftype
479
+ when :file
480
+ return ::File.open(@filepath, "rb", &aProc)
481
+
482
+ when :symlink
483
+ linkpath = ::File::readlink(@filepath)
484
+ stringio = StringIO.new(linkpath)
485
+ return yield(stringio) if block_given?
486
+ return stringio
487
+ else
488
+ raise "unknown @ftype #{@ftype}"
489
+ end
490
+ else
491
+ zis = ZipInputStream.new(@zipfile, localHeaderOffset)
492
+ zis.get_next_entry
493
+ if block_given?
494
+ begin
495
+ return yield(zis)
496
+ ensure
497
+ zis.close
498
+ end
499
+ else
500
+ return zis
501
+ end
502
+ end
503
+ end
504
+
505
+ def gather_fileinfo_from_srcpath(srcPath) # :nodoc:
506
+ stat = file_stat(srcPath)
507
+ case stat.ftype
508
+ when 'file'
509
+ if name_is_directory?
510
+ raise ArgumentError,
511
+ "entry name '#{newEntry}' indicates directory entry, but "+
512
+ "'#{srcPath}' is not a directory"
513
+ end
514
+ @ftype = :file
515
+ when 'directory'
516
+ if ! name_is_directory?
517
+ @name += "/"
518
+ end
519
+ @ftype = :directory
520
+ when 'link'
521
+ if name_is_directory?
522
+ raise ArgumentError,
523
+ "entry name '#{newEntry}' indicates directory entry, but "+
524
+ "'#{srcPath}' is not a directory"
525
+ end
526
+ @ftype = :symlink
527
+ else
528
+ raise RuntimeError, "unknown file type: #{srcPath.inspect} #{stat.inspect}"
529
+ end
530
+
531
+ @filepath = srcPath
532
+ get_extra_attributes_from_path(@filepath)
533
+ end
534
+
535
+ def write_to_zip_output_stream(aZipOutputStream) #:nodoc:all
536
+ if @ftype == :directory
537
+ aZipOutputStream.put_next_entry(self)
538
+ elsif @filepath
539
+ aZipOutputStream.put_next_entry(self)
540
+ get_input_stream { |is| IOExtras.copy_stream(aZipOutputStream, is) }
541
+ else
542
+ aZipOutputStream.copy_raw_entry(self)
543
+ end
544
+ end
545
+
546
+ def parent_as_string
547
+ entry_name = name.chomp("/")
548
+ slash_index = entry_name.rindex("/")
549
+ slash_index ? entry_name.slice(0, slash_index+1) : nil
550
+ end
551
+
552
+ def get_raw_input_stream(&aProc)
553
+ ::File.open(@zipfile, "rb", &aProc)
554
+ end
555
+
556
+ private
557
+
558
+ def set_time(binaryDosDate, binaryDosTime)
559
+ @time = Time.parse_binary_dos_format(binaryDosDate, binaryDosTime)
560
+ rescue ArgumentError
561
+ puts "Invalid date/time in zip entry"
562
+ end
563
+
564
+ def write_file(destPath, continueOnExistsProc = proc { false })
565
+ if ::File.exists?(destPath) && ! yield(self, destPath)
566
+ raise ZipDestinationFileExistsError,
567
+ "Destination '#{destPath}' already exists"
568
+ end
569
+ ::File.open(destPath, "wb") do |os|
570
+ get_input_stream do |is|
571
+ set_extra_attributes_on_path(destPath)
572
+
573
+ buf = ''
574
+ while buf = is.sysread(Decompressor::CHUNK_SIZE, buf)
575
+ os << buf
576
+ end
577
+ end
578
+ end
579
+ end
580
+
581
+ def create_directory(destPath)
582
+ if ::File.directory? destPath
583
+ return
584
+ elsif ::File.exists? destPath
585
+ if block_given? && yield(self, destPath)
586
+ FileUtils::rm_f destPath
587
+ else
588
+ raise ZipDestinationFileExistsError,
589
+ "Cannot create directory '#{destPath}'. "+
590
+ "A file already exists with that name"
591
+ end
592
+ end
593
+ Dir.mkdir destPath
594
+ set_extra_attributes_on_path(destPath)
595
+ end
596
+
597
+ # BUG: create_symlink() does not use &onExistsProc
598
+ def create_symlink(destPath)
599
+ stat = nil
600
+ begin
601
+ stat = ::File::lstat(destPath)
602
+ rescue Errno::ENOENT
603
+ end
604
+
605
+ io = get_input_stream
606
+ linkto = io.read
607
+
608
+ if stat
609
+ if stat.symlink?
610
+ if ::File::readlink(destPath) == linkto
611
+ return
612
+ else
613
+ raise ZipDestinationFileExistsError,
614
+ "Cannot create symlink '#{destPath}'. "+
615
+ "A symlink already exists with that name"
616
+ end
617
+ else
618
+ raise ZipDestinationFileExistsError,
619
+ "Cannot create symlink '#{destPath}'. "+
620
+ "A file already exists with that name"
621
+ end
622
+ end
623
+
624
+ ::File::symlink(linkto, destPath)
625
+ end
626
+ end
627
+ end
628
+
629
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
630
+ # rubyzip is free software; you can redistribute it and/or
631
+ # modify it under the terms of the ruby license.