rwddialogmap 0.01

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.
Files changed (170) hide show
  1. data/Readme.txt +475 -0
  2. data/bin/rwddialogmap +19 -0
  3. data/code/01rwdcore/01rwdcore.rb +29 -0
  4. data/code/01rwdcore/02helptexthashbegin.rb +4 -0
  5. data/code/01rwdcore/03helptexthash.rb +23 -0
  6. data/code/01rwdcore/04helptextend.rb +6 -0
  7. data/code/01rwdcore/jumplinkcommand.rb +26 -0
  8. data/code/01rwdcore/openhelpwindow.rb +31 -0
  9. data/code/01rwdcore/returntomain.rb +10 -0
  10. data/code/01rwdcore/rundocuments.rb +10 -0
  11. data/code/01rwdcore/runeditconfiguration.rb +10 -0
  12. data/code/01rwdcore/runhelpabout.rb +10 -0
  13. data/code/01rwdcore/runopentinkerdocument.rb +7 -0
  14. data/code/01rwdcore/rwdtinkerversion.rb +22 -0
  15. data/code/01rwdcore/rwdwindowreturn.rb +9 -0
  16. data/code/01rwdcore/selectiontab.rb +9 -0
  17. data/code/01rwdcore/setuphelpaboutoptions.rb +13 -0
  18. data/code/01rwdcore/setuptinkerdocuments.rb +6 -0
  19. data/code/01rwdcore/test_cases.rb +109 -0
  20. data/code/01rwdcore/test_harness.rb +13 -0
  21. data/code/01rwdcore/uploadreturns.rb +62 -0
  22. data/code/dd0viewphoto/dd0viewphoto.rb +3 -0
  23. data/code/superant.com.dialogmap/0uninstallapplet.rb +12 -0
  24. data/code/superant.com.dialogmap/changedir.rb +31 -0
  25. data/code/superant.com.dialogmap/clearhypernotescreendisplay.rb +14 -0
  26. data/code/superant.com.dialogmap/clearlinkscreendisplay.rb +13 -0
  27. data/code/superant.com.dialogmap/createnewnote.rb +28 -0
  28. data/code/superant.com.dialogmap/helptexthashrwdhypernote.rb +38 -0
  29. data/code/superant.com.dialogmap/launchurlfile.rb +35 -0
  30. data/code/superant.com.dialogmap/listdirectories.rb +19 -0
  31. data/code/superant.com.dialogmap/listdirectories3.rb +25 -0
  32. data/code/superant.com.dialogmap/listnotedirshtml3.rb +54 -0
  33. data/code/superant.com.dialogmap/loadconfigurationrecord.rb +22 -0
  34. data/code/superant.com.dialogmap/loadconfigurationvariables.rb +14 -0
  35. data/code/superant.com.dialogmap/loadhtmlnoterecord.rb +24 -0
  36. data/code/superant.com.dialogmap/openhelpwindowrwdhyernote.rb +38 -0
  37. data/code/superant.com.dialogmap/runhypernote.rb +10 -0
  38. data/code/superant.com.dialogmap/runwrdhypernotetxtwindow.rb +10 -0
  39. data/code/superant.com.dialogmap/rwdhypernoteresetdir.rb +7 -0
  40. data/code/superant.com.dialogmap/rwdtinkerversion.rb +10 -0
  41. data/code/superant.com.dialogmap/saveconfigurationrecord.rb +20 -0
  42. data/code/superant.com.dialogmap/savelinkrecord.rb +23 -0
  43. data/code/superant.com.dialogmap/viewdialogmaphtml3.rb +66 -0
  44. data/code/superant.com.dialogmap/xmlprettyprint.rb +65 -0
  45. data/code/superant.com.dialogmap/xmlprettyprint2.rb +74 -0
  46. data/code/superant.com.rwdtinkerbackwindow/diagnostictab.rb +19 -0
  47. data/code/superant.com.rwdtinkerbackwindow/helptexthashtinkerwin2.rb +61 -0
  48. data/code/superant.com.rwdtinkerbackwindow/initiateapplets.rb +240 -0
  49. data/code/superant.com.rwdtinkerbackwindow/installgemapplet.rb +34 -0
  50. data/code/superant.com.rwdtinkerbackwindow/installremotegem.rb +20 -0
  51. data/code/superant.com.rwdtinkerbackwindow/listgemdirs.rb +12 -0
  52. data/code/superant.com.rwdtinkerbackwindow/listgemzips.rb +53 -0
  53. data/code/superant.com.rwdtinkerbackwindow/listinstalledfiles.rb +12 -0
  54. data/code/superant.com.rwdtinkerbackwindow/listzips.rb +33 -0
  55. data/code/superant.com.rwdtinkerbackwindow/loadconfigurationrecord.rb +22 -0
  56. data/code/superant.com.rwdtinkerbackwindow/loadconfigurationvariables.rb +14 -0
  57. data/code/superant.com.rwdtinkerbackwindow/network.rb +87 -0
  58. data/code/superant.com.rwdtinkerbackwindow/openappletname.rb +19 -0
  59. data/code/superant.com.rwdtinkerbackwindow/openhelpwindowtinkerwin2.rb +38 -0
  60. data/code/superant.com.rwdtinkerbackwindow/remotegemlist.rb +24 -0
  61. data/code/superant.com.rwdtinkerbackwindow/removeapplet.rb +46 -0
  62. data/code/superant.com.rwdtinkerbackwindow/removeappletvariables.rb +52 -0
  63. data/code/superant.com.rwdtinkerbackwindow/runremoteinstall.rb +11 -0
  64. data/code/superant.com.rwdtinkerbackwindow/runrwdtinkerbackwindow.rb +15 -0
  65. data/code/superant.com.rwdtinkerbackwindow/rwdtinkerwin2version.rb +13 -0
  66. data/code/superant.com.rwdtinkerbackwindow/saveconfigurationrecord.rb +19 -0
  67. data/code/superant.com.rwdtinkerbackwindow/viewappletcontents.rb +22 -0
  68. data/code/superant.com.rwdtinkerbackwindow/viewgemappletcontents.rb +24 -0
  69. data/code/zz0applicationend/zz0end.rb +4 -0
  70. data/configuration/language.dist +8 -0
  71. data/configuration/rwdapplicationidentity.dist +3 -0
  72. data/configuration/rwdtinker.dist +15 -0
  73. data/configuration/rwdwdialogmap-0.01.dist +13 -0
  74. data/configuration/tinkerwin2variables.dist +18 -0
  75. data/gui/00coreguibegin/applicationguitop.rwd +4 -0
  76. data/gui/frontwindow0/cc0openphoto.rwd +22 -0
  77. data/gui/frontwindowselections/00selectiontabbegin.rwd +11 -0
  78. data/gui/frontwindowselections/jumplinkcommands.rwd +15 -0
  79. data/gui/frontwindowselections/wwselectionend.rwd +3 -0
  80. data/gui/frontwindowtdocuments/00documentbegin.rwd +6 -0
  81. data/gui/frontwindowtdocuments/tinkerdocuments.rwd +14 -0
  82. data/gui/frontwindowtdocuments/zzdocumentend.rwd +8 -0
  83. data/gui/helpaboutbegin/zzzrwdlasttab.rwd +6 -0
  84. data/gui/helpaboutbegin/zzzzhelpscreenstart.rwd +3 -0
  85. data/gui/helpaboutbegin/zzzzzzhelpabouttab.rwd +15 -0
  86. data/gui/helpaboutzend/helpscreenend.rwd +3 -0
  87. data/gui/helpaboutzend/zhelpscreenstart2.rwd +3 -0
  88. data/gui/helpaboutzend/zzzzhelpabout2.rwd +15 -0
  89. data/gui/helpaboutzend/zzzzhelpscreen2end.rwd +3 -0
  90. data/gui/tinkerbackwindows/superant.com.dialogmap/10appletbegin.rwd +4 -0
  91. data/gui/tinkerbackwindows/superant.com.dialogmap/12dialogview.rwd +65 -0
  92. data/gui/tinkerbackwindows/superant.com.dialogmap/15aviewxml.rwd +32 -0
  93. data/gui/tinkerbackwindows/superant.com.dialogmap/17aviewxml.rwd +28 -0
  94. data/gui/tinkerbackwindows/superant.com.dialogmap/21editnote.rwd +33 -0
  95. data/gui/tinkerbackwindows/superant.com.dialogmap/67viewconfiguration.rwd +29 -0
  96. data/gui/tinkerbackwindows/superant.com.dialogmap/81jumplinkcommands.rwd +17 -0
  97. data/gui/tinkerbackwindows/superant.com.dialogmap/9end.rwd +6 -0
  98. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/1appname.rwd +5 -0
  99. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/40rwdlistzips.rwd +41 -0
  100. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/45installremotezip.rwd +44 -0
  101. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/50rwdlistapplets.rwd +44 -0
  102. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/60editconfiguration.rwd +30 -0
  103. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/70rwddiagnostics.rwd +29 -0
  104. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/81jumplinkcommands.rwd +17 -0
  105. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/9backend.rwd +6 -0
  106. data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/1appname.rwd +31 -0
  107. data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/9end.rwd +4 -0
  108. data/gui/tinkerbackwindows/superant.com.versionwindow/1appname.rwd +19 -0
  109. data/gui/tinkerbackwindows/superant.com.versionwindow/helpaboutwindow.rwd +17 -0
  110. data/gui/zzcoreguiend/yy9rwdend.rwd +4 -0
  111. data/init.rb +277 -0
  112. data/installed/dialogmapdata2.inf +2 -0
  113. data/installed/rwdwdialogmap-0.01.inf +8 -0
  114. data/installed/temp.rb +1 -0
  115. data/lang/en/rwdcore/languagefile.rb +58 -0
  116. data/lang/es/rwdcore/languagefile-es.rb +62 -0
  117. data/lang/fr/rwdcore/languagefile.rb +64 -0
  118. data/lang/jp/rwdcore/languagefile.rb +72 -0
  119. data/lang/nl/rwdcore/languagefile.rb +75 -0
  120. data/lib/rconftool.rb +387 -0
  121. data/lib/rwd/browser.rb +123 -0
  122. data/lib/rwd/ftools.rb +174 -0
  123. data/lib/rwd/mime.rb +328 -0
  124. data/lib/rwd/net.rb +876 -0
  125. data/lib/rwd/ruby.rb +889 -0
  126. data/lib/rwd/rwd.rb +1422 -0
  127. data/lib/rwd/sgml.rb +236 -0
  128. data/lib/rwd/thread.rb +63 -0
  129. data/lib/rwd/tree.rb +371 -0
  130. data/lib/rwd/xml.rb +101 -0
  131. data/lib/rwdthemes/default.rwd +317 -0
  132. data/lib/rwdthemes/pda.rwd +72 -0
  133. data/lib/rwdthemes/windowslike.rwd +171 -0
  134. data/lib/zip/ioextras.rb +114 -0
  135. data/lib/zip/stdrubyext.rb +111 -0
  136. data/lib/zip/tempfile_bugfixed.rb +195 -0
  137. data/lib/zip/zip.rb +1378 -0
  138. data/lib/zip/zipfilesystem.rb +558 -0
  139. data/lib/zip/ziprequire.rb +61 -0
  140. data/rwd_files/HowTo_DialogMap.txt +233 -0
  141. data/rwd_files/HowTo_Tinker.txt +482 -0
  142. data/rwd_files/HowTo_TinkerWin2.txt +202 -0
  143. data/rwd_files/Notes/Another Brain Jam.mm +116 -0
  144. data/rwd_files/Notes/HR.xml +2 -0
  145. data/rwd_files/Notes/TaoistBooks.mm +82 -0
  146. data/rwd_files/Readme.txt +57 -0
  147. data/rwd_files/RubyWebDialogs.html +6 -0
  148. data/rwd_files/favicon.ico +0 -0
  149. data/rwd_files/rdoc-style.css +175 -0
  150. data/rwd_files/rwdapplications.html +54 -0
  151. data/rwd_files/tinker.png +0 -0
  152. data/rwdconfig.dist +22 -0
  153. data/rwddialogmap.rb +1 -0
  154. data/tests/RubyGauge.rb +179 -0
  155. data/tests/checkdepends.sh +4 -0
  156. data/tests/cleancnf.sh +6 -0
  157. data/tests/makedist-rwdwdialogmap.rb +58 -0
  158. data/tests/makedist.rb +65 -0
  159. data/tests/rdep.rb +354 -0
  160. data/tests/totranslate.lang +93 -0
  161. data/zips/rwdwaddresses-1.06.zip +0 -0
  162. data/zips/rwdwcalc-0.62.zip +0 -0
  163. data/zips/rwdwdialogmap-0.01.zip +0 -0
  164. data/zips/rwdwfoldeditor-0.04.zip +0 -0
  165. data/zips/rwdwgutenberg-0.09.zip +0 -0
  166. data/zips/rwdwruby-1.07.zip +0 -0
  167. data/zips/temp.rb +1 -0
  168. data/zips/tinkerbellw-0.02.zip +0 -0
  169. data/zips/wrubyslippers-1.06.zip +0 -0
  170. metadata +226 -0
data/lib/zip/zip.rb ADDED
@@ -0,0 +1,1378 @@
1
+
2
+ require 'delegate'
3
+ require 'singleton'
4
+ require 'tempfile'
5
+ require 'ftools'
6
+ require 'zlib'
7
+ require 'lib/zip/stdrubyext'
8
+ require 'lib/zip/ioextras'
9
+
10
+ if Tempfile.superclass == SimpleDelegator
11
+ require 'zip/tempfile_bugfixed'
12
+ Tempfile = BugFix::Tempfile
13
+ end
14
+
15
+ module Zlib
16
+ if ! const_defined? :MAX_WBITS
17
+ MAX_WBITS = Zlib::Deflate.MAX_WBITS
18
+ end
19
+ end
20
+
21
+ module Zip
22
+
23
+ RUBY_MINOR_VERSION = RUBY_VERSION.split(".")[1].to_i
24
+
25
+ # Ruby 1.7.x compatibility
26
+ # In ruby 1.6.x and 1.8.0 reading from an empty stream returns
27
+ # an empty string the first time and then nil.
28
+ # not so in 1.7.x
29
+ EMPTY_FILE_RETURNS_EMPTY_STRING_FIRST = RUBY_MINOR_VERSION != 7
30
+
31
+ class ZipInputStream
32
+ include IOExtras::AbstractInputStream
33
+
34
+ def initialize(filename, offset = 0)
35
+ super()
36
+ @archiveIO = File.open(filename, "rb")
37
+ @archiveIO.seek(offset, IO::SEEK_SET)
38
+ @decompressor = NullDecompressor.instance
39
+ @currentEntry = nil
40
+ end
41
+
42
+ def close
43
+ @archiveIO.close
44
+ end
45
+
46
+ def ZipInputStream.open(filename)
47
+ return new(filename) unless block_given?
48
+
49
+ zio = new(filename)
50
+ yield zio
51
+ ensure
52
+ zio.close if zio
53
+ end
54
+
55
+ def get_next_entry
56
+ @archiveIO.seek(@currentEntry.next_header_offset,
57
+ IO::SEEK_SET) if @currentEntry
58
+ open_entry
59
+ end
60
+
61
+ def rewind
62
+ return if @currentEntry.nil?
63
+ @lineno = 0
64
+ @archiveIO.seek(@currentEntry.localHeaderOffset,
65
+ IO::SEEK_SET)
66
+ open_entry
67
+ end
68
+
69
+ def open_entry
70
+ @currentEntry = ZipEntry.read_local_entry(@archiveIO)
71
+ if (@currentEntry == nil)
72
+ @decompressor = NullDecompressor.instance
73
+ elsif @currentEntry.compression_method == ZipEntry::STORED
74
+ @decompressor = PassThruDecompressor.new(@archiveIO,
75
+ @currentEntry.size)
76
+ elsif @currentEntry.compression_method == ZipEntry::DEFLATED
77
+ @decompressor = Inflater.new(@archiveIO)
78
+ else
79
+ raise ZipCompressionMethodError,
80
+ "Unsupported compression method #{@currentEntry.compression_method}"
81
+ end
82
+ flush
83
+ return @currentEntry
84
+ end
85
+
86
+ def read(numberOfBytes = nil)
87
+ @decompressor.read(numberOfBytes)
88
+ end
89
+ protected
90
+ def produce_input
91
+ @decompressor.produce_input
92
+ end
93
+
94
+ def input_finished?
95
+ @decompressor.input_finished?
96
+ end
97
+ end
98
+
99
+
100
+
101
+ class Decompressor #:nodoc:all
102
+ CHUNK_SIZE=32768
103
+ def initialize(inputStream)
104
+ super()
105
+ @inputStream=inputStream
106
+ end
107
+ end
108
+
109
+ class Inflater < Decompressor #:nodoc:all
110
+ def initialize(inputStream)
111
+ super
112
+ @zlibInflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
113
+ @outputBuffer=""
114
+ @hasReturnedEmptyString = ! EMPTY_FILE_RETURNS_EMPTY_STRING_FIRST
115
+ end
116
+
117
+ def read(numberOfBytes = nil)
118
+ readEverything = (numberOfBytes == nil)
119
+ while (readEverything || @outputBuffer.length < numberOfBytes)
120
+ break if internal_input_finished?
121
+ @outputBuffer << internal_produce_input
122
+ end
123
+ return value_when_finished if @outputBuffer.length==0 && input_finished?
124
+ endIndex= numberOfBytes==nil ? @outputBuffer.length : numberOfBytes
125
+ return @outputBuffer.slice!(0...endIndex)
126
+ end
127
+
128
+ def produce_input
129
+ if (@outputBuffer.empty?)
130
+ return internal_produce_input
131
+ else
132
+ return @outputBuffer.slice!(0...(@outputBuffer.length))
133
+ end
134
+ end
135
+
136
+ # to be used with produce_input, not read (as read may still have more data cached)
137
+ def input_finished?
138
+ @outputBuffer.empty? && internal_input_finished?
139
+ end
140
+
141
+ private
142
+
143
+ def internal_produce_input
144
+ @zlibInflater.inflate(@inputStream.read(Decompressor::CHUNK_SIZE))
145
+ end
146
+
147
+ def internal_input_finished?
148
+ @zlibInflater.finished?
149
+ end
150
+
151
+ # TODO: Specialize to handle different behaviour in ruby > 1.7.0 ?
152
+ def value_when_finished # mimic behaviour of ruby File object.
153
+ return nil if @hasReturnedEmptyString
154
+ @hasReturnedEmptyString=true
155
+ return ""
156
+ end
157
+ end
158
+
159
+ class PassThruDecompressor < Decompressor #:nodoc:all
160
+ def initialize(inputStream, charsToRead)
161
+ super inputStream
162
+ @charsToRead = charsToRead
163
+ @readSoFar = 0
164
+ @hasReturnedEmptyString = ! EMPTY_FILE_RETURNS_EMPTY_STRING_FIRST
165
+ end
166
+
167
+ # TODO: Specialize to handle different behaviour in ruby > 1.7.0 ?
168
+ def read(numberOfBytes = nil)
169
+ if input_finished?
170
+ hasReturnedEmptyStringVal=@hasReturnedEmptyString
171
+ @hasReturnedEmptyString=true
172
+ return "" unless hasReturnedEmptyStringVal
173
+ return nil
174
+ end
175
+
176
+ if (numberOfBytes == nil || @readSoFar+numberOfBytes > @charsToRead)
177
+ numberOfBytes = @charsToRead-@readSoFar
178
+ end
179
+ @readSoFar += numberOfBytes
180
+ @inputStream.read(numberOfBytes)
181
+ end
182
+
183
+ def produce_input
184
+ read(Decompressor::CHUNK_SIZE)
185
+ end
186
+
187
+ def input_finished?
188
+ (@readSoFar >= @charsToRead)
189
+ end
190
+ end
191
+
192
+ class NullDecompressor #:nodoc:all
193
+ include Singleton
194
+ def read(numberOfBytes = nil)
195
+ nil
196
+ end
197
+
198
+ def produce_input
199
+ nil
200
+ end
201
+
202
+ def input_finished?
203
+ true
204
+ end
205
+ end
206
+
207
+ class NullInputStream < NullDecompressor #:nodoc:all
208
+ include IOExtras::AbstractInputStream
209
+ end
210
+
211
+ class ZipEntry
212
+ STORED = 0
213
+ DEFLATED = 8
214
+
215
+ attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
216
+ :name, :size, :localHeaderOffset, :zipfile, :fstype, :externalFileAttributes
217
+
218
+ def initialize(zipfile = "", name = "", comment = "", extra = "",
219
+ compressed_size = 0, crc = 0,
220
+ compression_method = ZipEntry::DEFLATED, size = 0,
221
+ time = Time.now)
222
+ super()
223
+ if name.starts_with("/")
224
+ raise ZipEntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
225
+ end
226
+ @localHeaderOffset = 0
227
+ @internalFileAttributes = 1
228
+ @externalFileAttributes = 0
229
+ @version = 52 # this library's version
230
+ @fstype = 0 # default is fat
231
+ @zipfile, @comment, @compressed_size, @crc, @extra, @compression_method,
232
+ @name, @size = zipfile, comment, compressed_size, crc,
233
+ extra, compression_method, name, size
234
+ @time = time
235
+ unless ZipExtraField === @extra
236
+ @extra = ZipExtraField.new(@extra.to_s)
237
+ end
238
+ end
239
+
240
+ def time
241
+ if @extra["UniversalTime"]
242
+ @extra["UniversalTime"].mtime
243
+ else
244
+ # Atandard time field in central directory has local time
245
+ # under archive creator. Then, we can't get timezone.
246
+ @time
247
+ end
248
+ end
249
+ alias :mtime :time
250
+
251
+ def time=(aTime)
252
+ unless @extra.member?("UniversalTime")
253
+ @extra.create("UniversalTime")
254
+ end
255
+ @extra["UniversalTime"].mtime = aTime
256
+ @time = aTime
257
+ end
258
+
259
+ def directory?
260
+ return (%r{\/$} =~ @name) != nil
261
+ end
262
+ alias :is_directory :directory?
263
+
264
+ def file?
265
+ ! directory?
266
+ end
267
+
268
+ def local_entry_offset #:nodoc:all
269
+ localHeaderOffset + local_header_size
270
+ end
271
+
272
+ def local_header_size #:nodoc:all
273
+ LOCAL_ENTRY_STATIC_HEADER_LENGTH + (@name ? @name.size : 0) + (@extra ? @extra.local_size : 0)
274
+ end
275
+
276
+ def cdir_header_size #:nodoc:all
277
+ CDIR_ENTRY_STATIC_HEADER_LENGTH + (@name ? @name.size : 0) +
278
+ (@extra ? @extra.c_dir_size : 0) + (@comment ? @comment.size : 0)
279
+ end
280
+
281
+ def next_header_offset #:nodoc:all
282
+ local_entry_offset + self.compressed_size
283
+ end
284
+
285
+ def to_s
286
+ @name
287
+ end
288
+
289
+ protected
290
+
291
+ def ZipEntry.read_zip_short(io)
292
+ io.read(2).unpack('v')[0]
293
+ end
294
+
295
+ def ZipEntry.read_zip_long(io)
296
+ io.read(4).unpack('V')[0]
297
+ end
298
+ public
299
+
300
+ LOCAL_ENTRY_SIGNATURE = 0x04034b50
301
+ LOCAL_ENTRY_STATIC_HEADER_LENGTH = 30
302
+
303
+ def read_local_entry(io) #:nodoc:all
304
+ @localHeaderOffset = io.tell
305
+ staticSizedFieldsBuf = io.read(LOCAL_ENTRY_STATIC_HEADER_LENGTH)
306
+ unless (staticSizedFieldsBuf.size==LOCAL_ENTRY_STATIC_HEADER_LENGTH)
307
+ raise ZipError, "Premature end of file. Not enough data for zip entry local header"
308
+ end
309
+
310
+ localHeader ,
311
+ @version ,
312
+ @fstype ,
313
+ @gpFlags ,
314
+ @compression_method,
315
+ lastModTime ,
316
+ lastModDate ,
317
+ @crc ,
318
+ @compressed_size ,
319
+ @size ,
320
+ nameLength ,
321
+ extraLength = staticSizedFieldsBuf.unpack('VCCvvvvVVVvv')
322
+
323
+ unless (localHeader == LOCAL_ENTRY_SIGNATURE)
324
+ raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
325
+ end
326
+ set_time(lastModDate, lastModTime)
327
+
328
+ @name = io.read(nameLength)
329
+ extra = io.read(extraLength)
330
+
331
+ if (extra && extra.length != extraLength)
332
+ raise ZipError, "Truncated local zip entry header"
333
+ else
334
+ if ZipExtraField === @extra
335
+ @extra.merge(extra)
336
+ else
337
+ @extra = ZipExtraField.new(extra)
338
+ end
339
+ end
340
+ end
341
+
342
+ def ZipEntry.read_local_entry(io)
343
+ entry = new(io.path)
344
+ entry.read_local_entry(io)
345
+ return entry
346
+ rescue ZipError
347
+ return nil
348
+ end
349
+
350
+ def write_local_entry(io) #:nodoc:all
351
+ @localHeaderOffset = io.tell
352
+
353
+ io <<
354
+ [LOCAL_ENTRY_SIGNATURE ,
355
+ 0 ,
356
+ 0 , # @gpFlags ,
357
+ @compression_method ,
358
+ @time.to_binary_dos_time , # @lastModTime ,
359
+ @time.to_binary_dos_date , # @lastModDate ,
360
+ @crc ,
361
+ @compressed_size ,
362
+ @size ,
363
+ @name ? @name.length : 0,
364
+ @extra? @extra.local_length : 0 ].pack('VvvvvvVVVvv')
365
+ io << @name
366
+ io << (@extra ? @extra.to_local_bin : "")
367
+ end
368
+
369
+ CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50
370
+ CDIR_ENTRY_STATIC_HEADER_LENGTH = 46
371
+
372
+ def read_c_dir_entry(io) #:nodoc:all
373
+ staticSizedFieldsBuf = io.read(CDIR_ENTRY_STATIC_HEADER_LENGTH)
374
+ unless (staticSizedFieldsBuf.size == CDIR_ENTRY_STATIC_HEADER_LENGTH)
375
+ raise ZipError, "Premature end of file. Not enough data for zip cdir entry header"
376
+ end
377
+
378
+ cdirSignature ,
379
+ @version , # version of encoding software
380
+ @fstype , # filesystem type
381
+ @versionNeededToExtract,
382
+ @gpFlags ,
383
+ @compression_method ,
384
+ lastModTime ,
385
+ lastModDate ,
386
+ @crc ,
387
+ @compressed_size ,
388
+ @size ,
389
+ nameLength ,
390
+ extraLength ,
391
+ commentLength ,
392
+ diskNumberStart ,
393
+ @internalFileAttributes,
394
+ @externalFileAttributes,
395
+ @localHeaderOffset ,
396
+ @name ,
397
+ @extra ,
398
+ @comment = staticSizedFieldsBuf.unpack('VCCvvvvvVVVvvvvvVV')
399
+
400
+ unless (cdirSignature == CENTRAL_DIRECTORY_ENTRY_SIGNATURE)
401
+ raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
402
+ end
403
+ set_time(lastModDate, lastModTime)
404
+
405
+ @name = io.read(nameLength)
406
+ if ZipExtraField === @extra
407
+ @extra.merge(io.read(extraLength))
408
+ else
409
+ @extra = ZipExtraField.new(io.read(extraLength))
410
+ end
411
+ @comment = io.read(commentLength)
412
+ unless (@comment && @comment.length == commentLength)
413
+ raise ZipError, "Truncated cdir zip entry header"
414
+ end
415
+ end
416
+
417
+ def ZipEntry.read_c_dir_entry(io) #:nodoc:all
418
+ entry = new(io.path)
419
+ entry.read_c_dir_entry(io)
420
+ return entry
421
+ rescue ZipError
422
+ return nil
423
+ end
424
+
425
+
426
+ def write_c_dir_entry(io) #:nodoc:all
427
+ io <<
428
+ [CENTRAL_DIRECTORY_ENTRY_SIGNATURE,
429
+ @version , # version of encoding software
430
+ @fstype , # filesystem type
431
+ 0 , # @versionNeededToExtract ,
432
+ 0 , # @gpFlags ,
433
+ @compression_method ,
434
+ @time.to_binary_dos_time , # @lastModTime ,
435
+ @time.to_binary_dos_date , # @lastModDate ,
436
+ @crc ,
437
+ @compressed_size ,
438
+ @size ,
439
+ @name ? @name.length : 0 ,
440
+ @extra ? @extra.c_dir_length : 0 ,
441
+ @comment ? comment.length : 0 ,
442
+ 0 , # disk number start
443
+ @internalFileAttributes , # file type (binary=0, text=1)
444
+ @externalFileAttributes , # native filesystem attributes
445
+ @localHeaderOffset ,
446
+ @name ,
447
+ @extra ,
448
+ @comment ].pack('VCCvvvvvVVVvvvvvVV')
449
+
450
+ io << @name
451
+ io << (@extra ? @extra.to_c_dir_bin : "")
452
+ io << @comment
453
+ end
454
+
455
+ def == (other)
456
+ return false unless other.class == ZipEntry
457
+ # Compares contents of local entry and exposed fields
458
+ (@compression_method == other.compression_method &&
459
+ @crc == other.crc &&
460
+ @compressed_size == other.compressed_size &&
461
+ @size == other.size &&
462
+ @name == other.name &&
463
+ @extra == other.extra &&
464
+ self.time.dos_equals(other.time))
465
+ end
466
+
467
+ def <=> (other)
468
+ return to_s <=> other.to_s
469
+ end
470
+
471
+ def get_input_stream
472
+ zis = ZipInputStream.new(@zipfile, localHeaderOffset)
473
+ zis.get_next_entry
474
+ if block_given?
475
+ begin
476
+ return yield(zis)
477
+ ensure
478
+ zis.close
479
+ end
480
+ else
481
+ return zis
482
+ end
483
+ end
484
+
485
+
486
+ def write_to_zip_output_stream(aZipOutputStream) #:nodoc:all
487
+ aZipOutputStream.copy_raw_entry(self)
488
+ end
489
+
490
+ def parent_as_string
491
+ entry_name = name.chomp("/")
492
+ slash_index = entry_name.rindex("/")
493
+ slash_index ? entry_name.slice(0, slash_index+1) : nil
494
+ end
495
+
496
+ def get_raw_input_stream(&aProc)
497
+ File.open(@zipfile, "rb", &aProc)
498
+ end
499
+
500
+ private
501
+ def set_time(binaryDosDate, binaryDosTime)
502
+ @time = Time.parse_binary_dos_format(binaryDosDate, binaryDosTime)
503
+ rescue ArgumentError
504
+ puts "Invalid date/time in zip entry"
505
+ end
506
+ end
507
+
508
+
509
+ class ZipOutputStream
510
+ include IOExtras::AbstractOutputStream
511
+
512
+ attr_accessor :comment
513
+
514
+ def initialize(fileName)
515
+ super()
516
+ @fileName = fileName
517
+ @outputStream = File.new(@fileName, "wb")
518
+ @entrySet = ZipEntrySet.new
519
+ @compressor = NullCompressor.instance
520
+ @closed = false
521
+ @currentEntry = nil
522
+ @comment = nil
523
+ end
524
+
525
+ def ZipOutputStream.open(fileName)
526
+ return new(fileName) unless block_given?
527
+ zos = new(fileName)
528
+ yield zos
529
+ ensure
530
+ zos.close if zos
531
+ end
532
+
533
+ def close
534
+ return if @closed
535
+ finalize_current_entry
536
+ update_local_headers
537
+ write_central_directory
538
+ @outputStream.close
539
+ @closed = true
540
+ end
541
+
542
+ def put_next_entry(entry, level = Zlib::DEFAULT_COMPRESSION)
543
+ raise ZipError, "zip stream is closed" if @closed
544
+ newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@fileName, entry.to_s)
545
+ init_next_entry(newEntry)
546
+ @currentEntry=newEntry
547
+ end
548
+
549
+ def copy_raw_entry(entry)
550
+ entry = entry.dup
551
+ raise ZipError, "zip stream is closed" if @closed
552
+ raise ZipError, "entry is not a ZipEntry" if !entry.kind_of?(ZipEntry)
553
+ finalize_current_entry
554
+ @entrySet << entry
555
+ src_pos = entry.local_entry_offset
556
+ entry.write_local_entry(@outputStream)
557
+ @compressor = NullCompressor.instance
558
+ @outputStream << entry.get_raw_input_stream {
559
+ |is|
560
+ is.seek(src_pos, IO::SEEK_SET)
561
+ is.read(entry.compressed_size)
562
+ }
563
+ @compressor = NullCompressor.instance
564
+ @currentEntry = nil
565
+ end
566
+
567
+ private
568
+ def finalize_current_entry
569
+ return unless @currentEntry
570
+ finish
571
+ @currentEntry.compressed_size = @outputStream.tell - @currentEntry.localHeaderOffset -
572
+ @currentEntry.local_header_size
573
+ @currentEntry.size = @compressor.size
574
+ @currentEntry.crc = @compressor.crc
575
+ @currentEntry = nil
576
+ @compressor = NullCompressor.instance
577
+ end
578
+
579
+ def init_next_entry(entry, level = Zlib::DEFAULT_COMPRESSION)
580
+ finalize_current_entry
581
+ @entrySet << entry
582
+ entry.write_local_entry(@outputStream)
583
+ @compressor = get_compressor(entry, level)
584
+ end
585
+
586
+ def get_compressor(entry, level)
587
+ case entry.compression_method
588
+ when ZipEntry::DEFLATED then Deflater.new(@outputStream, level)
589
+ when ZipEntry::STORED then PassThruCompressor.new(@outputStream)
590
+ else raise ZipCompressionMethodError,
591
+ "Invalid compression method: '#{entry.compression_method}'"
592
+ end
593
+ end
594
+
595
+ def update_local_headers
596
+ pos = @outputStream.tell
597
+ @entrySet.each {
598
+ |entry|
599
+ @outputStream.pos = entry.localHeaderOffset
600
+ entry.write_local_entry(@outputStream)
601
+ }
602
+ @outputStream.pos = pos
603
+ end
604
+
605
+ def write_central_directory
606
+ cdir = ZipCentralDirectory.new(@entrySet, @comment)
607
+ cdir.write_to_stream(@outputStream)
608
+ end
609
+
610
+ protected
611
+
612
+ def finish
613
+ @compressor.finish
614
+ end
615
+
616
+ public
617
+ def << (data)
618
+ @compressor << data
619
+ end
620
+ end
621
+
622
+
623
+ class Compressor #:nodoc:all
624
+ def finish
625
+ end
626
+ end
627
+
628
+ class PassThruCompressor < Compressor #:nodoc:all
629
+ def initialize(outputStream)
630
+ super()
631
+ @outputStream = outputStream
632
+ @crc = Zlib::crc32
633
+ @size = 0
634
+ end
635
+
636
+ def << (data)
637
+ val = data.to_s
638
+ @crc = Zlib::crc32(val, @crc)
639
+ @size += val.size
640
+ @outputStream << val
641
+ end
642
+
643
+ attr_reader :size, :crc
644
+ end
645
+
646
+ class NullCompressor < Compressor #:nodoc:all
647
+ include Singleton
648
+
649
+ def << (data)
650
+ raise IOError, "closed stream"
651
+ end
652
+
653
+ attr_reader :size, :compressed_size
654
+ end
655
+
656
+ class Deflater < Compressor #:nodoc:all
657
+ def initialize(outputStream, level = Zlib::DEFAULT_COMPRESSION)
658
+ super()
659
+ @outputStream = outputStream
660
+ @zlibDeflater = Zlib::Deflate.new(level, -Zlib::MAX_WBITS)
661
+ @size = 0
662
+ @crc = Zlib::crc32
663
+ end
664
+
665
+ def << (data)
666
+ val = data.to_s
667
+ @crc = Zlib::crc32(val, @crc)
668
+ @size += val.size
669
+ @outputStream << @zlibDeflater.deflate(data)
670
+ end
671
+
672
+ def finish
673
+ until @zlibDeflater.finished?
674
+ @outputStream << @zlibDeflater.finish
675
+ end
676
+ end
677
+
678
+ attr_reader :size, :crc
679
+ end
680
+
681
+
682
+ class ZipEntrySet
683
+ include Enumerable
684
+
685
+ def initialize(anEnumerable = [])
686
+ super()
687
+ @entrySet = {}
688
+ anEnumerable.each { |o| push(o) }
689
+ end
690
+
691
+ def include?(entry)
692
+ @entrySet.include?(entry.to_s)
693
+ end
694
+
695
+ def <<(entry)
696
+ @entrySet[entry.to_s] = entry
697
+ end
698
+ alias :push :<<
699
+
700
+ def size
701
+ @entrySet.size
702
+ end
703
+ alias :length :size
704
+
705
+ def delete(entry)
706
+ @entrySet.delete(entry.to_s) ? entry : nil
707
+ end
708
+
709
+ def each(&aProc)
710
+ @entrySet.values.each(&aProc)
711
+ end
712
+
713
+ def entries
714
+ @entrySet.values
715
+ end
716
+
717
+ # deep clone
718
+ def dup
719
+ newZipEntrySet = ZipEntrySet.new(@entrySet.values.map { |e| e.dup })
720
+ end
721
+
722
+ def == (other)
723
+ return false unless other.kind_of?(ZipEntrySet)
724
+ return @entrySet == other.entrySet
725
+ end
726
+
727
+ def parent(entry)
728
+ @entrySet[entry.parent_as_string]
729
+ end
730
+
731
+ #TODO attr_accessor :auto_create_directories
732
+ protected
733
+ attr_accessor :entrySet
734
+ end
735
+
736
+
737
+ class ZipCentralDirectory #:nodoc:all
738
+ include Enumerable
739
+
740
+ END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50
741
+ MAX_END_OF_CENTRAL_DIRECTORY_STRUCTURE_SIZE = 65536 + 18
742
+ STATIC_EOCD_SIZE = 22
743
+
744
+ attr_reader :comment
745
+
746
+ def entries
747
+ @entrySet.entries
748
+ end
749
+
750
+ def initialize(entries = ZipEntrySet.new, comment = "")
751
+ super()
752
+ @entrySet = entries.kind_of?(ZipEntrySet) ? entries : ZipEntrySet.new(entries)
753
+ @comment = comment
754
+ end
755
+
756
+ def write_to_stream(io)
757
+ offset = io.tell
758
+ @entrySet.each { |entry| entry.write_c_dir_entry(io) }
759
+ write_e_o_c_d(io, offset)
760
+ end
761
+
762
+ def write_e_o_c_d(io, offset)
763
+ io <<
764
+ [END_OF_CENTRAL_DIRECTORY_SIGNATURE,
765
+ 0 , # @numberOfThisDisk
766
+ 0 , # @numberOfDiskWithStartOfCDir
767
+ @entrySet? @entrySet.size : 0 ,
768
+ @entrySet? @entrySet.size : 0 ,
769
+ cdir_size ,
770
+ offset ,
771
+ @comment ? @comment.length : 0 ].pack('VvvvvVVv')
772
+ io << @comment
773
+ end
774
+ private :write_e_o_c_d
775
+
776
+ def cdir_size
777
+ # does not include eocd
778
+ @entrySet.inject(0) { |value, entry| entry.cdir_header_size + value }
779
+ end
780
+ private :cdir_size
781
+
782
+ def read_e_o_c_d(io)
783
+ buf = get_e_o_c_d(io)
784
+ @numberOfThisDisk = ZipEntry::read_zip_short(buf)
785
+ @numberOfDiskWithStartOfCDir = ZipEntry::read_zip_short(buf)
786
+ @totalNumberOfEntriesInCDirOnThisDisk = ZipEntry::read_zip_short(buf)
787
+ @size = ZipEntry::read_zip_short(buf)
788
+ @sizeInBytes = ZipEntry::read_zip_long(buf)
789
+ @cdirOffset = ZipEntry::read_zip_long(buf)
790
+ commentLength = ZipEntry::read_zip_short(buf)
791
+ @comment = buf.read(commentLength)
792
+ raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0
793
+ end
794
+
795
+ def read_central_directory_entries(io)
796
+ begin
797
+ io.seek(@cdirOffset, IO::SEEK_SET)
798
+ rescue Errno::EINVAL
799
+ raise ZipError, "Zip consistency problem while reading central directory entry"
800
+ end
801
+ @entrySet = ZipEntrySet.new
802
+ @size.times {
803
+ @entrySet << ZipEntry.read_c_dir_entry(io)
804
+ }
805
+ end
806
+
807
+ def read_from_stream(io)
808
+ read_e_o_c_d(io)
809
+ read_central_directory_entries(io)
810
+ end
811
+
812
+ def get_e_o_c_d(io)
813
+ begin
814
+ io.seek(-MAX_END_OF_CENTRAL_DIRECTORY_STRUCTURE_SIZE, IO::SEEK_END)
815
+ rescue Errno::EINVAL
816
+ io.seek(0, IO::SEEK_SET)
817
+ rescue Errno::EFBIG # FreeBSD 4.9 returns Errno::EFBIG instead of Errno::EINVAL
818
+ io.seek(0, IO::SEEK_SET)
819
+ end
820
+ buf = io.read
821
+ sigIndex = buf.rindex([END_OF_CENTRAL_DIRECTORY_SIGNATURE].pack('V'))
822
+ raise ZipError, "Zip end of central directory signature not found" unless sigIndex
823
+ buf=buf.slice!((sigIndex+4)...(buf.size))
824
+ def buf.read(count)
825
+ slice!(0, count)
826
+ end
827
+ return buf
828
+ end
829
+
830
+ def each(&proc)
831
+ @entrySet.each(&proc)
832
+ end
833
+
834
+ def size
835
+ @entrySet.size
836
+ end
837
+
838
+ def ZipCentralDirectory.read_from_stream(io)
839
+ cdir = new
840
+ cdir.read_from_stream(io)
841
+ return cdir
842
+ rescue ZipError
843
+ return nil
844
+ end
845
+
846
+ def == (other)
847
+ return false unless other.kind_of?(ZipCentralDirectory)
848
+ @entrySet.entries.sort == other.entries.sort && comment == other.comment
849
+ end
850
+ end
851
+
852
+
853
+ class ZipError < StandardError ; end
854
+
855
+ class ZipEntryExistsError < ZipError; end
856
+ class ZipDestinationFileExistsError < ZipError; end
857
+ class ZipCompressionMethodError < ZipError; end
858
+ class ZipEntryNameError < ZipError; end
859
+
860
+ class ZipFile < ZipCentralDirectory
861
+
862
+ CREATE = 1
863
+
864
+ attr_reader :name
865
+
866
+ def initialize(fileName, create = nil)
867
+ super()
868
+ @name = fileName
869
+ @comment = ""
870
+ if (File.exists?(fileName))
871
+ File.open(name, "rb") { |f| read_from_stream(f) }
872
+ elsif (create == ZipFile::CREATE)
873
+ @entrySet = ZipEntrySet.new
874
+ else
875
+ raise ZipError, "File #{fileName} not found"
876
+ end
877
+ @create = create
878
+ @storedEntries = @entrySet.dup
879
+ end
880
+
881
+ def ZipFile.open(fileName, create = nil)
882
+ zf = ZipFile.new(fileName, create)
883
+ if block_given?
884
+ begin
885
+ yield zf
886
+ ensure
887
+ zf.close
888
+ end
889
+ else
890
+ zf
891
+ end
892
+ end
893
+
894
+ attr_accessor :comment
895
+
896
+ def ZipFile.foreach(aZipFileName, &block)
897
+ ZipFile.open(aZipFileName) {
898
+ |zipFile|
899
+ zipFile.each(&block)
900
+ }
901
+ end
902
+
903
+ def get_input_stream(entry, &aProc)
904
+ get_entry(entry).get_input_stream(&aProc)
905
+ end
906
+
907
+ def get_output_stream(entry, &aProc)
908
+ newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@name, entry.to_s)
909
+ if newEntry.directory?
910
+ raise ArgumentError,
911
+ "cannot open stream to directory entry - '#{newEntry}'"
912
+ end
913
+ zipStreamableEntry = ZipStreamableStream.new(newEntry)
914
+ @entrySet << zipStreamableEntry
915
+ zipStreamableEntry.get_output_stream(&aProc)
916
+ end
917
+
918
+ def to_s
919
+ @name
920
+ end
921
+
922
+ def read(entry)
923
+ get_input_stream(entry) { |is| is.read }
924
+ end
925
+
926
+ def add(entry, srcPath, &continueOnExistsProc)
927
+ continueOnExistsProc ||= proc { false }
928
+ check_entry_exists(entry, continueOnExistsProc, "add")
929
+ newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@name, entry.to_s)
930
+ if is_directory(newEntry, srcPath)
931
+ @entrySet << ZipStreamableDirectory.new(newEntry)
932
+ else
933
+ @entrySet << ZipStreamableFile.new(newEntry, srcPath)
934
+ end
935
+ end
936
+
937
+ def remove(entry)
938
+ @entrySet.delete(get_entry(entry))
939
+ end
940
+
941
+ def rename(entry, newName, &continueOnExistsProc)
942
+ foundEntry = get_entry(entry)
943
+ check_entry_exists(newName, continueOnExistsProc, "rename")
944
+ foundEntry.name=newName
945
+ end
946
+
947
+ def replace(entry, srcPath)
948
+ check_file(srcPath)
949
+ add(remove(entry), srcPath)
950
+ end
951
+
952
+ def extract(entry, destPath, &onExistsProc)
953
+ onExistsProc ||= proc { false }
954
+ foundEntry = get_entry(entry)
955
+ if foundEntry.is_directory
956
+ create_directory(foundEntry, destPath, &onExistsProc)
957
+ else
958
+ write_file(foundEntry, destPath, &onExistsProc)
959
+ end
960
+ end
961
+
962
+ def commit
963
+ return if ! commit_required?
964
+ on_success_replace(name) {
965
+ |tmpFile|
966
+ ZipOutputStream.open(tmpFile) {
967
+ |zos|
968
+
969
+ @entrySet.each { |e| e.write_to_zip_output_stream(zos) }
970
+ zos.comment = comment
971
+ }
972
+ true
973
+ }
974
+ initialize(name)
975
+ end
976
+
977
+ def close
978
+ commit
979
+ end
980
+
981
+ def commit_required?
982
+ return @entrySet != @storedEntries || @create == ZipFile::CREATE
983
+ end
984
+
985
+ def find_entry(entry)
986
+ @entrySet.detect {
987
+ |e|
988
+ e.name.sub(/\/$/, "") == entry.to_s.sub(/\/$/, "")
989
+ }
990
+ end
991
+
992
+ def get_entry(entry)
993
+ selectedEntry = find_entry(entry)
994
+ unless selectedEntry
995
+ raise Errno::ENOENT, entry
996
+ end
997
+ return selectedEntry
998
+ end
999
+
1000
+ def mkdir(entryName, permissionInt = 0) #permissionInt ignored
1001
+ if find_entry(entryName)
1002
+ raise Errno::EEXIST, "File exists - #{entryName}"
1003
+ end
1004
+ @entrySet << ZipStreamableDirectory.new(ZipEntry.new(name, entryName.to_s.ensure_end("/")))
1005
+ end
1006
+
1007
+ private
1008
+
1009
+ def create_directory(entry, destPath)
1010
+ if File.directory? destPath
1011
+ return
1012
+ elsif File.exists? destPath
1013
+ if block_given? && yield(entry, destPath)
1014
+ File.rm_f destPath
1015
+ else
1016
+ raise ZipDestinationFileExistsError,
1017
+ "Cannot create directory '#{destPath}'. "+
1018
+ "A file already exists with that name"
1019
+ end
1020
+ end
1021
+ Dir.mkdir destPath
1022
+ end
1023
+
1024
+ def is_directory(newEntry, srcPath)
1025
+ srcPathIsDirectory = File.directory?(srcPath)
1026
+ if newEntry.is_directory && ! srcPathIsDirectory
1027
+ raise ArgumentError,
1028
+ "entry name '#{newEntry}' indicates directory entry, but "+
1029
+ "'#{srcPath}' is not a directory"
1030
+ elsif ! newEntry.is_directory && srcPathIsDirectory
1031
+ newEntry.name += "/"
1032
+ end
1033
+ return newEntry.is_directory && srcPathIsDirectory
1034
+ end
1035
+
1036
+ def check_entry_exists(entryName, continueOnExistsProc, procedureName)
1037
+ continueOnExistsProc ||= proc { false }
1038
+ if @entrySet.detect { |e| e.name == entryName }
1039
+ if continueOnExistsProc.call
1040
+ remove get_entry(entryName)
1041
+ else
1042
+ raise ZipEntryExistsError,
1043
+ procedureName+" failed. Entry #{entryName} already exists"
1044
+ end
1045
+ end
1046
+ end
1047
+
1048
+ def write_file(entry, destPath, continueOnExistsProc = proc { false })
1049
+ if File.exists?(destPath) && ! yield(entry, destPath)
1050
+
1051
+ # raise ZipDestinationFileExistsError,
1052
+ $stderr.puts "Destination '#{destPath}' already exists"
1053
+ end
1054
+ File.open(destPath, "wb") {
1055
+ |os|
1056
+ entry.get_input_stream { |is| os << is.read }
1057
+ }
1058
+ end
1059
+
1060
+ def check_file(path)
1061
+ unless File.readable? path
1062
+ raise Errno::ENOENT, path
1063
+ end
1064
+ end
1065
+
1066
+ def on_success_replace(aFilename)
1067
+ tmpfile = get_tempfile
1068
+ tmpFilename = tmpfile.path
1069
+ tmpfile.close
1070
+ if yield tmpFilename
1071
+ File.move(tmpFilename, name)
1072
+ end
1073
+ end
1074
+
1075
+ def get_tempfile
1076
+ tempFile = Tempfile.new(File.basename(name), File.dirname(name))
1077
+ tempFile.binmode
1078
+ tempFile
1079
+ end
1080
+
1081
+ end
1082
+
1083
+ class ZipStreamableFile < DelegateClass(ZipEntry) #:nodoc:all
1084
+ def initialize(entry, filepath)
1085
+ super(entry)
1086
+ @delegate = entry
1087
+ @filepath = filepath
1088
+ end
1089
+
1090
+ def get_input_stream(&aProc)
1091
+ File.open(@filepath, "rb", &aProc)
1092
+ end
1093
+
1094
+ def write_to_zip_output_stream(aZipOutputStream)
1095
+ aZipOutputStream.put_next_entry(self)
1096
+ aZipOutputStream << get_input_stream { |is| is.read }
1097
+ end
1098
+
1099
+ def == (other)
1100
+ return false unless other.class == ZipStreamableFile
1101
+ @filepath == other.filepath && super(other.delegate)
1102
+ end
1103
+
1104
+ protected
1105
+ attr_reader :filepath, :delegate
1106
+ end
1107
+
1108
+ class ZipStreamableDirectory < DelegateClass(ZipEntry) #:nodoc:all
1109
+ def initialize(entry)
1110
+ super(entry)
1111
+ end
1112
+
1113
+ def get_input_stream(&aProc)
1114
+ return yield(NullInputStream.instance) if block_given?
1115
+ NullInputStream.instance
1116
+ end
1117
+
1118
+ def write_to_zip_output_stream(aZipOutputStream)
1119
+ aZipOutputStream.put_next_entry(self)
1120
+ end
1121
+ end
1122
+
1123
+ class ZipStreamableStream < DelegateClass(ZipEntry) #nodoc:all
1124
+ def initialize(entry)
1125
+ super(entry)
1126
+ @tempFile = Tempfile.new(File.basename(name), File.dirname(zipfile))
1127
+ @tempFile.binmode
1128
+ end
1129
+
1130
+ def get_output_stream
1131
+ if block_given?
1132
+ begin
1133
+ yield(@tempFile)
1134
+ ensure
1135
+ @tempFile.close
1136
+ end
1137
+ else
1138
+ @tempFile
1139
+ end
1140
+ end
1141
+
1142
+ def get_input_stream
1143
+ if ! @tempFile.closed?
1144
+ raise StandardError, "cannot open entry for reading while its open for writing - #{name}"
1145
+ end
1146
+ @tempFile.open # reopens tempfile from top
1147
+ if block_given?
1148
+ begin
1149
+ yield(@tempFile)
1150
+ ensure
1151
+ @tempFile.close
1152
+ end
1153
+ else
1154
+ @tempFile
1155
+ end
1156
+ end
1157
+
1158
+ def write_to_zip_output_stream(aZipOutputStream)
1159
+ aZipOutputStream.put_next_entry(self)
1160
+ aZipOutputStream << get_input_stream { |is| is.read }
1161
+ end
1162
+ end
1163
+
1164
+ class ZipExtraField < Hash
1165
+ ID_MAP = {}
1166
+
1167
+ # Meta class for extra fields
1168
+ class Generic
1169
+ def self.register_map
1170
+ if self.const_defined?(:HEADER_ID)
1171
+ ID_MAP[self.const_get(:HEADER_ID)] = self
1172
+ end
1173
+ end
1174
+
1175
+ def self.name
1176
+ self.to_s.split("::")[-1]
1177
+ end
1178
+
1179
+ # return field [size, content] or false
1180
+ def initial_parse(binstr)
1181
+ if ! binstr
1182
+ # If nil, start with empty.
1183
+ return false
1184
+ elsif binstr[0,2] != self.class.const_get(:HEADER_ID)
1185
+ $stderr.puts "Warning: weired extra feild header ID. skip parsing"
1186
+ return false
1187
+ end
1188
+ [binstr[2,2].unpack("v")[0], binstr[4..-1]]
1189
+ end
1190
+
1191
+ def ==(other)
1192
+ self.class != other.class and return false
1193
+ each { |k, v|
1194
+ v != other[k] and return false
1195
+ }
1196
+ true
1197
+ end
1198
+
1199
+ def to_local_bin
1200
+ s = pack_for_local
1201
+ self.class.const_get(:HEADER_ID) + [s.length].pack("v") + s
1202
+ end
1203
+
1204
+ def to_c_dir_bin
1205
+ s = pack_for_c_dir
1206
+ self.class.const_get(:HEADER_ID) + [s.length].pack("v") + s
1207
+ end
1208
+ end
1209
+
1210
+ # Info-ZIP Additional timestamp field
1211
+ class UniversalTime < Generic
1212
+ HEADER_ID = "UT"
1213
+ register_map
1214
+
1215
+ def initialize(binstr = nil)
1216
+ @ctime = nil
1217
+ @mtime = nil
1218
+ @atime = nil
1219
+ @flag = nil
1220
+ binstr and merge(binstr)
1221
+ end
1222
+ attr_accessor :atime, :ctime, :mtime, :flag
1223
+
1224
+ def merge(binstr)
1225
+ binstr == "" and return
1226
+ size, content = initial_parse(binstr)
1227
+ size or return
1228
+ @flag, mtime, atime, ctime = content.unpack("CVVV")
1229
+ mtime and @mtime ||= Time.at(mtime)
1230
+ atime and @atime ||= Time.at(atime)
1231
+ ctime and @ctime ||= Time.at(ctime)
1232
+ end
1233
+
1234
+ def ==(other)
1235
+ @mtime == other.mtime &&
1236
+ @atime == other.atime &&
1237
+ @ctime == other.ctime
1238
+ end
1239
+
1240
+ def pack_for_local
1241
+ s = [@flag].pack("C")
1242
+ @flag & 1 != 0 and s << [@mtime.to_i].pack("V")
1243
+ @flag & 2 != 0 and s << [@atime.to_i].pack("V")
1244
+ @flag & 4 != 0 and s << [@ctime.to_i].pack("V")
1245
+ s
1246
+ end
1247
+
1248
+ def pack_for_c_dir
1249
+ s = [@flag].pack("C")
1250
+ @flag & 1 == 1 and s << [@mtime.to_i].pack("V")
1251
+ s
1252
+ end
1253
+ end
1254
+
1255
+ # Info-ZIP Extra for UNIX uid/gid
1256
+ class IUnix < Generic
1257
+ HEADER_ID = "Ux"
1258
+ register_map
1259
+
1260
+ def initialize(binstr = nil)
1261
+ @uid = nil
1262
+ @gid = nil
1263
+ binstr and merge(binstr)
1264
+ end
1265
+ attr_accessor :uid, :gid
1266
+
1267
+ def merge(binstr)
1268
+ binstr == "" and return
1269
+ size, content = initial_parse(binstr)
1270
+ # size: 0 for central direcotry. 4 for local header
1271
+ return if(! size || size == 0)
1272
+ uid, gid = content.unpack("vv")
1273
+ @uid ||= uid
1274
+ @gid ||= gid
1275
+ end
1276
+
1277
+ def ==(other)
1278
+ @uid == other.uid &&
1279
+ @gid == other.gid
1280
+ end
1281
+
1282
+ def pack_for_local
1283
+ [@uid, @gid].pack("vv")
1284
+ end
1285
+
1286
+ def pack_for_c_dir
1287
+ ""
1288
+ end
1289
+ end
1290
+
1291
+ ## start main of ZipExtraField < Hash
1292
+ def initialize(binstr = nil)
1293
+ binstr and merge(binstr)
1294
+ end
1295
+
1296
+ def merge(binstr)
1297
+ binstr == "" and return
1298
+ i = 0
1299
+ while i < binstr.length
1300
+ id = binstr[i,2]
1301
+ len = binstr[i+2,2].to_s.unpack("v")[0]
1302
+ if id && ID_MAP.member?(id)
1303
+ field_name = ID_MAP[id].name
1304
+ if self.member?(field_name)
1305
+ self[field_name].mergea(binstr[i, len+4])
1306
+ else
1307
+ field_obj = ID_MAP[id].new(binstr[i, len+4])
1308
+ self[field_name] = field_obj
1309
+ end
1310
+ elsif id
1311
+ unless self["Unknown"]
1312
+ s = ""
1313
+ class << s
1314
+ alias_method :to_c_dir_bin, :to_s
1315
+ alias_method :to_local_bin, :to_s
1316
+ end
1317
+ self["Unknown"] = s
1318
+ end
1319
+ if ! len || len+4 > binstr[i..-1].length
1320
+ self["Unknown"] << binstr[i..-1]
1321
+ break;
1322
+ end
1323
+ self["Unknown"] << binstr[i, len+4]
1324
+ end
1325
+ i += len+4
1326
+ end
1327
+ end
1328
+
1329
+ def create(name)
1330
+ field_class = nil
1331
+ ID_MAP.each { |id, klass|
1332
+ if klass.name == name
1333
+ field_class = klass
1334
+ break
1335
+ end
1336
+ }
1337
+ if ! field_class
1338
+ raise ZipError, "Unknown extra field '#{name}'"
1339
+ end
1340
+ self[name] = field_class.new()
1341
+ end
1342
+
1343
+ def to_local_bin
1344
+ s = ""
1345
+ each { |k, v|
1346
+ s << v.to_local_bin
1347
+ }
1348
+ s
1349
+ end
1350
+ alias :to_s :to_local_bin
1351
+
1352
+ def to_c_dir_bin
1353
+ s = ""
1354
+ each { |k, v|
1355
+ s << v.to_c_dir_bin
1356
+ }
1357
+ s
1358
+ end
1359
+
1360
+ def c_dir_length
1361
+ to_c_dir_bin.length
1362
+ end
1363
+ def local_length
1364
+ to_local_bin.length
1365
+ end
1366
+ alias :c_dir_size :c_dir_length
1367
+ alias :local_size :local_length
1368
+ alias :length :local_length
1369
+ alias :size :local_length
1370
+ end # end ZipExtraField
1371
+
1372
+ end # Zip namespace module
1373
+
1374
+
1375
+
1376
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
1377
+ # rubyzip is free software; you can redistribute it and/or
1378
+ # modify it under the terms of the ruby license.