superp-rubyzip 0.1.0

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,86 @@
1
+ module Zip
2
+ class EntrySet #:nodoc:all
3
+ include Enumerable
4
+ attr_accessor :entry_set, :entry_order
5
+
6
+ def initialize(an_enumerable = [])
7
+ super()
8
+ @entry_set = {}
9
+ @entry_order = []
10
+ an_enumerable.each { |o| push(o) }
11
+ end
12
+
13
+ def include?(entry)
14
+ @entry_set.include?(to_key(entry))
15
+ end
16
+
17
+ def find_entry(entry)
18
+ @entry_set[to_key(entry)]
19
+ end
20
+
21
+ def <<(entry)
22
+ @entry_order.delete(to_key(entry))
23
+ @entry_order << to_key(entry)
24
+ @entry_set[to_key(entry)] = entry
25
+ end
26
+
27
+ alias :push :<<
28
+
29
+ def size
30
+ @entry_set.size
31
+ end
32
+
33
+ alias :length :size
34
+
35
+ def delete(entry)
36
+ if @entry_order.delete(to_key(entry)) && @entry_set.delete(to_key(entry))
37
+ entry
38
+ else
39
+ nil
40
+ end
41
+ end
42
+
43
+ def each(&block)
44
+ @entry_order.each do |key|
45
+ block.call @entry_set[key]
46
+ end
47
+ end
48
+
49
+ def entries
50
+ @entry_order.map { |key| @entry_set[key] }
51
+ end
52
+
53
+ # deep clone
54
+ def dup
55
+ EntrySet.new(@entry_order.map { |key| @entry_set[key].dup })
56
+ end
57
+
58
+ def ==(other)
59
+ return false unless other.kind_of?(EntrySet)
60
+ @entry_set == other.entry_set && @entry_order == other.entry_order
61
+ end
62
+
63
+ def parent(entry)
64
+ @entry_set[to_key(entry.parent_as_string)]
65
+ end
66
+
67
+ def glob(pattern, flags = ::File::FNM_PATHNAME|::File::FNM_DOTMATCH)
68
+ entries.map do |entry|
69
+ next nil unless ::File.fnmatch(pattern, entry.name.chomp('/'), flags)
70
+ yield(entry) if block_given?
71
+ entry
72
+ end.compact
73
+ end
74
+
75
+ protected
76
+
77
+ private
78
+ def to_key(entry)
79
+ entry.to_s.sub(/\/$/, '')
80
+ end
81
+ end
82
+ end
83
+
84
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
85
+ # rubyzip is free software; you can redistribute it and/or
86
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,8 @@
1
+ module Zip
2
+ class ZipError < StandardError; end
3
+ class ZipEntryExistsError < ZipError; end
4
+ class ZipDestinationFileExistsError < ZipError; end
5
+ class ZipCompressionMethodError < ZipError; end
6
+ class ZipEntryNameError < ZipError; end
7
+ class ZipInternalError < ZipError; end
8
+ end
@@ -0,0 +1,90 @@
1
+ module Zip
2
+ class ExtraField < Hash
3
+ ID_MAP = {}
4
+
5
+ def initialize(binstr = nil)
6
+ binstr and merge(binstr)
7
+ end
8
+
9
+ def extra_field_type_exist(binstr, id, len, i)
10
+ field_name = ID_MAP[id].name
11
+ if self.member?(field_name)
12
+ self[field_name].merge(binstr[i, len + 4])
13
+ else
14
+ field_obj = ID_MAP[id].new(binstr[i, len + 4])
15
+ self[field_name] = field_obj
16
+ end
17
+ end
18
+
19
+ def extra_field_type_unknown(binstr, len, i)
20
+ create_unknown_item unless self['Unknown']
21
+ if !len || len + 4 > binstr[i..-1].bytesize
22
+ self['Unknown'] << binstr[i..-1]
23
+ return
24
+ end
25
+ self['Unknown'] << binstr[i, len + 4]
26
+ end
27
+
28
+ def create_unknown_item
29
+ s = ''
30
+ class << s
31
+ alias_method :to_c_dir_bin, :to_s
32
+ alias_method :to_local_bin, :to_s
33
+ end
34
+ self['Unknown'] = s
35
+ end
36
+
37
+ def merge(binstr)
38
+ return if binstr.empty?
39
+ i = 0
40
+ while i < binstr.bytesize
41
+ id = binstr[i, 2]
42
+ len = binstr[i + 2, 2].to_s.unpack('v').first
43
+ if id && ID_MAP.member?(id)
44
+ extra_field_type_exist(binstr, id, len, i)
45
+ elsif id
46
+ create_unknown_item unless self['Unknown']
47
+ break unless extra_field_type_unknown(binstr, len, i)
48
+ end
49
+ i += len + 4
50
+ end
51
+ end
52
+
53
+ def create(name)
54
+ unless field_class = ID_MAP.values.find { |k| k.name == name }
55
+ raise ZipError, "Unknown extra field '#{name}'"
56
+ end
57
+ self[name] = field_class.new
58
+ end
59
+
60
+ def to_local_bin
61
+ self.map { |_, v| v.to_local_bin }.join
62
+ end
63
+
64
+ alias :to_s :to_local_bin
65
+
66
+ def to_c_dir_bin
67
+ self.map { |_, v| v.to_c_dir_bin }.join
68
+ end
69
+
70
+ def c_dir_length
71
+ to_c_dir_bin.bytesize
72
+ end
73
+
74
+ def local_length
75
+ to_local_bin.bytesize
76
+ end
77
+
78
+ alias :c_dir_size :c_dir_length
79
+ alias :local_size :local_length
80
+ alias :length :local_length
81
+ alias :size :local_length
82
+ end
83
+ end
84
+
85
+ require 'zip/extra_field/generic'
86
+ require 'zip/extra_field/universal_time'
87
+ require 'zip/extra_field/unix'
88
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
89
+ # rubyzip is free software; you can redistribute it and/or
90
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,43 @@
1
+ module Zip
2
+ class ExtraField::Generic
3
+ def self.register_map
4
+ if self.const_defined?(:HEADER_ID)
5
+ ::Zip::ExtraField::ID_MAP[self.const_get(:HEADER_ID)] = self
6
+ end
7
+ end
8
+
9
+ def self.name
10
+ self.to_s.split("::")[-1]
11
+ end
12
+
13
+ # return field [size, content] or false
14
+ def initial_parse(binstr)
15
+ if !binstr
16
+ # If nil, start with empty.
17
+ return false
18
+ elsif binstr[0, 2] != self.class.const_get(:HEADER_ID)
19
+ $stderr.puts "Warning: weired extra feild header ID. skip parsing"
20
+ return false
21
+ end
22
+ [binstr[2, 2].unpack("v")[0], binstr[4..-1]]
23
+ end
24
+
25
+ def ==(other)
26
+ return false if self.class != other.class
27
+ each do |k, v|
28
+ v != other[k] and return false
29
+ end
30
+ true
31
+ end
32
+
33
+ def to_local_bin
34
+ s = pack_for_local
35
+ self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v") + s
36
+ end
37
+
38
+ def to_c_dir_bin
39
+ s = pack_for_c_dir
40
+ self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v") + s
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,47 @@
1
+ module Zip
2
+ # Info-ZIP Additional timestamp field
3
+ class ExtraField::UniversalTime < ExtraField::Generic
4
+ HEADER_ID = "UT"
5
+ register_map
6
+
7
+ def initialize(binstr = nil)
8
+ @ctime = nil
9
+ @mtime = nil
10
+ @atime = nil
11
+ @flag = nil
12
+ binstr and merge(binstr)
13
+ end
14
+
15
+ attr_accessor :atime, :ctime, :mtime, :flag
16
+
17
+ def merge(binstr)
18
+ return if binstr.empty?
19
+ size, content = initial_parse(binstr)
20
+ size or return
21
+ @flag, mtime, atime, ctime = content.unpack("CVVV")
22
+ mtime and @mtime ||= ::Zip::DOSTime.at(mtime)
23
+ atime and @atime ||= ::Zip::DOSTime.at(atime)
24
+ ctime and @ctime ||= ::Zip::DOSTime.at(ctime)
25
+ end
26
+
27
+ def ==(other)
28
+ @mtime == other.mtime &&
29
+ @atime == other.atime &&
30
+ @ctime == other.ctime
31
+ end
32
+
33
+ def pack_for_local
34
+ s = [@flag].pack("C")
35
+ @flag & 1 != 0 and s << [@mtime.to_i].pack("V")
36
+ @flag & 2 != 0 and s << [@atime.to_i].pack("V")
37
+ @flag & 4 != 0 and s << [@ctime.to_i].pack("V")
38
+ s
39
+ end
40
+
41
+ def pack_for_c_dir
42
+ s = [@flag].pack("C")
43
+ @flag & 1 == 1 and s << [@mtime.to_i].pack("V")
44
+ s
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ module Zip
2
+ # Info-ZIP Extra for UNIX uid/gid
3
+ class ExtraField::IUnix < ExtraField::Generic
4
+ HEADER_ID = "Ux"
5
+ register_map
6
+
7
+ def initialize(binstr = nil)
8
+ @uid = 0
9
+ @gid = 0
10
+ binstr and merge(binstr)
11
+ end
12
+
13
+ attr_accessor :uid, :gid
14
+
15
+ def merge(binstr)
16
+ return if binstr.empty?
17
+ size, content = initial_parse(binstr)
18
+ # size: 0 for central directory. 4 for local header
19
+ return if (!size || size == 0)
20
+ uid, gid = content.unpack("vv")
21
+ @uid ||= uid
22
+ @gid ||= gid
23
+ end
24
+
25
+ def ==(other)
26
+ @uid == other.uid &&
27
+ @gid == other.gid
28
+ end
29
+
30
+ def pack_for_local
31
+ [@uid, @gid].pack("vv")
32
+ end
33
+
34
+ def pack_for_c_dir
35
+ ""
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,419 @@
1
+ module Zip
2
+ # ZipFile is modeled after java.util.zip.ZipFile from the Java SDK.
3
+ # The most important methods are those inherited from
4
+ # ZipCentralDirectory for accessing information about the entries in
5
+ # the archive and methods such as get_input_stream and
6
+ # get_output_stream for reading from and writing entries to the
7
+ # archive. The class includes a few convenience methods such as
8
+ # #extract for extracting entries to the filesystem, and #remove,
9
+ # #replace, #rename and #mkdir for making simple modifications to
10
+ # the archive.
11
+ #
12
+ # Modifications to a zip archive are not committed until #commit or
13
+ # #close is called. The method #open accepts a block following
14
+ # the pattern from File.open offering a simple way to
15
+ # automatically close the archive when the block returns.
16
+ #
17
+ # The following example opens zip archive <code>my.zip</code>
18
+ # (creating it if it doesn't exist) and adds an entry
19
+ # <code>first.txt</code> and a directory entry <code>a_dir</code>
20
+ # to it.
21
+ #
22
+ # require 'zip/zip'
23
+ #
24
+ # Zip::ZipFile.open("my.zip", Zip::ZipFile::CREATE) {
25
+ # |zipfile|
26
+ # zipfile.get_output_stream("first.txt") { |f| f.puts "Hello from ZipFile" }
27
+ # zipfile.mkdir("a_dir")
28
+ # }
29
+ #
30
+ # The next example reopens <code>my.zip</code> writes the contents of
31
+ # <code>first.txt</code> to standard out and deletes the entry from
32
+ # the archive.
33
+ #
34
+ # require 'zip/zip'
35
+ #
36
+ # Zip::ZipFile.open("my.zip", Zip::ZipFile::CREATE) {
37
+ # |zipfile|
38
+ # puts zipfile.read("first.txt")
39
+ # zipfile.remove("first.txt")
40
+ # }
41
+ #
42
+ # ZipFileSystem offers an alternative API that emulates ruby's
43
+ # interface for accessing the filesystem, ie. the File and Dir classes.
44
+
45
+ class File < CentralDirectory
46
+
47
+ CREATE = 1
48
+ SPLIT_SIGNATURE = 0x08074b50
49
+ MAX_SEGMENT_SIZE = 3221225472
50
+ MIN_SEGMENT_SIZE = 65536
51
+ DATA_BUFFER_SIZE = 8192
52
+
53
+ attr_reader :name
54
+
55
+ # default -> false
56
+ attr_accessor :restore_ownership
57
+ # default -> false
58
+ attr_accessor :restore_permissions
59
+ # default -> true
60
+ attr_accessor :restore_times
61
+
62
+ # Opens a zip archive. Pass true as the second parameter to create
63
+ # a new archive if it doesn't exist already.
64
+ def initialize(fileName, create = nil, buffer = false)
65
+ super()
66
+ @name = fileName
67
+ @comment = ""
68
+ @create = create
69
+ case
70
+ when ::File.exists?(fileName) && !buffer
71
+ @create = nil
72
+ ::File.open(name, "rb") do |f|
73
+ read_from_stream(f)
74
+ end
75
+ when create
76
+ @entry_set = EntrySet.new
77
+ else
78
+ raise ZipError, "File #{fileName} not found"
79
+ end
80
+ @storedEntries = @entry_set.dup
81
+ @storedComment = @comment
82
+ @restore_ownership = false
83
+ @restore_permissions = false
84
+ @restore_times = true
85
+ end
86
+
87
+ class << self
88
+ # Same as #new. If a block is passed the ZipFile object is passed
89
+ # to the block and is automatically closed afterwards just as with
90
+ # ruby's builtin File.open method.
91
+ def open(fileName, create = nil)
92
+ zf = ::Zip::File.new(fileName, create)
93
+ if block_given?
94
+ begin
95
+ yield zf
96
+ ensure
97
+ zf.close
98
+ end
99
+ else
100
+ zf
101
+ end
102
+ end
103
+
104
+ # Same as #open. But outputs data to a buffer instead of a file
105
+ def add_buffer
106
+ zf = ::Zip::File.new('', true, true)
107
+ yield zf
108
+ zf.write_buffer
109
+ end
110
+
111
+ # Like #open, but reads zip archive contents from a String or open IO
112
+ # stream, and outputs data to a buffer.
113
+ # (This can be used to extract data from a
114
+ # downloaded zip archive without first saving it to disk.)
115
+ def open_buffer(io)
116
+ zf = ::Zip::File.new('', true, true)
117
+ if io.is_a? IO
118
+ zf.read_from_stream(io)
119
+ elsif io.is_a? String
120
+ require 'stringio'
121
+ zf.read_from_stream(StringIO.new(io))
122
+ else
123
+ raise "Zip::ZipFile.open_buffer expects an argument of class String or IO. Found: #{io.class}"
124
+ end
125
+ yield zf
126
+ zf.write_buffer
127
+ end
128
+
129
+ # Iterates over the contents of the ZipFile. This is more efficient
130
+ # than using a ZipInputStream since this methods simply iterates
131
+ # through the entries in the central directory structure in the archive
132
+ # whereas ZipInputStream jumps through the entire archive accessing the
133
+ # local entry headers (which contain the same information as the
134
+ # central directory).
135
+ def foreach(aZipFileName, &block)
136
+ open(aZipFileName) do |zipFile|
137
+ zipFile.each(&block)
138
+ end
139
+ end
140
+
141
+ def get_segment_size_for_split(segment_size)
142
+ case
143
+ when MIN_SEGMENT_SIZE > segment_size
144
+ MIN_SEGMENT_SIZE
145
+ when MAX_SEGMENT_SIZE < segment_size
146
+ MAX_SEGMENT_SIZE
147
+ else
148
+ segment_size
149
+ end
150
+ end
151
+
152
+ def get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
153
+ partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
154
+ partial_zip_file_name + ::File.extname(zip_file_name)) unless partial_zip_file_name.nil?
155
+ partial_zip_file_name ||= zip_file_name
156
+ partial_zip_file_name
157
+ end
158
+
159
+ def get_segment_count_for_split(zip_file_size, segment_size)
160
+ (zip_file_size / segment_size).to_i + (zip_file_size % segment_size == 0 ? 0 : 1)
161
+ end
162
+
163
+ def put_split_signature(szip_file, segment_size)
164
+ signature_packed = [SPLIT_SIGNATURE].pack('V')
165
+ szip_file << signature_packed
166
+ segment_size - signature_packed.size
167
+ end
168
+
169
+ #
170
+ # TODO: Make the code more understandable
171
+ #
172
+ def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
173
+ ssegment_size = zip_file_size - zip_file.pos
174
+ ssegment_size = segment_size if ssegment_size > segment_size
175
+ szip_file_name = "#{partial_zip_file_name}.#{'%03d'%(szip_file_index)}"
176
+ ::File.open(szip_file_name, 'wb') do |szip_file|
177
+ if szip_file_index == 1
178
+ ssegment_size = put_split_signature(szip_file, segment_size)
179
+ end
180
+ chunk_bytes = 0
181
+ until ssegment_size == chunk_bytes || zip_file.eof?
182
+ segment_bytes_left = ssegment_size - chunk_bytes
183
+ buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE
184
+ chunk = zip_file.read(buffer_size)
185
+ chunk_bytes += buffer_size
186
+ szip_file << chunk
187
+ # Info for track splitting
188
+ yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given?
189
+ end
190
+ end
191
+ end
192
+
193
+ # Splits an archive into parts with segment size
194
+ def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil)
195
+ raise ZipError, "File #{zip_file_name} not found" unless ::File.exists?(zip_file_name)
196
+ raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
197
+ zip_file_size = ::File.size(zip_file_name)
198
+ segment_size = get_segment_size_for_split(segment_size)
199
+ return if zip_file_size <= segment_size
200
+ segment_count = get_segment_count_for_split(zip_file_size, segment_size)
201
+ # Checking for correct zip structure
202
+ self.open(zip_file_name) {}
203
+ partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
204
+ szip_file_index = 0
205
+ ::File.open(zip_file_name, 'rb') do |zip_file|
206
+ until zip_file.eof?
207
+ szip_file_index += 1
208
+ save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
209
+ end
210
+ end
211
+ ::File.delete(zip_file_name) if delete_zip_file
212
+ szip_file_index
213
+ end
214
+ end
215
+
216
+ # Returns the zip files comment, if it has one
217
+ attr_accessor :comment
218
+
219
+ # Returns an input stream to the specified entry. If a block is passed
220
+ # the stream object is passed to the block and the stream is automatically
221
+ # closed afterwards just as with ruby's builtin File.open method.
222
+ def get_input_stream(entry, &aProc)
223
+ get_entry(entry).get_input_stream(&aProc)
224
+ end
225
+
226
+ # Returns an output stream to the specified entry. If a block is passed
227
+ # the stream object is passed to the block and the stream is automatically
228
+ # closed afterwards just as with ruby's builtin File.open method.
229
+ def get_output_stream(entry, permissionInt = nil, &aProc)
230
+ newEntry = entry.kind_of?(Entry) ? entry : Entry.new(@name, entry.to_s)
231
+ if newEntry.directory?
232
+ raise ArgumentError,
233
+ "cannot open stream to directory entry - '#{newEntry}'"
234
+ end
235
+ newEntry.unix_perms = permissionInt
236
+ zipStreamableEntry = StreamableStream.new(newEntry)
237
+ @entry_set << zipStreamableEntry
238
+ zipStreamableEntry.get_output_stream(&aProc)
239
+ end
240
+
241
+ # Returns the name of the zip archive
242
+ def to_s
243
+ @name
244
+ end
245
+
246
+ # Returns a string containing the contents of the specified entry
247
+ def read(entry)
248
+ get_input_stream(entry) { |is| is.read }
249
+ end
250
+
251
+ # Convenience method for adding the contents of a file to the archive
252
+ def add(entry, srcPath, &continue_on_exists_proc)
253
+ continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
254
+ check_entry_exists(entry, continue_on_exists_proc, "add")
255
+ newEntry = entry.kind_of?(Entry) ? entry : Entry.new(@name, entry.to_s)
256
+ newEntry.gather_fileinfo_from_srcpath(srcPath)
257
+ @entry_set << newEntry
258
+ end
259
+
260
+ # Removes the specified entry.
261
+ def remove(entry)
262
+ @entry_set.delete(get_entry(entry))
263
+ end
264
+
265
+ # Renames the specified entry.
266
+ def rename(entry, new_name, &continue_on_exists_proc)
267
+ foundEntry = get_entry(entry)
268
+ check_entry_exists(new_name, continue_on_exists_proc, 'rename')
269
+ @entry_set.delete(foundEntry)
270
+ foundEntry.name = new_name
271
+ @entry_set << foundEntry
272
+ end
273
+
274
+ # Replaces the specified entry with the contents of srcPath (from
275
+ # the file system).
276
+ def replace(entry, srcPath)
277
+ check_file(srcPath)
278
+ remove(entry)
279
+ add(entry, srcPath)
280
+ end
281
+
282
+ # Extracts entry to file dest_path.
283
+ def extract(entry, dest_path, &block)
284
+ block ||= proc { ::Zip.on_exists_proc }
285
+ found_entry = get_entry(entry)
286
+ found_entry.extract(dest_path, &block)
287
+ end
288
+
289
+ # Commits changes that has been made since the previous commit to
290
+ # the zip archive.
291
+ def commit
292
+ return if !commit_required?
293
+ on_success_replace(name) {
294
+ |tmpFile|
295
+ OutputStream.open(tmpFile) {
296
+ |zos|
297
+
298
+ @entry_set.each {
299
+ |e|
300
+ e.write_to_zip_output_stream(zos)
301
+ e.dirty = false
302
+ }
303
+ zos.comment = comment
304
+ }
305
+ true
306
+ }
307
+ initialize(name)
308
+ end
309
+
310
+ # Write buffer write changes to buffer and return
311
+ def write_buffer
312
+ buffer = OutputStream.write_buffer do |zos|
313
+ @entry_set.each { |e| e.write_to_zip_output_stream(zos) }
314
+ zos.comment = comment
315
+ end
316
+ return buffer
317
+ end
318
+
319
+ # Closes the zip file committing any changes that has been made.
320
+ def close
321
+ commit
322
+ end
323
+
324
+ # Returns true if any changes has been made to this archive since
325
+ # the previous commit
326
+ def commit_required?
327
+ @entry_set.each do |e|
328
+ return true if e.dirty
329
+ end
330
+ @comment != @storedComment || @entry_set != @storedEntries || @create == File::CREATE
331
+ end
332
+
333
+ # Searches for entry with the specified name. Returns nil if
334
+ # no entry is found. See also get_entry
335
+ def find_entry(entry_name)
336
+ @entry_set.find_entry(entry_name)
337
+ end
338
+
339
+ # Searches for entries given a glob
340
+ def glob(*args, &block)
341
+ @entry_set.glob(*args, &block)
342
+ end
343
+
344
+ # Searches for an entry just as find_entry, but throws Errno::ENOENT
345
+ # if no entry is found.
346
+ def get_entry(entry)
347
+ selectedEntry = find_entry(entry)
348
+ unless selectedEntry
349
+ raise Errno::ENOENT, entry
350
+ end
351
+ selectedEntry.restore_ownership = @restore_ownership
352
+ selectedEntry.restore_permissions = @restore_permissions
353
+ selectedEntry.restore_times = @restore_times
354
+ selectedEntry
355
+ end
356
+
357
+ # Creates a directory
358
+ def mkdir(entryName, permissionInt = 0755)
359
+ if find_entry(entryName)
360
+ raise Errno::EEXIST, "File exists - #{entryName}"
361
+ end
362
+ entryName = entryName.dup.to_s
363
+ entryName << '/' unless entryName.end_with?('/')
364
+ @entry_set << StreamableDirectory.new(@name, entryName, nil, permissionInt)
365
+ end
366
+
367
+ private
368
+
369
+ def is_directory(newEntry, srcPath)
370
+ srcPathIsDirectory = ::File.directory?(srcPath)
371
+ if newEntry.is_directory && !srcPathIsDirectory
372
+ raise ArgumentError,
373
+ "entry name '#{newEntry}' indicates directory entry, but "+
374
+ "'#{srcPath}' is not a directory"
375
+ elsif !newEntry.is_directory && srcPathIsDirectory
376
+ newEntry.name += "/"
377
+ end
378
+ newEntry.is_directory && srcPathIsDirectory
379
+ end
380
+
381
+ def check_entry_exists(entryName, continue_on_exists_proc, procedureName)
382
+ continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
383
+ if @entry_set.include?(entryName)
384
+ if continue_on_exists_proc.call
385
+ remove get_entry(entryName)
386
+ else
387
+ raise ZipEntryExistsError,
388
+ procedureName + " failed. Entry #{entryName} already exists"
389
+ end
390
+ end
391
+ end
392
+
393
+ def check_file(path)
394
+ unless ::File.readable?(path)
395
+ raise Errno::ENOENT, path
396
+ end
397
+ end
398
+
399
+ def on_success_replace(aFilename)
400
+ tmpfile = get_tempfile
401
+ tmpFilename = tmpfile.path
402
+ tmpfile.close
403
+ if yield tmpFilename
404
+ ::File.rename(tmpFilename, name)
405
+ end
406
+ end
407
+
408
+ def get_tempfile
409
+ tempFile = Tempfile.new(::File.basename(name), ::File.dirname(name))
410
+ tempFile.binmode
411
+ tempFile
412
+ end
413
+
414
+ end
415
+ end
416
+
417
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
418
+ # rubyzip is free software; you can redistribute it and/or
419
+ # modify it under the terms of the ruby license.