rwdeliza 0.03

Sign up to get free protection for your applications and to get access to all the features.
Files changed (222) hide show
  1. data/ElizaData/database/db.1 +7015 -0
  2. data/ElizaData/database/db.10 +7001 -0
  3. data/ElizaData/database/db.11 +7001 -0
  4. data/ElizaData/database/db.12 +7003 -0
  5. data/ElizaData/database/db.13 +7003 -0
  6. data/ElizaData/database/db.14 +7008 -0
  7. data/ElizaData/database/db.15 +7001 -0
  8. data/ElizaData/database/db.16 +7001 -0
  9. data/ElizaData/database/db.17 +7001 -0
  10. data/ElizaData/database/db.18 +7001 -0
  11. data/ElizaData/database/db.19 +7001 -0
  12. data/ElizaData/database/db.2 +7001 -0
  13. data/ElizaData/database/db.20 +5467 -0
  14. data/ElizaData/database/db.3 +7001 -0
  15. data/ElizaData/database/db.4 +7001 -0
  16. data/ElizaData/database/db.5 +7001 -0
  17. data/ElizaData/database/db.6 +7001 -0
  18. data/ElizaData/database/db.7 +7001 -0
  19. data/ElizaData/database/db.8 +7001 -0
  20. data/ElizaData/database/db.9 +7001 -0
  21. data/ElizaData/responses/hello.res +1 -0
  22. data/ElizaData/responses/i_agree.res +1 -0
  23. data/ElizaData/responses/i_am_listening_to_you.res +1 -0
  24. data/ElizaData/responses/please_explain.res +1 -0
  25. data/ElizaData/responses/thank_you.res +1 -0
  26. data/ElizaData/tiny.dict +211 -0
  27. data/ElizaData/words/adjectives1.words +906 -0
  28. data/ElizaData/words/adjectives2w.words +1 -0
  29. data/ElizaData/words/noun0.words +15 -0
  30. data/ElizaData/words/noun1.words +1391 -0
  31. data/ElizaData/words/noun2.words +1924 -0
  32. data/ElizaData/words/noun4.words +330 -0
  33. data/ElizaData/words/pronoun1.words +6 -0
  34. data/ElizaData/words/verb4.words +350 -0
  35. data/ElizaData/words/verb42.words +391 -0
  36. data/ElizaData/words/verb42w.words +1 -0
  37. data/ElizaData/words/verb43.words +402 -0
  38. data/ElizaData/words/verb43w.words +1 -0
  39. data/ElizaData/words/verb45.words +452 -0
  40. data/ElizaData/words/verb4w.words +1 -0
  41. data/ElizaData/words/verb5.words +13 -0
  42. data/ElizaData/words/verb61.words +35 -0
  43. data/ElizaData/words/verb62.words +41 -0
  44. data/ElizaData/words/verb83.words +17 -0
  45. data/Readme.txt +462 -0
  46. data/bin/rwdeliza +19 -0
  47. data/code/01rwdcore/01rwdcore.rb +29 -0
  48. data/code/01rwdcore/02helptexthashbegin.rb +4 -0
  49. data/code/01rwdcore/03helptexthash.rb +23 -0
  50. data/code/01rwdcore/04helptextend.rb +6 -0
  51. data/code/01rwdcore/jumplinkcommand.rb +26 -0
  52. data/code/01rwdcore/openhelpwindow.rb +31 -0
  53. data/code/01rwdcore/returntomain.rb +10 -0
  54. data/code/01rwdcore/rundocuments.rb +10 -0
  55. data/code/01rwdcore/runeditconfiguration.rb +10 -0
  56. data/code/01rwdcore/runhelpabout.rb +10 -0
  57. data/code/01rwdcore/runopentinkerdocument.rb +7 -0
  58. data/code/01rwdcore/rwdtinkerversion.rb +22 -0
  59. data/code/01rwdcore/rwdwindowreturn.rb +9 -0
  60. data/code/01rwdcore/selectiontab.rb +9 -0
  61. data/code/01rwdcore/setuphelpaboutoptions.rb +13 -0
  62. data/code/01rwdcore/setuptinkerdocuments.rb +6 -0
  63. data/code/01rwdcore/test_cases.rb +109 -0
  64. data/code/01rwdcore/test_harness.rb +13 -0
  65. data/code/01rwdcore/uploadreturns.rb +62 -0
  66. data/code/dd0viewphoto/dd0viewphoto.rb +3 -0
  67. data/code/superant.com.rwdeliza/0uninstallapplet.rb +10 -0
  68. data/code/superant.com.rwdeliza/eliza01.rb +45 -0
  69. data/code/superant.com.rwdeliza/helptexthashrwdeliza.rb +39 -0
  70. data/code/superant.com.rwdeliza/openhelpwindowrwdeliza.rb +23 -0
  71. data/code/superant.com.rwdeliza/runrwdshellwindow.rb +12 -0
  72. data/code/superant.com.rwdeliza/rwdtinkerversion.rb +10 -0
  73. data/code/superant.com.rwdeliza/tagsentence.rb +39 -0
  74. data/code/superant.com.rwdtinkerbackwindow/diagnostictab.rb +19 -0
  75. data/code/superant.com.rwdtinkerbackwindow/helptexthashtinkerwin2.rb +61 -0
  76. data/code/superant.com.rwdtinkerbackwindow/initiateapplets.rb +240 -0
  77. data/code/superant.com.rwdtinkerbackwindow/installgemapplet.rb +34 -0
  78. data/code/superant.com.rwdtinkerbackwindow/installremotegem.rb +20 -0
  79. data/code/superant.com.rwdtinkerbackwindow/listgemdirs.rb +12 -0
  80. data/code/superant.com.rwdtinkerbackwindow/listgemzips.rb +53 -0
  81. data/code/superant.com.rwdtinkerbackwindow/listinstalledfiles.rb +12 -0
  82. data/code/superant.com.rwdtinkerbackwindow/listzips.rb +27 -0
  83. data/code/superant.com.rwdtinkerbackwindow/loadconfigurationrecord.rb +22 -0
  84. data/code/superant.com.rwdtinkerbackwindow/loadconfigurationvariables.rb +14 -0
  85. data/code/superant.com.rwdtinkerbackwindow/network.rb +87 -0
  86. data/code/superant.com.rwdtinkerbackwindow/openappletname.rb +19 -0
  87. data/code/superant.com.rwdtinkerbackwindow/openhelpwindowtinkerwin2.rb +38 -0
  88. data/code/superant.com.rwdtinkerbackwindow/remotegemlist.rb +24 -0
  89. data/code/superant.com.rwdtinkerbackwindow/removeapplet.rb +46 -0
  90. data/code/superant.com.rwdtinkerbackwindow/removeappletvariables.rb +52 -0
  91. data/code/superant.com.rwdtinkerbackwindow/runremoteinstall.rb +11 -0
  92. data/code/superant.com.rwdtinkerbackwindow/runrwdtinkerbackwindow.rb +15 -0
  93. data/code/superant.com.rwdtinkerbackwindow/rwdtinkerwin2version.rb +13 -0
  94. data/code/superant.com.rwdtinkerbackwindow/saveconfigurationrecord.rb +19 -0
  95. data/code/superant.com.rwdtinkerbackwindow/viewappletcontents.rb +22 -0
  96. data/code/superant.com.rwdtinkerbackwindow/viewgemappletcontents.rb +24 -0
  97. data/code/superant.com.words/dictlookup.rb +20 -0
  98. data/code/superant.com.words/runfortunewindow.rb +14 -0
  99. data/code/superant.com.words/runrwdwordsbackwindow.rb +10 -0
  100. data/code/superant.com.words/runrwdwordsversion.rb +14 -0
  101. data/code/superant.com.words/rwdtinkerversion.rb +10 -0
  102. data/code/zz0applicationend/zz0end.rb +4 -0
  103. data/configuration/language.dist +8 -0
  104. data/configuration/rwdapplicationidentity.dist +3 -0
  105. data/configuration/rwdtinker.dist +18 -0
  106. data/configuration/rwdweliza-0.03.dist +31 -0
  107. data/configuration/tinkerwin2variables.dist +17 -0
  108. data/gui/00coreguibegin/applicationguitop.rwd +4 -0
  109. data/gui/frontwindow0/cc0openphoto.rwd +22 -0
  110. data/gui/frontwindowselections/00selectiontabbegin.rwd +11 -0
  111. data/gui/frontwindowselections/jumplinkcommands.rwd +15 -0
  112. data/gui/frontwindowselections/wwselectionend.rwd +3 -0
  113. data/gui/frontwindowtdocuments/00documentbegin.rwd +6 -0
  114. data/gui/frontwindowtdocuments/tinkerdocuments.rwd +14 -0
  115. data/gui/frontwindowtdocuments/zzdocumentend.rwd +8 -0
  116. data/gui/helpaboutbegin/zzzrwdlasttab.rwd +6 -0
  117. data/gui/helpaboutbegin/zzzzhelpscreenstart.rwd +3 -0
  118. data/gui/helpaboutbegin/zzzzzzhelpabouttab.rwd +15 -0
  119. data/gui/helpaboutzend/helpscreenend.rwd +3 -0
  120. data/gui/helpaboutzend/zhelpscreenstart2.rwd +3 -0
  121. data/gui/helpaboutzend/zzzzhelpabout2.rwd +15 -0
  122. data/gui/helpaboutzend/zzzzhelpscreen2end.rwd +3 -0
  123. data/gui/tinkerbackwindows/superant.com.refreshwindow/fortunerefreshwindowtwo.rwd +9 -0
  124. data/gui/tinkerbackwindows/superant.com.rwdeliza/1appname.rwd +5 -0
  125. data/gui/tinkerbackwindows/superant.com.rwdeliza/1eliza.rwd +21 -0
  126. data/gui/tinkerbackwindows/superant.com.rwdeliza/4sentance.rwd +21 -0
  127. data/gui/tinkerbackwindows/superant.com.rwdeliza/98jumplinkcommands.rwd +17 -0
  128. data/gui/tinkerbackwindows/superant.com.rwdeliza/zbackend.rwd +6 -0
  129. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/1appname.rwd +5 -0
  130. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/20downloadftp.rwd +45 -0
  131. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/67viewconfiguration.rwd +29 -0
  132. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/70rwddiagnostics.rwd +16 -0
  133. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/m01menubegin.rwd +18 -0
  134. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/zvbackend.rwd +6 -0
  135. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/1appname.rwd +5 -0
  136. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/40rwdlistzips.rwd +41 -0
  137. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/45installremotezip.rwd +44 -0
  138. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/50rwdlistapplets.rwd +44 -0
  139. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/60editconfiguration.rwd +30 -0
  140. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/70rwddiagnostics.rwd +29 -0
  141. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/81jumplinkcommands.rwd +17 -0
  142. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/9backend.rwd +6 -0
  143. data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/1appname.rwd +31 -0
  144. data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/9end.rwd +4 -0
  145. data/gui/tinkerbackwindows/superant.com.versionwindow/1appname.rwd +19 -0
  146. data/gui/tinkerbackwindows/superant.com.versionwindow/helpaboutwindow.rwd +17 -0
  147. data/gui/tinkerbackwindows/superant.com.words/1appname.rwd +4 -0
  148. data/gui/tinkerbackwindows/superant.com.words/1dictionary.rwd +19 -0
  149. data/gui/tinkerbackwindows/superant.com.words/3rwdfortune.rwd +14 -0
  150. data/gui/tinkerbackwindows/superant.com.words/77jumplinkcommands.rwd +17 -0
  151. data/gui/tinkerbackwindows/superant.com.words/z9end.rwd +6 -0
  152. data/gui/zzcoreguiend/yy9rwdend.rwd +4 -0
  153. data/init.rb +277 -0
  154. data/installed/rwdweliza-0.03.inf +24 -0
  155. data/installed/temp.rb +1 -0
  156. data/lang/en/rwdcore/languagefile.rb +58 -0
  157. data/lang/es/rwdcore/languagefile-es.rb +62 -0
  158. data/lang/fr/rwdcore/languagefile.rb +64 -0
  159. data/lang/jp/rwdcore/languagefile.rb +72 -0
  160. data/lang/nl/rwdcore/languagefile.rb +75 -0
  161. data/lib/dict.rb +438 -0
  162. data/lib/druida.rb +499 -0
  163. data/lib/hashslice.rb +71 -0
  164. data/lib/linguistics.rb +360 -0
  165. data/lib/linguistics/en.rb +1601 -0
  166. data/lib/linguistics/en/infinitive.rb +1148 -0
  167. data/lib/linguistics/en/linkparser.rb +142 -0
  168. data/lib/linguistics/en/wordnet.rb +253 -0
  169. data/lib/linguistics/iso639.rb +456 -0
  170. data/lib/linkparser.rb +461 -0
  171. data/lib/linkparser/connection.rb +81 -0
  172. data/lib/linkparser/connector.rb +201 -0
  173. data/lib/linkparser/definition.rb +225 -0
  174. data/lib/linkparser/dictionary.rb +208 -0
  175. data/lib/linkparser/linkage.rb +185 -0
  176. data/lib/linkparser/log.rb +39 -0
  177. data/lib/linkparser/sentence.rb +79 -0
  178. data/lib/linkparser/utils.rb +540 -0
  179. data/lib/linkparser/word.rb +92 -0
  180. data/lib/rconftool.rb +380 -0
  181. data/lib/rwd/browser.rb +123 -0
  182. data/lib/rwd/ftools.rb +174 -0
  183. data/lib/rwd/mime.rb +328 -0
  184. data/lib/rwd/net.rb +866 -0
  185. data/lib/rwd/ruby.rb +889 -0
  186. data/lib/rwd/rwd.rb +1942 -0
  187. data/lib/rwd/sgml.rb +236 -0
  188. data/lib/rwd/thread.rb +63 -0
  189. data/lib/rwd/tree.rb +371 -0
  190. data/lib/rwd/xml.rb +101 -0
  191. data/lib/zip/ioextras.rb +114 -0
  192. data/lib/zip/stdrubyext.rb +111 -0
  193. data/lib/zip/tempfile_bugfixed.rb +195 -0
  194. data/lib/zip/zip.rb +1378 -0
  195. data/lib/zip/zipfilesystem.rb +558 -0
  196. data/lib/zip/ziprequire.rb +61 -0
  197. data/rwd_files/HowTo_Eliza.txt +195 -0
  198. data/rwd_files/HowTo_Tinker.txt +471 -0
  199. data/rwd_files/HowTo_TinkerWin2.txt +202 -0
  200. data/rwd_files/Readme.txt +57 -0
  201. data/rwd_files/RubyWebDialogs.html +6 -0
  202. data/rwd_files/favicon.ico +0 -0
  203. data/rwd_files/rdoc-style.css +175 -0
  204. data/rwd_files/rwdapplications.html +54 -0
  205. data/rwd_files/tinker.png +0 -0
  206. data/rwdconfig.dist +21 -0
  207. data/rwdeliza.rb +1 -0
  208. data/tests/RubyGauge.rb +179 -0
  209. data/tests/checkdepends.sh +4 -0
  210. data/tests/cleancnf.sh +6 -0
  211. data/tests/makedist-rwdweliza.rb +56 -0
  212. data/tests/makedist.rb +66 -0
  213. data/tests/rdep.rb +354 -0
  214. data/tests/totranslate.lang +93 -0
  215. data/zips/rwdwaddresses-1.05.zip +0 -0
  216. data/zips/rwdwcalc-0.61.zip +0 -0
  217. data/zips/rwdwgutenberg-0.09.zip +0 -0
  218. data/zips/rwdwschedule-1.04.zip +0 -0
  219. data/zips/rwdwshell-1.04.zip +0 -0
  220. data/zips/temp.rb +1 -0
  221. data/zips/wrubyslippers-1.06.zip +0 -0
  222. metadata +282 -0
@@ -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.