mezza-rubyzip 0.9.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.