writeexcel 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. data/.gitattributes +1 -1
  2. data/.gitignore +24 -24
  3. data/README.rdoc +34 -55
  4. data/VERSION +1 -1
  5. data/charts/chartex.rb +316 -316
  6. data/charts/demo1.rb +46 -46
  7. data/charts/demo2.rb +65 -65
  8. data/charts/demo3.rb +117 -117
  9. data/charts/demo4.rb +119 -119
  10. data/charts/demo5.rb +48 -48
  11. data/examples/a_simple.rb +43 -43
  12. data/examples/autofilter.rb +265 -265
  13. data/examples/bigfile.rb +30 -30
  14. data/examples/chart_area.rb +121 -121
  15. data/examples/chart_bar.rb +120 -120
  16. data/examples/chart_column.rb +120 -120
  17. data/examples/chart_line.rb +120 -120
  18. data/examples/chart_pie.rb +108 -108
  19. data/examples/chart_scatter.rb +121 -121
  20. data/examples/chart_stock.rb +148 -148
  21. data/examples/chess.rb +142 -142
  22. data/examples/colors.rb +129 -129
  23. data/examples/comments1.rb +27 -27
  24. data/examples/comments2.rb +352 -352
  25. data/examples/copyformat.rb +52 -52
  26. data/examples/data_validate.rb +279 -279
  27. data/examples/date_time.rb +87 -87
  28. data/examples/defined_name.rb +32 -32
  29. data/examples/demo.rb +124 -124
  30. data/examples/diag_border.rb +36 -36
  31. data/examples/formats.rb +490 -490
  32. data/examples/formula_result.rb +30 -30
  33. data/examples/header.rb +137 -137
  34. data/examples/hide_sheet.rb +29 -29
  35. data/examples/hyperlink.rb +43 -43
  36. data/examples/images.rb +63 -63
  37. data/examples/indent.rb +31 -31
  38. data/examples/merge1.rb +40 -40
  39. data/examples/merge2.rb +45 -45
  40. data/examples/merge3.rb +66 -66
  41. data/examples/merge4.rb +83 -83
  42. data/examples/merge5.rb +80 -80
  43. data/examples/merge6.rb +67 -67
  44. data/examples/outline.rb +255 -255
  45. data/examples/outline_collapsed.rb +209 -209
  46. data/examples/panes.rb +113 -113
  47. data/examples/properties.rb +34 -34
  48. data/examples/properties_jp.rb +33 -33
  49. data/examples/protection.rb +47 -47
  50. data/examples/regions.rb +53 -53
  51. data/examples/repeat.rb +43 -43
  52. data/examples/right_to_left.rb +27 -27
  53. data/examples/row_wrap.rb +53 -53
  54. data/examples/stats.rb +74 -74
  55. data/examples/stocks.rb +81 -81
  56. data/examples/tab_colors.rb +31 -31
  57. data/examples/utf8.rb +15 -15
  58. data/examples/write_arrays.rb +83 -83
  59. data/lib/writeexcel/biffwriter.rb +232 -232
  60. data/lib/writeexcel/caller_info.rb +12 -12
  61. data/lib/writeexcel/chart.rb +2190 -2177
  62. data/lib/writeexcel/charts/area.rb +154 -154
  63. data/lib/writeexcel/charts/bar.rb +177 -177
  64. data/lib/writeexcel/charts/column.rb +156 -156
  65. data/lib/writeexcel/charts/external.rb +66 -66
  66. data/lib/writeexcel/charts/line.rb +154 -154
  67. data/lib/writeexcel/charts/pie.rb +169 -169
  68. data/lib/writeexcel/charts/scatter.rb +192 -192
  69. data/lib/writeexcel/charts/stock.rb +213 -213
  70. data/lib/writeexcel/colors.rb +64 -64
  71. data/lib/writeexcel/compatibility.rb +0 -255
  72. data/lib/writeexcel/debug_info.rb +37 -33
  73. data/lib/writeexcel/excelformulaparser.rb +587 -587
  74. data/lib/writeexcel/format.rb +13 -4
  75. data/lib/writeexcel/formula.rb +26 -9
  76. data/lib/writeexcel/helper.rb +68 -64
  77. data/lib/writeexcel/olewriter.rb +311 -311
  78. data/lib/writeexcel/properties.rb +242 -240
  79. data/lib/writeexcel/storage_lite.rb +984 -978
  80. data/lib/writeexcel/workbook.rb +3210 -3192
  81. data/lib/writeexcel/worksheet.rb +143 -51
  82. data/lib/writeexcel/write_file.rb +44 -40
  83. data/lib/writeexcel.rb +1159 -1159
  84. data/test/helper.rb +31 -28
  85. data/test/perl_output/README +31 -31
  86. data/test/test_00_IEEE_double.rb +13 -13
  87. data/test/test_01_add_worksheet.rb +10 -10
  88. data/test/test_02_merge_formats.rb +53 -53
  89. data/test/test_04_dimensions.rb +392 -392
  90. data/test/test_05_rows.rb +179 -179
  91. data/test/test_06_extsst.rb +77 -77
  92. data/test/test_11_date_time.rb +479 -479
  93. data/test/test_12_date_only.rb +501 -501
  94. data/test/test_13_date_seconds.rb +481 -481
  95. data/test/test_21_escher.rb +637 -637
  96. data/test/test_22_mso_drawing_group.rb +745 -745
  97. data/test/test_23_note.rb +73 -73
  98. data/test/test_24_txo.rb +75 -75
  99. data/test/test_25_position_object.rb +84 -84
  100. data/test/test_26_autofilter.rb +314 -314
  101. data/test/test_27_autofilter.rb +131 -131
  102. data/test/test_28_autofilter.rb +161 -161
  103. data/test/test_29_process_jpg.rb +683 -683
  104. data/test/test_30_validation_dval.rb +77 -77
  105. data/test/test_31_validation_dv_strings.rb +126 -126
  106. data/test/test_32_validation_dv_formula.rb +206 -206
  107. data/test/test_40_property_types.rb +188 -188
  108. data/test/test_41_properties.rb +235 -235
  109. data/test/test_42_set_properties.rb +437 -437
  110. data/test/test_50_name_stored.rb +299 -299
  111. data/test/test_51_name_print_area.rb +357 -357
  112. data/test/test_52_name_print_titles.rb +454 -454
  113. data/test/test_53_autofilter.rb +203 -203
  114. data/test/test_60_chart_generic.rb +578 -578
  115. data/test/test_61_chart_subclasses.rb +95 -95
  116. data/test/test_62_chart_formats.rb +272 -272
  117. data/test/test_63_chart_area_formats.rb +649 -649
  118. data/test/test_biff.rb +75 -75
  119. data/test/test_compatibility.rb +12 -627
  120. data/test/test_example_match.rb +3144 -3144
  121. data/test/test_formula.rb +61 -61
  122. data/test/test_ole.rb +106 -106
  123. data/test/test_storage_lite.rb +125 -125
  124. data/test/test_workbook.rb +139 -139
  125. data/test/test_worksheet.rb +110 -110
  126. data/utils/add_magic_comment.rb +80 -80
  127. data/writeexcel.gemspec +4 -6
  128. data/writeexcel.rdoc +58 -15
  129. metadata +9 -6
  130. data/test/test_new_encoding.rb +0 -205
@@ -1,978 +1,984 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # OLE::Storage_Lite
4
- # by Kawai, Takanori (Hippo2000) 2000.11.4, 8, 14
5
- # This Program is Still ALPHA version.
6
- #//////////////////////////////////////////////////////////////////////////////
7
- #
8
- # converted from CPAN's OLE::Storage_Lite.
9
- # converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
10
- #
11
-
12
- require 'tempfile'
13
- require 'stringio'
14
-
15
- class OLEStorageLite #:nodoc:
16
- PPS_TYPE_ROOT = 5
17
- PPS_TYPE_DIR = 1
18
- PPS_TYPE_FILE = 2
19
- DATA_SIZE_SMALL = 0x1000
20
- LONG_INT_SIZE = 4
21
- PPS_SIZE = 0x80
22
-
23
- attr_reader :file
24
-
25
- def initialize(file = nil)
26
- @file = file
27
- end
28
-
29
- def getPpsTree(data)
30
- info = _initParse(file)
31
- info ? _getPpsTree(0, info, data) : nil
32
- end
33
-
34
- def getPpsSearch(name, data, icase)
35
- info = _initParse(file)
36
- info ? _getPpsSearch(0, info, name, data, icase) : nil
37
- end
38
-
39
- def getNthPps(no, data)
40
- info = _initParse(file)
41
- info ? _getNthPps(no, info, data) : nil
42
- end
43
-
44
- def _initParse(file)
45
- io = file.respond_to?(:to_str) ? open(file, 'rb') : file
46
- _getHeaderInfo(io)
47
- end
48
- private :_initParse
49
-
50
- def _getPpsTree(no, info, data, done)
51
- if done
52
- return [] if done.include?(no)
53
- else
54
- done = []
55
- end
56
- done << no
57
-
58
- rootblock = info[:root_start]
59
-
60
- #1. Get Information about itself
61
- pps = _getNthPps(no, info, data)
62
-
63
- #2. Child
64
- if pps.dir_pps != 0xFFFFFFFF
65
- pps.child = _getPpsTree(pps.dir_pps, info, data, done)
66
- else
67
- pps.child = nil
68
- end
69
-
70
- #3. Previous,Next PPSs
71
- list = []
72
- list << _getPpsTree(pps.prev_pps, info, data, done) if pps.prev_pps != 0xFFFFFFFF
73
- list << pps
74
- list << _getPpsTree(pps.next_pps, info, data, done) if pps.next_pps != 0xFFFFFFFF
75
- end
76
- private :_getPpsTree
77
-
78
- def _getPpsSearch(no, info, name, data, icase, done = nil)
79
- rootblock = info[:root_start]
80
- #1. Check it self
81
- if done
82
- return [] if done.include?(no)
83
- else
84
- done = []
85
- end
86
- done << no
87
- pps = _getNthPps(no, info, nil)
88
-
89
- re = Regexp.new("^\Q#{pps.name}\E$", Regexp::IGNORECASE)
90
- if (icase && !name.select { |v| v =~ re }.empty?) || name.include?(pps.name)
91
- pps = _getNthPps(no, info, data) if data
92
- res = [pps]
93
- else
94
- res = []
95
- end
96
-
97
- #2. Check Child, Previous, Next PPSs
98
- res +=
99
- _getPpsSearch(pps.dir_pps, info, name, data, icase, done) if pps.dir_pps != 0xFFFFFFFF
100
- res +=
101
- _getPpsSearch(pps.prev_pps, info, name, data, icase, done) if pps.prev_pps != 0xFFFFFFFF
102
- res +=
103
- _getPpsSearch(pps.next_pps, info, name, data, icase, done) if pps.next_pps != 0xFFFFFFFF
104
- res
105
- end
106
- private :_getPpsSearch
107
-
108
- def _getHeaderInfo(io)
109
- info = { :fileh => io }
110
-
111
- #0. Check ID
112
- info[:fileh].seek(0, 0)
113
- return nil unless info[:fileh].read(8) == "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
114
-
115
- # BIG BLOCK SIZE
116
- val = _getInfoFromFile(info[:fileh], 0x1E, 2, "v")
117
- return nil if val.nil?
118
- info[:big_block_size] = 2 ** val
119
-
120
- # SMALL BLOCK SIZE
121
- val = _getInfoFromFile(info[:fileh], 0x20, 2, "v")
122
- return nil if val.nil?
123
- info[:small_block_size] = 2 ** val
124
-
125
- # BDB Count
126
- val = _getInfoFromFile(info[:fileh], 0x2C, 4, "V")
127
- return nil if val.nil?
128
- info[:bdb_count] = val
129
-
130
- # START BLOCK
131
- val = _getInfoFromFile(info[:fileh], 0x30, 4, "V")
132
- return nil if val.nil?
133
- info[:root_start] = val
134
-
135
- # SMALL BD START
136
- val = _getInfoFromFile(info[:fileh], 0x3C, 4, "V")
137
- return nil if val.nil?
138
- info[:sbd_start] = val
139
-
140
- # SMALL BD COUNT
141
- val = _getInfoFromFile(info[:fileh], 0x40, 4, "V")
142
- return nil if val.nil?
143
- info[:sbd_count] = val
144
-
145
- # EXTRA BBD START
146
- val = _getInfoFromFile(info[:fileh], 0x44, 4, "V")
147
- return nil if val.nil?
148
- info[:extra_bbd_start] = val
149
-
150
- # EXTRA BBD COUNT
151
- val = _getInfoFromFile(info[:fileh], 0x48, 4, "V")
152
- return nil if val.nil?
153
- info[:extra_bbd_count] = val
154
-
155
- #GET BBD INFO
156
- info[:bbd_info] = _getBbdInfo(info)
157
-
158
- # GET ROOT PPS
159
- root = _getNthPps(0, info, nil)
160
- info[:sb_start] = root.start_block
161
- info[:sb_size] = root.size
162
- info
163
- end
164
- private :_getHeaderInfo
165
-
166
- def _getInfoFromFile(io, pos, len, fmt)
167
- io.seek(pos, 0)
168
- str = io.read(len)
169
- if str.bytesize != len
170
- nil
171
- else
172
- str.unpack(fmt)[0]
173
- end
174
- end
175
- private :_getInfoFromFile
176
-
177
- def _getBbdInfo(info)
178
- bdlist = []
179
- iBdbCnt = info[:bdb_count]
180
- i1stCnt = (info[:big_block_size] - 0x4C) / LONG_INT_SIZE
181
- iBdlCnt = info[:big_block_size] / LONG_INT_SIZE - 1
182
-
183
- #1. 1st BDlist
184
- info[:fileh].seek(0x4C, 0)
185
- iGetCnt = iBdbCnt < i1stCnt ? iBdbCnt : i1stCnt
186
- str = info[:fileh].read(LONG_INT_SIZE * iGetCnt)
187
- bdlist += str.unpack("V#{iGetCnt}")
188
- iBdbCnt -= iGetCnt
189
-
190
- #2. Extra BDList
191
- iBlock = info[:extra_bbd_start]
192
- while iBdbCnt> 0 && _isNormalBlock(iBlock)
193
- _setFilePos(iBlock, 0, info)
194
- iGetCnt = iBdbCnt < iBdlCnt ? iBdbCnt : iBdlCnt
195
- str = info[:fileh].read(LONG_INT_SIZE * iGetCnt)
196
- bdlist += str.unpack("V#{iGetCnt}")
197
- iBdbCnt -= iGetCnt
198
- str = info[:fileh].read(LONG_INT_SIZE)
199
- iBlock = str.unpack("V")
200
- end
201
-
202
- #3.Get BDs
203
- hBd = Hash.new
204
- iBlkNo = 0
205
- iBdCnt = info[:big_block_size] / LONG_INT_SIZE
206
- bdlist.each do |iBdL|
207
- _setFilePos(iBdL, 0, info)
208
- str = info[:fileh].read(info[:big_block_size])
209
- arr = str.unpack("V#{iBdCnt}")
210
- (0...iBdCnt).each do |i|
211
- hBd[iBlkNo] = arr[i] if arr[i] != iBlkNo + 1
212
- iBlkNo += 1
213
- end
214
- end
215
- hBd
216
- end
217
- private :_getBbdInfo
218
-
219
- def _getNthPps(pos, info, data)
220
- ppsstart = info[:root_start]
221
-
222
- basecnt = info[:big_block_size] / PPS_SIZE
223
- ppsblock = pos / basecnt
224
- ppspos = pos % basecnt
225
-
226
- block = _getNthBlockNo(ppsstart, ppsblock, info)
227
- return nil if block.nil?
228
-
229
- _setFilePos(block, PPS_SIZE * ppspos, info)
230
- str = info[:fileh].read(PPS_SIZE)
231
- return nil if str.nil? || str == ''
232
- nmsize = str[0x40, 2].unpack('v')[0]
233
- nmsize -= 2 if nmsize > 2
234
- nm = str[0, nmsize]
235
- type = str[0x42, 2].unpack('C')[0]
236
- ppsprev = str[0x44, LONG_INT_SIZE].unpack('V')[0]
237
- ppsnext = str[0x48, LONG_INT_SIZE].unpack('V')[0]
238
- dirpps = str[0x4C, LONG_INT_SIZE].unpack('V')[0]
239
- time1st =
240
- (type == PPS_TYPE_ROOT || type == PPS_TYPE_DIR) ? oleData2Local(str[0x64, 8]) : nil
241
- time2nd =
242
- (type == PPS_TYPE_ROOT || type == PPS_TYPE_DIR) ? oleData2Local(str[0x6C, 8]) : nil
243
- start, size = str[0x74, 8].unpack('VV')
244
- if data
245
- sdata = _getData(type, start, size, info)
246
- OLEStorageLitePPS.new(pos, nm, type, ppsprev, ppsnext, dirpps,
247
- time1st, time2nd, start, size, sdata, nil)
248
- else
249
- OLEStorageLitePPS.new(pos, nm, type, ppsprev, ppsnext, dirpps,
250
- time1st, time2nd, start, size, nil, nil)
251
- end
252
- end
253
- private :_getNthPps
254
-
255
- def _setFilePos(iBlock, iPos, info)
256
- info[:fileh].seek((iBlock + 1) * info[:big_block_size] + iPos, 0)
257
- end
258
- private :_setFilePos
259
-
260
- def _getNthBlockNo(stblock, nth, info)
261
- inext = stblock
262
- (0...nth).each do |i|
263
- sv = inext
264
- inext = _getNextBlockNo(sv, info)
265
- return nil unless _isNormalBlock(inext)
266
- end
267
- inext
268
- end
269
- private :_getNthBlockNo
270
-
271
- def _getData(iType, iBlock, iSize, info)
272
- if iType == PPS_TYPE_FILE
273
- if iSize < DATA_SIZE_SMALL
274
- return _getSmallData(iBlock, iSize, info)
275
- else
276
- return _getBigData(iBlock, iSize, info)
277
- end
278
- elsif iType == PPS_TYPE_ROOT # Root
279
- return _getBigData(iBlock, iSize, info)
280
- elsif iType == PPS_TYPE_DIR # Directory
281
- return nil
282
- end
283
- end
284
- private :_getData
285
-
286
- def _getBigData(iBlock, iSize, info)
287
- return '' unless _isNormalBlock(iBlock)
288
- iRest = iSize
289
- sRes = ''
290
- aKeys = info[:bbd_info].keys.sort
291
-
292
- while iRest > 0
293
- aRes = aKeys.select { |key| key >= iBlock }
294
- iNKey = aRes[0]
295
- i = iNKey - iBlock
296
- iNext = info[:bbd_info][iNKey]
297
- _setFilePos(iBlock, 0, info)
298
- iGetSize = info[:big_block_size] * (i + 1)
299
- iGetSize = iRest if iRest < iGetSize
300
- sRes += info[:fileh].read(iGetSize)
301
- iRest -= iGetSize
302
- iBlock = iNext
303
- end
304
- sRes
305
- end
306
- private :_getBigData
307
-
308
- def _getNextBlockNo(iBlockNo, info)
309
- iRes = info[:bbd_info][iBlockNo]
310
- iRes ? iRes : iBlockNo + 1
311
- end
312
- private :_getNextBlockNo
313
-
314
- def _isNormalBlock(iBlock)
315
- iBlock < 0xFFFFFFFC ? 1 : nil
316
- end
317
- private :_isNormalBlock
318
-
319
- def _getSmallData(iSmBlock, iSize, info)
320
- iRest = iSize
321
- sRes = ''
322
- while iRest > 0
323
- _setFilePosSmall(iSmBlock, info)
324
- sRes += info[:fileh].read(
325
- iRest >= info[:small_block_size] ? info[:small_block_size] : iRest)
326
- iRest -= info[:small_block_size]
327
- iSmBlock = _getNextSmallBlockNo(iSmBlock, info)
328
- end
329
- sRes
330
- end
331
- private :_getSmallData
332
-
333
- def _setFilePosSmall(iSmBlock, info)
334
- iSmStart = info[:sb_start]
335
- iBaseCnt = info[:big_block_size] / info[:small_block_size]
336
- iNth = iSmBlock / iBaseCnt
337
- iPos = iSmBlock % iBaseCnt
338
-
339
- iBlk = _getNthBlockNo(iSmStart, iNth, info)
340
- _setFilePos(iBlk, iPos * info[:small_block_size], info)
341
- end
342
- private :_setFilePosSmall
343
-
344
- def _getNextSmallBlockNo(iSmBlock, info)
345
- iBaseCnt = info[:big_block_size] / LONG_INT_SIZE
346
- iNth = iSmBlock / iBaseCnt
347
- iPos = iSmBlock % iBaseCnt
348
- iBlk = _getNthBlockNo(info[:sbd_start], iNth, info)
349
- _setFilePos(iBlk, iPos * LONG_INT_SIZE, info)
350
- info[:fileh].read(LONG_INT_SIZE).unpack('V')
351
- end
352
- private :_getNextSmallBlockNo
353
-
354
- def asc2ucs(str)
355
- str.split(//).join("\0") + "\0"
356
- end
357
-
358
- def ucs2asc(str)
359
- ary = str.unpack('v*').map { |s| [s].pack('c')}
360
- ary.join('')
361
- end
362
-
363
- #------------------------------------------------------------------------------
364
- # OLEDate2Local()
365
- #
366
- # Convert from a Window FILETIME structure to a localtime array. FILETIME is
367
- # a 64-bit value representing the number of 100-nanosecond intervals since
368
- # January 1 1601.
369
- #
370
- # We first convert the FILETIME to seconds and then subtract the difference
371
- # between the 1601 epoch and the 1970 Unix epoch.
372
- #
373
- def oleData2Local(oletime)
374
- # Unpack the FILETIME into high and low longs.
375
- lo, hi = oletime.unpack('V2')
376
-
377
- # Convert the longs to a double.
378
- nanoseconds = hi * 2 ** 32 + lo
379
-
380
- # Convert the 100 nanosecond units into seconds.
381
- time = nanoseconds / 1e7
382
-
383
- # Subtract the number of seconds between the 1601 and 1970 epochs.
384
- time -= 11644473600
385
-
386
- # Convert to a localtime (actually gmtime) structure.
387
- if time >= 1
388
- ltime = Time.at(time).getgm.to_a[0, 9]
389
- ltime[4] -= 1 # month
390
- ltime[5] -= 1900 # year
391
- ltime[7] -= 1 # past from 1, Jan
392
- ltime[8] = ltime[8] ? 1 : 0
393
- ltime
394
- else
395
- []
396
- end
397
- end
398
-
399
- #------------------------------------------------------------------------------
400
- # LocalDate2OLE()
401
- #
402
- # Convert from a a localtime array to a Window FILETIME structure. FILETIME is
403
- # a 64-bit value representing the number of 100-nanosecond intervals since
404
- # January 1 1601.
405
- #
406
- # We first convert the localtime (actually gmtime) to seconds and then add the
407
- # difference between the 1601 epoch and the 1970 Unix epoch. We convert that to
408
- # 100 nanosecond units, divide it into high and low longs and return it as a
409
- # packed 64bit structure.
410
- #
411
- def localDate2OLE(localtime)
412
- return "\x00" * 8 unless localtime
413
-
414
- # Convert from localtime (actually gmtime) to seconds.
415
- args = localtime.reverse
416
- args[0] += 1900 # year
417
- args[1] += 1 # month
418
- time = Time.gm(*args)
419
-
420
- # Add the number of seconds between the 1601 and 1970 epochs.
421
- time = time.to_i + 11644473600
422
-
423
- # The FILETIME seconds are in units of 100 nanoseconds.
424
- nanoseconds = time * 10000000
425
-
426
- # Pack the total nanoseconds into 64 bits...
427
- hi, lo = nanoseconds.divmod 1 << 32
428
-
429
- [lo, hi].pack("VV") # oletime
430
- end
431
- end
432
-
433
- class OLEStorageLitePPS < OLEStorageLite #:nodoc:
434
- attr_accessor :no, :name, :type, :prev_pps, :next_pps, :dir_pps
435
- attr_accessor :time_1st, :time_2nd, :start_block, :size, :data, :child
436
- attr_reader :pps_file
437
-
438
- def initialize(iNo, sNm, iType, iPrev, iNext, iDir,
439
- raTime1st, raTime2nd, iStart, iSize, sData, raChild)
440
- @no = iNo
441
- @name = sNm
442
- @type = iType
443
- @prev_pps = iPrev
444
- @next_pps = iNext
445
- @dir_pps = iDir
446
- @time_1st = raTime1st
447
- @time_2nd = raTime2nd
448
- @start_block = iStart
449
- @size = iSize
450
- @data = sData
451
- @child = raChild
452
- @pps_file = nil
453
- end
454
-
455
- def _datalen
456
- return 0 if @data.nil?
457
- if @pps_file
458
- return @pps_file.lstat.size
459
- else
460
- return @data.bytesize
461
- end
462
- end
463
- protected :_datalen
464
-
465
- def _makeSmallData(aList, rh_info)
466
- file = rh_info[:fileh]
467
- iSmBlk = 0
468
- sRes = ''
469
-
470
- aList.each do |pps|
471
- #1. Make SBD, small data string
472
- if pps.type == PPS_TYPE_FILE
473
- next if pps.size <= 0
474
- if pps.size < rh_info[:small_size]
475
- iSmbCnt = pps.size / rh_info[:small_block_size]
476
- iSmbCnt += 1 if pps.size % rh_info[:small_block_size] > 0
477
- #1.1 Add to SBD
478
- 0.upto(iSmbCnt-1-1) do |i|
479
- file.write([i + iSmBlk+1].pack("V"))
480
- end
481
- file.write([-2].pack("V"))
482
-
483
- #1.2 Add to Data String(this will be written for RootEntry)
484
- #Check for update
485
- if pps.pps_file
486
- pps.pps_file.seek(0) #To The Top
487
- while sBuff = pps.pps_file.read(4096)
488
- sRes << sBuff
489
- end
490
- else
491
- sRes << pps.data
492
- end
493
- if pps.size % rh_info[:small_block_size] > 0
494
- cnt = rh_info[:small_block_size] - (pps.size % rh_info[:small_block_size])
495
- sRes << "\0" * cnt
496
- end
497
- #1.3 Set for PPS
498
- pps.start_block = iSmBlk
499
- iSmBlk += iSmbCnt
500
- end
501
- end
502
- end
503
- iSbCnt = rh_info[:big_block_size] / LONG_INT_SIZE
504
- file.write([-1].pack("V") * (iSbCnt - (iSmBlk % iSbCnt))) if iSmBlk % iSbCnt > 0
505
- #2. Write SBD with adjusting length for block
506
- sRes
507
- end
508
- private :_makeSmallData
509
-
510
- def _savePpsWk(rh_info)
511
- #1. Write PPS
512
- file = rh_info[:fileh]
513
- file.write( [
514
- @name,
515
- ("\x00" * (64 - @name.bytesize)), #64
516
- [@name.bytesize + 2].pack("v"), #66
517
- [@type].pack("c"), #67
518
- [0x00].pack("c"), #UK #68
519
- [@prev_pps].pack("V"), #Prev #72
520
- [@next_pps].pack("V"), #Next #76
521
- [@dir_pps].pack("V"), #Dir #80
522
- "\x00\x09\x02\x00", #84
523
- "\x00\x00\x00\x00", #88
524
- "\xc0\x00\x00\x00", #92
525
- "\x00\x00\x00\x46", #96
526
- "\x00\x00\x00\x00", #100
527
- localDate2OLE(@time_1st), #108
528
- localDate2OLE(@time_2nd) #116
529
- ].collect { |d| d.force_encoding(Encoding::BINARY) }.join('')
530
- )
531
- if @start_block != 0
532
- file.write([@start_block].pack('V'))
533
- else
534
- file.write([0].pack('V'))
535
- end
536
- if @size != 0 #124
537
- file.write([@size].pack('V'))
538
- else
539
- file.write([0].pack('V'))
540
- end
541
- file.write([0].pack('V')) #128
542
- end
543
- protected :_savePpsWk
544
- end
545
-
546
- class OLEStorageLitePPSRoot < OLEStorageLitePPS #:nodoc:
547
- def initialize(raTime1st, raTime2nd, raChild)
548
- super(
549
- nil,
550
- asc2ucs('Root Entry'),
551
- PPS_TYPE_ROOT,
552
- nil,
553
- nil,
554
- nil,
555
- raTime1st,
556
- raTime2nd,
557
- nil,
558
- nil,
559
- nil,
560
- raChild)
561
- end
562
-
563
- def save(sFile, bNoAs = nil, rh_info = nil)
564
- #0.Initial Setting for saving
565
- rh_info = Hash.new unless rh_info
566
- if rh_info[:big_block_size]
567
- rh_info[:big_block_size] = 2 ** adjust2(rh_info[:big_block_size])
568
- else
569
- rh_info[:big_block_size] = 2 ** 9
570
- end
571
- if rh_info[:small_block_size]
572
- rh_info[:small_block_size] = 2 ** adjust2(rh_info[:small_block_size])
573
- else
574
- rh_info[:small_block_size] = 2 ** 6
575
- end
576
- rh_info[:small_size] = 0x1000
577
- rh_info[:pps_size] = 0x80
578
-
579
- close_file = true
580
-
581
- #1.Open File
582
- #1.1 sFile is Ref of scalar
583
- if sFile.respond_to?(:to_str)
584
- rh_info[:fileh] = open(sFile, "wb")
585
- else
586
- rh_info[:fileh] = sFile.binmode
587
- end
588
-
589
- iBlk = 0
590
- #1. Make an array of PPS (for Save)
591
- aList=[]
592
- if bNoAs
593
- _savePpsSetPnt2([self], aList, rh_info)
594
- else
595
- _savePpsSetPnt([self], aList, rh_info)
596
- end
597
- iSBDcnt, iBBcnt, iPPScnt = _calcSize(aList, rh_info)
598
-
599
- #2.Save Header
600
- _saveHeader(rh_info, iSBDcnt, iBBcnt, iPPScnt)
601
-
602
- #3.Make Small Data string (write SBD)
603
- # Small Datas become RootEntry Data
604
- @data = _makeSmallData(aList, rh_info)
605
-
606
- #4. Write BB
607
- iBBlk = iSBDcnt
608
- _saveBigData(iBBlk, aList, rh_info)
609
-
610
- #5. Write PPS
611
- _savePps(aList, rh_info)
612
-
613
- #6. Write BD and BDList and Adding Header informations
614
- _saveBbd(iSBDcnt, iBBcnt, iPPScnt, rh_info)
615
-
616
- #7.Close File
617
- rh_info[:fileh].close if close_file
618
- end
619
-
620
- def _calcSize(aList, rh_info)
621
- #0. Calculate Basic Setting
622
- iSBDcnt, iBBcnt, iPPScnt = [0,0,0]
623
- iSmallLen = 0
624
- iSBcnt = 0
625
- aList.each do |pps|
626
- if pps.type == PPS_TYPE_FILE
627
- pps.size = pps._datalen #Mod
628
- if pps.size < rh_info[:small_size]
629
- iSBcnt += pps.size / rh_info[:small_block_size]
630
- iSBcnt += 1 if pps.size % rh_info[:small_block_size] > 0
631
- else
632
- iBBcnt += pps.size / rh_info[:big_block_size]
633
- iBBcnt += 1 if pps.size % rh_info[:big_block_size] > 0
634
- end
635
- end
636
- end
637
- iSmallLen = iSBcnt * rh_info[:small_block_size]
638
- iSlCnt = rh_info[:big_block_size] / LONG_INT_SIZE
639
- iSBDcnt = iSBcnt / iSlCnt
640
- iSBDcnt += 1 if iSBcnt % iSlCnt > 0
641
- iBBcnt += iSmallLen / rh_info[:big_block_size]
642
- iBBcnt += 1 if iSmallLen % rh_info[:big_block_size] > 0
643
- iCnt = aList.size
644
- iBdCnt = rh_info[:big_block_size] / PPS_SIZE
645
- iPPScnt = iCnt / iBdCnt
646
- iPPScnt += 1 if iCnt % iBdCnt > 0
647
- [iSBDcnt, iBBcnt, iPPScnt]
648
- end
649
- private :_calcSize
650
-
651
- def _adjust2(i2)
652
- iWk = Math.log(i2)/Math.log(2)
653
- iWk > Integer(iWk) ? Integer(iWk) + 1 : iWk
654
- end
655
- private :_adjust2
656
-
657
- def _saveHeader(rh_info, iSBDcnt, iBBcnt, iPPScnt)
658
- file = rh_info[:fileh]
659
-
660
- #0. Calculate Basic Setting
661
- iBlCnt = rh_info[:big_block_size] / LONG_INT_SIZE
662
- i1stBdL = (rh_info[:big_block_size] - 0x4C) / LONG_INT_SIZE
663
- i1stBdMax = i1stBdL * iBlCnt - i1stBdL
664
- iBdExL = 0
665
- iAll = iBBcnt + iPPScnt + iSBDcnt
666
- iAllW = iAll
667
- iBdCntW = iAllW / iBlCnt
668
- iBdCntW += 1 if iAllW % iBlCnt > 0
669
- iBdCnt = 0
670
- #0.1 Calculate BD count
671
- iBlCnt -= 1 #the BlCnt is reduced in the count of the last sect is used for a pointer the next Bl
672
- iBBleftover = iAll - i1stBdMax
673
- if iAll >i1stBdMax
674
- iBdCnt, iBdExL, iBBleftover = calc_idbcnt_idbexl_ibbleftover(iBBleftover, iBlCnt, iBdCnt, iBdExL)
675
- end
676
- iBdCnt += i1stBdL
677
- #print "iBdCnt = iBdCnt \n"
678
-
679
- #1.Save Header
680
- file.write( [
681
- "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1",
682
- "\x00\x00\x00\x00" * 4,
683
- [0x3b].pack("v"),
684
- [0x03].pack("v"),
685
- [-2].pack("v"),
686
- [9].pack("v"),
687
- [6].pack("v"),
688
- [0].pack("v"),
689
- "\x00\x00\x00\x00" * 2,
690
- [iBdCnt].pack("V"),
691
- [iBBcnt+iSBDcnt].pack("V"), #ROOT START
692
- [0].pack("V"),
693
- [0x1000].pack("V"),
694
- [0].pack("V"), #Small Block Depot
695
- [1].pack("V")
696
- ].collect { |d| d.force_encoding(Encoding::BINARY) }.join('')
697
- )
698
- #2. Extra BDList Start, Count
699
- if iAll <= i1stBdMax
700
- file.write(
701
- [-2].pack("V") + #Extra BDList Start
702
- [0].pack("V") #Extra BDList Count
703
- )
704
- else
705
- file.write(
706
- [iAll + iBdCnt].pack("V") +
707
- [iBdExL].pack("V")
708
- )
709
- end
710
-
711
- #3. BDList
712
- cnt = i1stBdL
713
- cnt = iBdCnt if iBdCnt < i1stBdL
714
- 0.upto(cnt-1) do |i|
715
- file.write([iAll + i].pack("V"))
716
- end
717
- file.write([-1].pack("V") * (i1stBdL - cnt)) if cnt < i1stBdL
718
- end
719
- private :_saveHeader
720
-
721
- def _saveBigData(iStBlk, aList, rh_info)
722
- iRes = 0
723
- file = rh_info[:fileh]
724
-
725
- #1.Write Big (ge 0x1000) Data into Block
726
- aList.each do |pps|
727
- if pps.type != PPS_TYPE_DIR
728
- #print "PPS: pps DEF:", defined(pps->{Data}), "\n"
729
- pps.size = pps._datalen #Mod
730
- if (pps.size >= rh_info[:small_size]) ||
731
- ((pps.type == PPS_TYPE_ROOT) && !pps.data.nil?)
732
- #1.1 Write Data
733
- #Check for update
734
- if pps.pps_file
735
- iLen = 0
736
- pps.pps_file.seek(0, 0) #To The Top
737
- while sBuff = pps.pps_file.read(4096)
738
- iLen += sBuff.bytesize
739
- file.write(sBuff) #Check for update
740
- end
741
- else
742
- file.write(pps.data)
743
- end
744
- if pps.size % rh_info[:big_block_size] > 0
745
- file.write(
746
- "\x00" *
747
- (rh_info[:big_block_size] -
748
- (pps.size % rh_info[:big_block_size]))
749
- )
750
- end
751
- #1.2 Set For PPS
752
- pps.start_block = iStBlk
753
- iStBlk += pps.size / rh_info[:big_block_size]
754
- iStBlk += 1 if pps.size % rh_info[:big_block_size] > 0
755
- end
756
- end
757
- end
758
- end
759
-
760
- def _savePps(aList, rh_info)
761
- #0. Initial
762
- file = rh_info[:fileh]
763
- #2. Save PPS
764
- aList.each do |oItem|
765
- oItem._savePpsWk(rh_info)
766
- end
767
- #3. Adjust for Block
768
- iCnt = aList.size
769
- iBCnt = rh_info[:big_block_size] / rh_info[:pps_size]
770
- if iCnt % iBCnt > 0
771
- file.write("\x00" * ((iBCnt - (iCnt % iBCnt)) * rh_info[:pps_size]))
772
- end
773
- (iCnt / iBCnt) + ((iCnt % iBCnt) > 0 ? 1: 0)
774
- end
775
- private :_savePps
776
-
777
- def _savePpsSetPnt(pps_array, aList, rh_info)
778
- #1. make Array as Children-Relations
779
- #1.1 if No Children
780
- if pps_array.nil? || pps_array.size == 0
781
- return 0xFFFFFFFF
782
- #1.2 Just Only one
783
- elsif pps_array.size == 1
784
- aList << pps_array[0]
785
- pps_array[0].no = aList.size - 1
786
- pps_array[0].prev_pps = 0xFFFFFFFF
787
- pps_array[0].next_pps = 0xFFFFFFFF
788
- pps_array[0].dir_pps = _savePpsSetPnt(pps_array[0].child, aList, rh_info)
789
- return pps_array[0].no
790
- #1.3 Array
791
- else
792
- iCnt = pps_array.size
793
- #1.3.1 Define Center
794
- iPos = Integer(iCnt / 2.0) #$iCnt
795
-
796
- aList.push(pps_array[iPos])
797
- pps_array[iPos].no = aList.size - 1
798
-
799
- aWk = pps_array.dup
800
- #1.3.2 Devide a array into Previous,Next
801
- aPrev = aWk[0, iPos]
802
- aWk[0..iPos-1] = nil
803
- aNext = aWk[1, iCnt - iPos - 1]
804
- aWk[1..(1 + iCnt - iPos -1 -1)] = nil
805
- pps_array[iPos].prev_pps = _savePpsSetPnt(aPrev, aList, rh_info)
806
- pps_array[iPos].next_pps = _savePpsSetPnt(aNext, aList, rh_info)
807
- pps_array[iPos].dir_pps = _savePpsSetPnt(pps_array[iPos].child, aList, rh_info)
808
- return pps_array[iPos].no
809
- end
810
- end
811
- private :_savePpsSetPnt
812
-
813
- def _savePpsSetPnt2(pps_array, aList, rh_info)
814
- #1. make Array as Children-Relations
815
- #1.1 if No Children
816
- if pps_array.nil? || pps_array.size == 0
817
- return 0xFFFFFFFF
818
- #1.2 Just Only one
819
- elsif pps_array.size == 1
820
- aList << pps_array[0]
821
- pps_array[0].no = aList.size - 1
822
- pps_array[0].prev_pps = 0xFFFFFFFF
823
- pps_array[0].next_pps = 0xFFFFFFFF
824
- pps_array[0].dir_pps = _savePpsSetPnt2(pps_array[0].child, aList, rh_info)
825
- return pps_array[0].no
826
- #1.3 Array
827
- else
828
- iCnt = pps_array.size
829
- #1.3.1 Define Center
830
- iPos = 0 #int($iCnt/ 2); #$iCnt
831
-
832
- aWk = pps_array.dup
833
- aPrev = aWk[1, 1]
834
- aWk[1..1] = nil
835
- aNext = aWk[1..aWk.size] #, $iCnt - $iPos -1);
836
- pps_array[iPos].prev_pps = _savePpsSetPnt2(pps_array, aList, rh_info)
837
- aList.push(pps_array[iPos])
838
- pps_array[iPos].no = aList.size
839
-
840
- #1.3.2 Devide a array into Previous,Next
841
- pps_array[iPos].next_pps = _savePpsSetPnt2(aNext, aList, rh_info)
842
- pps_array[iPos].dir_pps = _savePpsSetPnt2(pps_array[iPos].child, aList, rh_info)
843
- return pps_array[iPos].no
844
- end
845
- end
846
- private :_savePpsSetPnt2
847
-
848
- def _saveBbd(iSbdSize, iBsize, iPpsCnt, rh_info)
849
- file = rh_info[:fileh]
850
- #0. Calculate Basic Setting
851
- iBbCnt = rh_info[:big_block_size] / LONG_INT_SIZE
852
- iBlCnt = iBbCnt - 1
853
- i1stBdL = (rh_info[:big_block_size] - 0x4C) / LONG_INT_SIZE
854
- i1stBdMax = i1stBdL * iBbCnt - i1stBdL
855
- iBdExL = 0
856
- iAll = iBsize + iPpsCnt + iSbdSize
857
- iAllW = iAll
858
- iBdCntW = iAllW / iBbCnt
859
- iBdCntW += 1 if iAllW % iBbCnt > 0
860
- iBdCnt = 0
861
- #0.1 Calculate BD count
862
- iBBleftover = iAll - i1stBdMax
863
- if iAll >i1stBdMax
864
- iBdCnt, iBdExL, iBBleftover = calc_idbcnt_idbexl_ibbleftover(iBBleftover, iBlCnt, iBdCnt, iBdExL)
865
- end
866
- iAllW += iBdExL
867
- iBdCnt += i1stBdL
868
- #print "iBdCnt = iBdCnt \n"
869
-
870
- #1. Making BD
871
- #1.1 Set for SBD
872
- if iSbdSize > 0
873
- 0.upto(iSbdSize-1-1) do |i|
874
- file.write([i + 1].pack('V'))
875
- end
876
- file.write([-2].pack('V'))
877
- end
878
- #1.2 Set for B
879
- 0.upto(iBsize-1-1) do |i|
880
- file.write([i + iSbdSize + 1].pack('V'))
881
- end
882
- file.write([-2].pack('V'))
883
-
884
- #1.3 Set for PPS
885
- 0.upto(iPpsCnt-1-1) do |i|
886
- file.write([i+iSbdSize+iBsize+1].pack("V"))
887
- end
888
- file.write([-2].pack('V'))
889
- #1.4 Set for BBD itself ( 0xFFFFFFFD : BBD)
890
- 0.upto(iBdCnt-1) do |i|
891
- file.write([0xFFFFFFFD].pack("V"))
892
- end
893
- #1.5 Set for ExtraBDList
894
- 0.upto(iBdExL-1) do |i|
895
- file.write([0xFFFFFFFC].pack("V"))
896
- end
897
- #1.6 Adjust for Block
898
- if (iAllW + iBdCnt) % iBbCnt > 0
899
- file.write([-1].pack('V') * (iBbCnt - ((iAllW + iBdCnt) % iBbCnt)))
900
- end
901
- #2.Extra BDList
902
- if iBdCnt > i1stBdL
903
- iN = 0
904
- iNb = 0
905
- i1stBdL.upto(iBdCnt-1) do |i|
906
- if iN >= iBbCnt-1
907
- iN = 0
908
- iNb += 1
909
- file.write([iAll+iBdCnt+iNb].pack("V"))
910
- end
911
- file.write([iBsize+iSbdSize+iPpsCnt+i].pack("V"))
912
- iN += 1
913
- end
914
- if (iBdCnt-i1stBdL) % (iBbCnt-1) > 0
915
- file.write([-1].pack("V") * ((iBbCnt-1) - ((iBdCnt-i1stBdL) % (iBbCnt-1))))
916
- end
917
- file.write([-2].pack('V'))
918
- end
919
- end
920
-
921
- def calc_idbcnt_idbexl_ibbleftover(iBBleftover, iBlCnt, iBdCnt, iBdExL)
922
- while true
923
- iBdCnt = iBBleftover / iBlCnt
924
- iBdCnt += 1 if iBBleftover % iBlCnt > 0
925
- iBdExL = iBdCnt / iBlCnt
926
- iBdExL += 1 if iBdCnt % iBlCnt > 0
927
- iBBleftover += iBdExL
928
- break if iBdCnt == iBBleftover / iBlCnt + (iBBleftover % iBlCnt > 0 ? 1 : 0)
929
- end
930
- [iBdCnt, iBdExL, iBBleftover]
931
- end
932
- private :calc_idbcnt_idbexl_ibbleftover
933
- end
934
-
935
- class OLEStorageLitePPSFile < OLEStorageLitePPS #:nodoc:
936
- def initialize(sNm, data = '')
937
- super(
938
- nil,
939
- sNm || '',
940
- PPS_TYPE_FILE,
941
- nil,
942
- nil,
943
- nil,
944
- nil,
945
- nil,
946
- nil,
947
- nil,
948
- data || '',
949
- nil
950
- )
951
- end
952
-
953
- def set_file(sFile = '')
954
- if sFile.nil? or sFile == ''
955
- @pps_file = Tempfile.new('OLEStorageLitePPSFile')
956
- elsif sFile.respond_to?(:write)
957
- @pps_file = sFile
958
- elsif sFile.respond_to?(:to_str)
959
- #File Name
960
- @pps_file = open(sFile, "r+")
961
- return nil unless @pps_file
962
- else
963
- return nil
964
- end
965
- @pps_file.seek(0, IO::SEEK_END)
966
- @pps_file.binmode
967
- end
968
-
969
- def append (data)
970
- return if data.nil?
971
- if @pps_file
972
- @pps_file << data
973
- @pps_file.flush
974
- else
975
- @data << data
976
- end
977
- end
978
- end
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # OLE::Storage_Lite
4
+ # by Kawai, Takanori (Hippo2000) 2000.11.4, 8, 14
5
+ # This Program is Still ALPHA version.
6
+ #//////////////////////////////////////////////////////////////////////////////
7
+ #
8
+ # converted from CPAN's OLE::Storage_Lite.
9
+ # converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
10
+ #
11
+
12
+ require 'tempfile'
13
+ require 'stringio'
14
+
15
+ class OLEStorageLite #:nodoc:
16
+ PPS_TYPE_ROOT = 5
17
+ PPS_TYPE_DIR = 1
18
+ PPS_TYPE_FILE = 2
19
+ DATA_SIZE_SMALL = 0x1000
20
+ LONG_INT_SIZE = 4
21
+ PPS_SIZE = 0x80
22
+
23
+ attr_reader :file
24
+
25
+ def initialize(file = nil)
26
+ @file = file
27
+ end
28
+
29
+ def getPpsTree(data)
30
+ info = _initParse(file)
31
+ info ? _getPpsTree(0, info, data) : nil
32
+ end
33
+
34
+ def getPpsSearch(name, data, icase)
35
+ info = _initParse(file)
36
+ info ? _getPpsSearch(0, info, name, data, icase) : nil
37
+ end
38
+
39
+ def getNthPps(no, data)
40
+ info = _initParse(file)
41
+ info ? _getNthPps(no, info, data) : nil
42
+ end
43
+
44
+ def _initParse(file)
45
+ io = file.respond_to?(:to_str) ? open(file, 'rb') : file
46
+ _getHeaderInfo(io)
47
+ end
48
+ private :_initParse
49
+
50
+ def _getPpsTree(no, info, data, done)
51
+ if done
52
+ return [] if done.include?(no)
53
+ else
54
+ done = []
55
+ end
56
+ done << no
57
+
58
+ rootblock = info[:root_start]
59
+
60
+ #1. Get Information about itself
61
+ pps = _getNthPps(no, info, data)
62
+
63
+ #2. Child
64
+ if pps.dir_pps != 0xFFFFFFFF
65
+ pps.child = _getPpsTree(pps.dir_pps, info, data, done)
66
+ else
67
+ pps.child = nil
68
+ end
69
+
70
+ #3. Previous,Next PPSs
71
+ list = []
72
+ list << _getPpsTree(pps.prev_pps, info, data, done) if pps.prev_pps != 0xFFFFFFFF
73
+ list << pps
74
+ list << _getPpsTree(pps.next_pps, info, data, done) if pps.next_pps != 0xFFFFFFFF
75
+ end
76
+ private :_getPpsTree
77
+
78
+ def _getPpsSearch(no, info, name, data, icase, done = nil)
79
+ rootblock = info[:root_start]
80
+ #1. Check it self
81
+ if done
82
+ return [] if done.include?(no)
83
+ else
84
+ done = []
85
+ end
86
+ done << no
87
+ pps = _getNthPps(no, info, nil)
88
+
89
+ re = Regexp.new("^\Q#{pps.name}\E$", Regexp::IGNORECASE)
90
+ if (icase && !name.select { |v| v =~ re }.empty?) || name.include?(pps.name)
91
+ pps = _getNthPps(no, info, data) if data
92
+ res = [pps]
93
+ else
94
+ res = []
95
+ end
96
+
97
+ #2. Check Child, Previous, Next PPSs
98
+ res +=
99
+ _getPpsSearch(pps.dir_pps, info, name, data, icase, done) if pps.dir_pps != 0xFFFFFFFF
100
+ res +=
101
+ _getPpsSearch(pps.prev_pps, info, name, data, icase, done) if pps.prev_pps != 0xFFFFFFFF
102
+ res +=
103
+ _getPpsSearch(pps.next_pps, info, name, data, icase, done) if pps.next_pps != 0xFFFFFFFF
104
+ res
105
+ end
106
+ private :_getPpsSearch
107
+
108
+ def _getHeaderInfo(io)
109
+ info = { :fileh => io }
110
+
111
+ #0. Check ID
112
+ info[:fileh].seek(0, 0)
113
+ return nil unless info[:fileh].read(8) == "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
114
+
115
+ # BIG BLOCK SIZE
116
+ val = _getInfoFromFile(info[:fileh], 0x1E, 2, "v")
117
+ return nil if val.nil?
118
+ info[:big_block_size] = 2 ** val
119
+
120
+ # SMALL BLOCK SIZE
121
+ val = _getInfoFromFile(info[:fileh], 0x20, 2, "v")
122
+ return nil if val.nil?
123
+ info[:small_block_size] = 2 ** val
124
+
125
+ # BDB Count
126
+ val = _getInfoFromFile(info[:fileh], 0x2C, 4, "V")
127
+ return nil if val.nil?
128
+ info[:bdb_count] = val
129
+
130
+ # START BLOCK
131
+ val = _getInfoFromFile(info[:fileh], 0x30, 4, "V")
132
+ return nil if val.nil?
133
+ info[:root_start] = val
134
+
135
+ # SMALL BD START
136
+ val = _getInfoFromFile(info[:fileh], 0x3C, 4, "V")
137
+ return nil if val.nil?
138
+ info[:sbd_start] = val
139
+
140
+ # SMALL BD COUNT
141
+ val = _getInfoFromFile(info[:fileh], 0x40, 4, "V")
142
+ return nil if val.nil?
143
+ info[:sbd_count] = val
144
+
145
+ # EXTRA BBD START
146
+ val = _getInfoFromFile(info[:fileh], 0x44, 4, "V")
147
+ return nil if val.nil?
148
+ info[:extra_bbd_start] = val
149
+
150
+ # EXTRA BBD COUNT
151
+ val = _getInfoFromFile(info[:fileh], 0x48, 4, "V")
152
+ return nil if val.nil?
153
+ info[:extra_bbd_count] = val
154
+
155
+ #GET BBD INFO
156
+ info[:bbd_info] = _getBbdInfo(info)
157
+
158
+ # GET ROOT PPS
159
+ root = _getNthPps(0, info, nil)
160
+ info[:sb_start] = root.start_block
161
+ info[:sb_size] = root.size
162
+ info
163
+ end
164
+ private :_getHeaderInfo
165
+
166
+ def _getInfoFromFile(io, pos, len, fmt)
167
+ io.seek(pos, 0)
168
+ str = io.read(len)
169
+ if str.bytesize != len
170
+ nil
171
+ else
172
+ str.unpack(fmt)[0]
173
+ end
174
+ end
175
+ private :_getInfoFromFile
176
+
177
+ def _getBbdInfo(info)
178
+ bdlist = []
179
+ iBdbCnt = info[:bdb_count]
180
+ i1stCnt = (info[:big_block_size] - 0x4C) / LONG_INT_SIZE
181
+ iBdlCnt = info[:big_block_size] / LONG_INT_SIZE - 1
182
+
183
+ #1. 1st BDlist
184
+ info[:fileh].seek(0x4C, 0)
185
+ iGetCnt = iBdbCnt < i1stCnt ? iBdbCnt : i1stCnt
186
+ str = info[:fileh].read(LONG_INT_SIZE * iGetCnt)
187
+ bdlist += str.unpack("V#{iGetCnt}")
188
+ iBdbCnt -= iGetCnt
189
+
190
+ #2. Extra BDList
191
+ iBlock = info[:extra_bbd_start]
192
+ while iBdbCnt> 0 && _isNormalBlock(iBlock)
193
+ _setFilePos(iBlock, 0, info)
194
+ iGetCnt = iBdbCnt < iBdlCnt ? iBdbCnt : iBdlCnt
195
+ str = info[:fileh].read(LONG_INT_SIZE * iGetCnt)
196
+ bdlist += str.unpack("V#{iGetCnt}")
197
+ iBdbCnt -= iGetCnt
198
+ str = info[:fileh].read(LONG_INT_SIZE)
199
+ iBlock = str.unpack("V")
200
+ end
201
+
202
+ #3.Get BDs
203
+ hBd = Hash.new
204
+ iBlkNo = 0
205
+ iBdCnt = info[:big_block_size] / LONG_INT_SIZE
206
+ bdlist.each do |iBdL|
207
+ _setFilePos(iBdL, 0, info)
208
+ str = info[:fileh].read(info[:big_block_size])
209
+ arr = str.unpack("V#{iBdCnt}")
210
+ (0...iBdCnt).each do |i|
211
+ hBd[iBlkNo] = arr[i] if arr[i] != iBlkNo + 1
212
+ iBlkNo += 1
213
+ end
214
+ end
215
+ hBd
216
+ end
217
+ private :_getBbdInfo
218
+
219
+ def _getNthPps(pos, info, data)
220
+ ppsstart = info[:root_start]
221
+
222
+ basecnt = info[:big_block_size] / PPS_SIZE
223
+ ppsblock = pos / basecnt
224
+ ppspos = pos % basecnt
225
+
226
+ block = _getNthBlockNo(ppsstart, ppsblock, info)
227
+ return nil if block.nil?
228
+
229
+ _setFilePos(block, PPS_SIZE * ppspos, info)
230
+ str = info[:fileh].read(PPS_SIZE)
231
+ return nil if str.nil? || str == ''
232
+ nmsize = str[0x40, 2].unpack('v')[0]
233
+ nmsize -= 2 if nmsize > 2
234
+ nm = str[0, nmsize]
235
+ type = str[0x42, 2].unpack('C')[0]
236
+ ppsprev = str[0x44, LONG_INT_SIZE].unpack('V')[0]
237
+ ppsnext = str[0x48, LONG_INT_SIZE].unpack('V')[0]
238
+ dirpps = str[0x4C, LONG_INT_SIZE].unpack('V')[0]
239
+ time1st =
240
+ (type == PPS_TYPE_ROOT || type == PPS_TYPE_DIR) ? oleData2Local(str[0x64, 8]) : nil
241
+ time2nd =
242
+ (type == PPS_TYPE_ROOT || type == PPS_TYPE_DIR) ? oleData2Local(str[0x6C, 8]) : nil
243
+ start, size = str[0x74, 8].unpack('VV')
244
+ if data
245
+ sdata = _getData(type, start, size, info)
246
+ OLEStorageLitePPS.new(pos, nm, type, ppsprev, ppsnext, dirpps,
247
+ time1st, time2nd, start, size, sdata, nil)
248
+ else
249
+ OLEStorageLitePPS.new(pos, nm, type, ppsprev, ppsnext, dirpps,
250
+ time1st, time2nd, start, size, nil, nil)
251
+ end
252
+ end
253
+ private :_getNthPps
254
+
255
+ def _setFilePos(iBlock, iPos, info)
256
+ info[:fileh].seek((iBlock + 1) * info[:big_block_size] + iPos, 0)
257
+ end
258
+ private :_setFilePos
259
+
260
+ def _getNthBlockNo(stblock, nth, info)
261
+ inext = stblock
262
+ (0...nth).each do |i|
263
+ sv = inext
264
+ inext = _getNextBlockNo(sv, info)
265
+ return nil unless _isNormalBlock(inext)
266
+ end
267
+ inext
268
+ end
269
+ private :_getNthBlockNo
270
+
271
+ def _getData(iType, iBlock, iSize, info)
272
+ if iType == PPS_TYPE_FILE
273
+ if iSize < DATA_SIZE_SMALL
274
+ return _getSmallData(iBlock, iSize, info)
275
+ else
276
+ return _getBigData(iBlock, iSize, info)
277
+ end
278
+ elsif iType == PPS_TYPE_ROOT # Root
279
+ return _getBigData(iBlock, iSize, info)
280
+ elsif iType == PPS_TYPE_DIR # Directory
281
+ return nil
282
+ end
283
+ end
284
+ private :_getData
285
+
286
+ def _getBigData(iBlock, iSize, info)
287
+ return '' unless _isNormalBlock(iBlock)
288
+ iRest = iSize
289
+ sRes = ''
290
+ aKeys = info[:bbd_info].keys.sort
291
+
292
+ while iRest > 0
293
+ aRes = aKeys.select { |key| key >= iBlock }
294
+ iNKey = aRes[0]
295
+ i = iNKey - iBlock
296
+ iNext = info[:bbd_info][iNKey]
297
+ _setFilePos(iBlock, 0, info)
298
+ iGetSize = info[:big_block_size] * (i + 1)
299
+ iGetSize = iRest if iRest < iGetSize
300
+ sRes += info[:fileh].read(iGetSize)
301
+ iRest -= iGetSize
302
+ iBlock = iNext
303
+ end
304
+ sRes
305
+ end
306
+ private :_getBigData
307
+
308
+ def _getNextBlockNo(iBlockNo, info)
309
+ iRes = info[:bbd_info][iBlockNo]
310
+ iRes ? iRes : iBlockNo + 1
311
+ end
312
+ private :_getNextBlockNo
313
+
314
+ def _isNormalBlock(iBlock)
315
+ iBlock < 0xFFFFFFFC ? 1 : nil
316
+ end
317
+ private :_isNormalBlock
318
+
319
+ def _getSmallData(iSmBlock, iSize, info)
320
+ iRest = iSize
321
+ sRes = ''
322
+ while iRest > 0
323
+ _setFilePosSmall(iSmBlock, info)
324
+ sRes += info[:fileh].read(
325
+ iRest >= info[:small_block_size] ? info[:small_block_size] : iRest)
326
+ iRest -= info[:small_block_size]
327
+ iSmBlock = _getNextSmallBlockNo(iSmBlock, info)
328
+ end
329
+ sRes
330
+ end
331
+ private :_getSmallData
332
+
333
+ def _setFilePosSmall(iSmBlock, info)
334
+ iSmStart = info[:sb_start]
335
+ iBaseCnt = info[:big_block_size] / info[:small_block_size]
336
+ iNth = iSmBlock / iBaseCnt
337
+ iPos = iSmBlock % iBaseCnt
338
+
339
+ iBlk = _getNthBlockNo(iSmStart, iNth, info)
340
+ _setFilePos(iBlk, iPos * info[:small_block_size], info)
341
+ end
342
+ private :_setFilePosSmall
343
+
344
+ def _getNextSmallBlockNo(iSmBlock, info)
345
+ iBaseCnt = info[:big_block_size] / LONG_INT_SIZE
346
+ iNth = iSmBlock / iBaseCnt
347
+ iPos = iSmBlock % iBaseCnt
348
+ iBlk = _getNthBlockNo(info[:sbd_start], iNth, info)
349
+ _setFilePos(iBlk, iPos * LONG_INT_SIZE, info)
350
+ info[:fileh].read(LONG_INT_SIZE).unpack('V')
351
+ end
352
+ private :_getNextSmallBlockNo
353
+
354
+ def asc2ucs(str)
355
+ str.split(//).join("\0") + "\0"
356
+ end
357
+
358
+ def ucs2asc(str)
359
+ ary = str.unpack('v*').map { |s| [s].pack('c')}
360
+ ary.join('')
361
+ end
362
+
363
+ #------------------------------------------------------------------------------
364
+ # OLEDate2Local()
365
+ #
366
+ # Convert from a Window FILETIME structure to a localtime array. FILETIME is
367
+ # a 64-bit value representing the number of 100-nanosecond intervals since
368
+ # January 1 1601.
369
+ #
370
+ # We first convert the FILETIME to seconds and then subtract the difference
371
+ # between the 1601 epoch and the 1970 Unix epoch.
372
+ #
373
+ def oleData2Local(oletime)
374
+ # Unpack the FILETIME into high and low longs.
375
+ lo, hi = oletime.unpack('V2')
376
+
377
+ # Convert the longs to a double.
378
+ nanoseconds = hi * 2 ** 32 + lo
379
+
380
+ # Convert the 100 nanosecond units into seconds.
381
+ time = nanoseconds / 1e7
382
+
383
+ # Subtract the number of seconds between the 1601 and 1970 epochs.
384
+ time -= 11644473600
385
+
386
+ # Convert to a localtime (actually gmtime) structure.
387
+ if time >= 1
388
+ ltime = Time.at(time).getgm.to_a[0, 9]
389
+ ltime[4] -= 1 # month
390
+ ltime[5] -= 1900 # year
391
+ ltime[7] -= 1 # past from 1, Jan
392
+ ltime[8] = ltime[8] ? 1 : 0
393
+ ltime
394
+ else
395
+ []
396
+ end
397
+ end
398
+
399
+ #------------------------------------------------------------------------------
400
+ # LocalDate2OLE()
401
+ #
402
+ # Convert from a a localtime array to a Window FILETIME structure. FILETIME is
403
+ # a 64-bit value representing the number of 100-nanosecond intervals since
404
+ # January 1 1601.
405
+ #
406
+ # We first convert the localtime (actually gmtime) to seconds and then add the
407
+ # difference between the 1601 epoch and the 1970 Unix epoch. We convert that to
408
+ # 100 nanosecond units, divide it into high and low longs and return it as a
409
+ # packed 64bit structure.
410
+ #
411
+ def localDate2OLE(localtime)
412
+ return "\x00" * 8 unless localtime
413
+
414
+ # Convert from localtime (actually gmtime) to seconds.
415
+ args = localtime.reverse
416
+ args[0] += 1900 # year
417
+ args[1] += 1 # month
418
+ time = Time.gm(*args)
419
+
420
+ # Add the number of seconds between the 1601 and 1970 epochs.
421
+ time = time.to_i + 11644473600
422
+
423
+ # The FILETIME seconds are in units of 100 nanoseconds.
424
+ nanoseconds = time * 10000000
425
+
426
+ # Pack the total nanoseconds into 64 bits...
427
+ hi, lo = nanoseconds.divmod 1 << 32
428
+
429
+ [lo, hi].pack("VV") # oletime
430
+ end
431
+ end
432
+
433
+ class OLEStorageLitePPS < OLEStorageLite #:nodoc:
434
+ attr_accessor :no, :name, :type, :prev_pps, :next_pps, :dir_pps
435
+ attr_accessor :time_1st, :time_2nd, :start_block, :size, :data, :child
436
+ attr_reader :pps_file
437
+
438
+ def initialize(iNo, sNm, iType, iPrev, iNext, iDir,
439
+ raTime1st, raTime2nd, iStart, iSize, sData, raChild)
440
+ @no = iNo
441
+ @name = sNm
442
+ @type = iType
443
+ @prev_pps = iPrev
444
+ @next_pps = iNext
445
+ @dir_pps = iDir
446
+ @time_1st = raTime1st
447
+ @time_2nd = raTime2nd
448
+ @start_block = iStart
449
+ @size = iSize
450
+ @data = sData
451
+ @child = raChild
452
+ @pps_file = nil
453
+ end
454
+
455
+ def _datalen
456
+ return 0 if @data.nil?
457
+ if @pps_file
458
+ return @pps_file.lstat.size
459
+ else
460
+ return @data.bytesize
461
+ end
462
+ end
463
+ protected :_datalen
464
+
465
+ def _makeSmallData(aList, rh_info)
466
+ file = rh_info[:fileh]
467
+ iSmBlk = 0
468
+ sRes = ''
469
+
470
+ aList.each do |pps|
471
+ #1. Make SBD, small data string
472
+ if pps.type == PPS_TYPE_FILE
473
+ next if pps.size <= 0
474
+ if pps.size < rh_info[:small_size]
475
+ iSmbCnt = pps.size / rh_info[:small_block_size]
476
+ iSmbCnt += 1 if pps.size % rh_info[:small_block_size] > 0
477
+ #1.1 Add to SBD
478
+ 0.upto(iSmbCnt-1-1) do |i|
479
+ file.write([i + iSmBlk+1].pack("V"))
480
+ end
481
+ file.write([-2].pack("V"))
482
+
483
+ #1.2 Add to Data String(this will be written for RootEntry)
484
+ #Check for update
485
+ if pps.pps_file
486
+ pps.pps_file.seek(0) #To The Top
487
+ while sBuff = pps.pps_file.read(4096)
488
+ sRes << sBuff
489
+ end
490
+ else
491
+ sRes << pps.data
492
+ end
493
+ if pps.size % rh_info[:small_block_size] > 0
494
+ cnt = rh_info[:small_block_size] - (pps.size % rh_info[:small_block_size])
495
+ sRes << "\0" * cnt
496
+ end
497
+ #1.3 Set for PPS
498
+ pps.start_block = iSmBlk
499
+ iSmBlk += iSmbCnt
500
+ end
501
+ end
502
+ end
503
+ iSbCnt = rh_info[:big_block_size] / LONG_INT_SIZE
504
+ file.write([-1].pack("V") * (iSbCnt - (iSmBlk % iSbCnt))) if iSmBlk % iSbCnt > 0
505
+ #2. Write SBD with adjusting length for block
506
+ sRes
507
+ end
508
+ private :_makeSmallData
509
+
510
+ def _savePpsWk(rh_info)
511
+ #1. Write PPS
512
+ file = rh_info[:fileh]
513
+ data = [
514
+ @name,
515
+ ("\x00" * (64 - @name.bytesize)), #64
516
+ [@name.bytesize + 2].pack("v"), #66
517
+ [@type].pack("c"), #67
518
+ [0x00].pack("c"), #UK #68
519
+ [@prev_pps].pack("V"), #Prev #72
520
+ [@next_pps].pack("V"), #Next #76
521
+ [@dir_pps].pack("V"), #Dir #80
522
+ "\x00\x09\x02\x00", #84
523
+ "\x00\x00\x00\x00", #88
524
+ "\xc0\x00\x00\x00", #92
525
+ "\x00\x00\x00\x46", #96
526
+ "\x00\x00\x00\x00", #100
527
+ localDate2OLE(@time_1st), #108
528
+ localDate2OLE(@time_2nd) #116
529
+ ]
530
+ file.write(
531
+ ruby_18 { data.join('') } ||
532
+ ruby_19 { data.collect { |d| d.force_encoding(Encoding::BINARY) }.join('') }
533
+ )
534
+ if @start_block != 0
535
+ file.write([@start_block].pack('V'))
536
+ else
537
+ file.write([0].pack('V'))
538
+ end
539
+ if @size != 0 #124
540
+ file.write([@size].pack('V'))
541
+ else
542
+ file.write([0].pack('V'))
543
+ end
544
+ file.write([0].pack('V')) #128
545
+ end
546
+ protected :_savePpsWk
547
+ end
548
+
549
+ class OLEStorageLitePPSRoot < OLEStorageLitePPS #:nodoc:
550
+ def initialize(raTime1st, raTime2nd, raChild)
551
+ super(
552
+ nil,
553
+ asc2ucs('Root Entry'),
554
+ PPS_TYPE_ROOT,
555
+ nil,
556
+ nil,
557
+ nil,
558
+ raTime1st,
559
+ raTime2nd,
560
+ nil,
561
+ nil,
562
+ nil,
563
+ raChild)
564
+ end
565
+
566
+ def save(sFile, bNoAs = nil, rh_info = nil)
567
+ #0.Initial Setting for saving
568
+ rh_info = Hash.new unless rh_info
569
+ if rh_info[:big_block_size]
570
+ rh_info[:big_block_size] = 2 ** adjust2(rh_info[:big_block_size])
571
+ else
572
+ rh_info[:big_block_size] = 2 ** 9
573
+ end
574
+ if rh_info[:small_block_size]
575
+ rh_info[:small_block_size] = 2 ** adjust2(rh_info[:small_block_size])
576
+ else
577
+ rh_info[:small_block_size] = 2 ** 6
578
+ end
579
+ rh_info[:small_size] = 0x1000
580
+ rh_info[:pps_size] = 0x80
581
+
582
+ close_file = true
583
+
584
+ #1.Open File
585
+ #1.1 sFile is Ref of scalar
586
+ if sFile.respond_to?(:to_str)
587
+ rh_info[:fileh] = open(sFile, "wb")
588
+ else
589
+ rh_info[:fileh] = sFile.binmode
590
+ end
591
+
592
+ iBlk = 0
593
+ #1. Make an array of PPS (for Save)
594
+ aList=[]
595
+ if bNoAs
596
+ _savePpsSetPnt2([self], aList, rh_info)
597
+ else
598
+ _savePpsSetPnt([self], aList, rh_info)
599
+ end
600
+ iSBDcnt, iBBcnt, iPPScnt = _calcSize(aList, rh_info)
601
+
602
+ #2.Save Header
603
+ _saveHeader(rh_info, iSBDcnt, iBBcnt, iPPScnt)
604
+
605
+ #3.Make Small Data string (write SBD)
606
+ # Small Datas become RootEntry Data
607
+ @data = _makeSmallData(aList, rh_info)
608
+
609
+ #4. Write BB
610
+ iBBlk = iSBDcnt
611
+ _saveBigData(iBBlk, aList, rh_info)
612
+
613
+ #5. Write PPS
614
+ _savePps(aList, rh_info)
615
+
616
+ #6. Write BD and BDList and Adding Header informations
617
+ _saveBbd(iSBDcnt, iBBcnt, iPPScnt, rh_info)
618
+
619
+ #7.Close File
620
+ rh_info[:fileh].close if close_file
621
+ end
622
+
623
+ def _calcSize(aList, rh_info)
624
+ #0. Calculate Basic Setting
625
+ iSBDcnt, iBBcnt, iPPScnt = [0,0,0]
626
+ iSmallLen = 0
627
+ iSBcnt = 0
628
+ aList.each do |pps|
629
+ if pps.type == PPS_TYPE_FILE
630
+ pps.size = pps._datalen #Mod
631
+ if pps.size < rh_info[:small_size]
632
+ iSBcnt += pps.size / rh_info[:small_block_size]
633
+ iSBcnt += 1 if pps.size % rh_info[:small_block_size] > 0
634
+ else
635
+ iBBcnt += pps.size / rh_info[:big_block_size]
636
+ iBBcnt += 1 if pps.size % rh_info[:big_block_size] > 0
637
+ end
638
+ end
639
+ end
640
+ iSmallLen = iSBcnt * rh_info[:small_block_size]
641
+ iSlCnt = rh_info[:big_block_size] / LONG_INT_SIZE
642
+ iSBDcnt = iSBcnt / iSlCnt
643
+ iSBDcnt += 1 if iSBcnt % iSlCnt > 0
644
+ iBBcnt += iSmallLen / rh_info[:big_block_size]
645
+ iBBcnt += 1 if iSmallLen % rh_info[:big_block_size] > 0
646
+ iCnt = aList.size
647
+ iBdCnt = rh_info[:big_block_size] / PPS_SIZE
648
+ iPPScnt = iCnt / iBdCnt
649
+ iPPScnt += 1 if iCnt % iBdCnt > 0
650
+ [iSBDcnt, iBBcnt, iPPScnt]
651
+ end
652
+ private :_calcSize
653
+
654
+ def _adjust2(i2)
655
+ iWk = Math.log(i2)/Math.log(2)
656
+ iWk > Integer(iWk) ? Integer(iWk) + 1 : iWk
657
+ end
658
+ private :_adjust2
659
+
660
+ def _saveHeader(rh_info, iSBDcnt, iBBcnt, iPPScnt)
661
+ file = rh_info[:fileh]
662
+
663
+ #0. Calculate Basic Setting
664
+ iBlCnt = rh_info[:big_block_size] / LONG_INT_SIZE
665
+ i1stBdL = (rh_info[:big_block_size] - 0x4C) / LONG_INT_SIZE
666
+ i1stBdMax = i1stBdL * iBlCnt - i1stBdL
667
+ iBdExL = 0
668
+ iAll = iBBcnt + iPPScnt + iSBDcnt
669
+ iAllW = iAll
670
+ iBdCntW = iAllW / iBlCnt
671
+ iBdCntW += 1 if iAllW % iBlCnt > 0
672
+ iBdCnt = 0
673
+ #0.1 Calculate BD count
674
+ iBlCnt -= 1 #the BlCnt is reduced in the count of the last sect is used for a pointer the next Bl
675
+ iBBleftover = iAll - i1stBdMax
676
+ if iAll >i1stBdMax
677
+ iBdCnt, iBdExL, iBBleftover = calc_idbcnt_idbexl_ibbleftover(iBBleftover, iBlCnt, iBdCnt, iBdExL)
678
+ end
679
+ iBdCnt += i1stBdL
680
+ #print "iBdCnt = iBdCnt \n"
681
+
682
+ #1.Save Header
683
+ data = [
684
+ "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1",
685
+ "\x00\x00\x00\x00" * 4,
686
+ [0x3b].pack("v"),
687
+ [0x03].pack("v"),
688
+ [-2].pack("v"),
689
+ [9].pack("v"),
690
+ [6].pack("v"),
691
+ [0].pack("v"),
692
+ "\x00\x00\x00\x00" * 2,
693
+ [iBdCnt].pack("V"),
694
+ [iBBcnt+iSBDcnt].pack("V"), #ROOT START
695
+ [0].pack("V"),
696
+ [0x1000].pack("V"),
697
+ [0].pack("V"), #Small Block Depot
698
+ [1].pack("V")
699
+ ]
700
+ file.write(
701
+ ruby_18 { data.join('') } ||
702
+ ruby_19 { data.collect { |d| d.force_encoding(Encoding::BINARY) }.join('') }
703
+ )
704
+ #2. Extra BDList Start, Count
705
+ if iAll <= i1stBdMax
706
+ file.write(
707
+ [-2].pack("V") + #Extra BDList Start
708
+ [0].pack("V") #Extra BDList Count
709
+ )
710
+ else
711
+ file.write(
712
+ [iAll + iBdCnt].pack("V") +
713
+ [iBdExL].pack("V")
714
+ )
715
+ end
716
+
717
+ #3. BDList
718
+ cnt = i1stBdL
719
+ cnt = iBdCnt if iBdCnt < i1stBdL
720
+ 0.upto(cnt-1) do |i|
721
+ file.write([iAll + i].pack("V"))
722
+ end
723
+ file.write([-1].pack("V") * (i1stBdL - cnt)) if cnt < i1stBdL
724
+ end
725
+ private :_saveHeader
726
+
727
+ def _saveBigData(iStBlk, aList, rh_info)
728
+ iRes = 0
729
+ file = rh_info[:fileh]
730
+
731
+ #1.Write Big (ge 0x1000) Data into Block
732
+ aList.each do |pps|
733
+ if pps.type != PPS_TYPE_DIR
734
+ #print "PPS: pps DEF:", defined(pps->{Data}), "\n"
735
+ pps.size = pps._datalen #Mod
736
+ if (pps.size >= rh_info[:small_size]) ||
737
+ ((pps.type == PPS_TYPE_ROOT) && !pps.data.nil?)
738
+ #1.1 Write Data
739
+ #Check for update
740
+ if pps.pps_file
741
+ iLen = 0
742
+ pps.pps_file.seek(0, 0) #To The Top
743
+ while sBuff = pps.pps_file.read(4096)
744
+ iLen += sBuff.bytesize
745
+ file.write(sBuff) #Check for update
746
+ end
747
+ else
748
+ file.write(pps.data)
749
+ end
750
+ if pps.size % rh_info[:big_block_size] > 0
751
+ file.write(
752
+ "\x00" *
753
+ (rh_info[:big_block_size] -
754
+ (pps.size % rh_info[:big_block_size]))
755
+ )
756
+ end
757
+ #1.2 Set For PPS
758
+ pps.start_block = iStBlk
759
+ iStBlk += pps.size / rh_info[:big_block_size]
760
+ iStBlk += 1 if pps.size % rh_info[:big_block_size] > 0
761
+ end
762
+ end
763
+ end
764
+ end
765
+
766
+ def _savePps(aList, rh_info)
767
+ #0. Initial
768
+ file = rh_info[:fileh]
769
+ #2. Save PPS
770
+ aList.each do |oItem|
771
+ oItem._savePpsWk(rh_info)
772
+ end
773
+ #3. Adjust for Block
774
+ iCnt = aList.size
775
+ iBCnt = rh_info[:big_block_size] / rh_info[:pps_size]
776
+ if iCnt % iBCnt > 0
777
+ file.write("\x00" * ((iBCnt - (iCnt % iBCnt)) * rh_info[:pps_size]))
778
+ end
779
+ (iCnt / iBCnt) + ((iCnt % iBCnt) > 0 ? 1: 0)
780
+ end
781
+ private :_savePps
782
+
783
+ def _savePpsSetPnt(pps_array, aList, rh_info)
784
+ #1. make Array as Children-Relations
785
+ #1.1 if No Children
786
+ if pps_array.nil? || pps_array.size == 0
787
+ return 0xFFFFFFFF
788
+ #1.2 Just Only one
789
+ elsif pps_array.size == 1
790
+ aList << pps_array[0]
791
+ pps_array[0].no = aList.size - 1
792
+ pps_array[0].prev_pps = 0xFFFFFFFF
793
+ pps_array[0].next_pps = 0xFFFFFFFF
794
+ pps_array[0].dir_pps = _savePpsSetPnt(pps_array[0].child, aList, rh_info)
795
+ return pps_array[0].no
796
+ #1.3 Array
797
+ else
798
+ iCnt = pps_array.size
799
+ #1.3.1 Define Center
800
+ iPos = Integer(iCnt / 2.0) #$iCnt
801
+
802
+ aList.push(pps_array[iPos])
803
+ pps_array[iPos].no = aList.size - 1
804
+
805
+ aWk = pps_array.dup
806
+ #1.3.2 Devide a array into Previous,Next
807
+ aPrev = aWk[0, iPos]
808
+ aWk[0..iPos-1] = nil
809
+ aNext = aWk[1, iCnt - iPos - 1]
810
+ aWk[1..(1 + iCnt - iPos -1 -1)] = nil
811
+ pps_array[iPos].prev_pps = _savePpsSetPnt(aPrev, aList, rh_info)
812
+ pps_array[iPos].next_pps = _savePpsSetPnt(aNext, aList, rh_info)
813
+ pps_array[iPos].dir_pps = _savePpsSetPnt(pps_array[iPos].child, aList, rh_info)
814
+ return pps_array[iPos].no
815
+ end
816
+ end
817
+ private :_savePpsSetPnt
818
+
819
+ def _savePpsSetPnt2(pps_array, aList, rh_info)
820
+ #1. make Array as Children-Relations
821
+ #1.1 if No Children
822
+ if pps_array.nil? || pps_array.size == 0
823
+ return 0xFFFFFFFF
824
+ #1.2 Just Only one
825
+ elsif pps_array.size == 1
826
+ aList << pps_array[0]
827
+ pps_array[0].no = aList.size - 1
828
+ pps_array[0].prev_pps = 0xFFFFFFFF
829
+ pps_array[0].next_pps = 0xFFFFFFFF
830
+ pps_array[0].dir_pps = _savePpsSetPnt2(pps_array[0].child, aList, rh_info)
831
+ return pps_array[0].no
832
+ #1.3 Array
833
+ else
834
+ iCnt = pps_array.size
835
+ #1.3.1 Define Center
836
+ iPos = 0 #int($iCnt/ 2); #$iCnt
837
+
838
+ aWk = pps_array.dup
839
+ aPrev = aWk[1, 1]
840
+ aWk[1..1] = nil
841
+ aNext = aWk[1..aWk.size] #, $iCnt - $iPos -1);
842
+ pps_array[iPos].prev_pps = _savePpsSetPnt2(pps_array, aList, rh_info)
843
+ aList.push(pps_array[iPos])
844
+ pps_array[iPos].no = aList.size
845
+
846
+ #1.3.2 Devide a array into Previous,Next
847
+ pps_array[iPos].next_pps = _savePpsSetPnt2(aNext, aList, rh_info)
848
+ pps_array[iPos].dir_pps = _savePpsSetPnt2(pps_array[iPos].child, aList, rh_info)
849
+ return pps_array[iPos].no
850
+ end
851
+ end
852
+ private :_savePpsSetPnt2
853
+
854
+ def _saveBbd(iSbdSize, iBsize, iPpsCnt, rh_info)
855
+ file = rh_info[:fileh]
856
+ #0. Calculate Basic Setting
857
+ iBbCnt = rh_info[:big_block_size] / LONG_INT_SIZE
858
+ iBlCnt = iBbCnt - 1
859
+ i1stBdL = (rh_info[:big_block_size] - 0x4C) / LONG_INT_SIZE
860
+ i1stBdMax = i1stBdL * iBbCnt - i1stBdL
861
+ iBdExL = 0
862
+ iAll = iBsize + iPpsCnt + iSbdSize
863
+ iAllW = iAll
864
+ iBdCntW = iAllW / iBbCnt
865
+ iBdCntW += 1 if iAllW % iBbCnt > 0
866
+ iBdCnt = 0
867
+ #0.1 Calculate BD count
868
+ iBBleftover = iAll - i1stBdMax
869
+ if iAll >i1stBdMax
870
+ iBdCnt, iBdExL, iBBleftover = calc_idbcnt_idbexl_ibbleftover(iBBleftover, iBlCnt, iBdCnt, iBdExL)
871
+ end
872
+ iAllW += iBdExL
873
+ iBdCnt += i1stBdL
874
+ #print "iBdCnt = iBdCnt \n"
875
+
876
+ #1. Making BD
877
+ #1.1 Set for SBD
878
+ if iSbdSize > 0
879
+ 0.upto(iSbdSize-1-1) do |i|
880
+ file.write([i + 1].pack('V'))
881
+ end
882
+ file.write([-2].pack('V'))
883
+ end
884
+ #1.2 Set for B
885
+ 0.upto(iBsize-1-1) do |i|
886
+ file.write([i + iSbdSize + 1].pack('V'))
887
+ end
888
+ file.write([-2].pack('V'))
889
+
890
+ #1.3 Set for PPS
891
+ 0.upto(iPpsCnt-1-1) do |i|
892
+ file.write([i+iSbdSize+iBsize+1].pack("V"))
893
+ end
894
+ file.write([-2].pack('V'))
895
+ #1.4 Set for BBD itself ( 0xFFFFFFFD : BBD)
896
+ 0.upto(iBdCnt-1) do |i|
897
+ file.write([0xFFFFFFFD].pack("V"))
898
+ end
899
+ #1.5 Set for ExtraBDList
900
+ 0.upto(iBdExL-1) do |i|
901
+ file.write([0xFFFFFFFC].pack("V"))
902
+ end
903
+ #1.6 Adjust for Block
904
+ if (iAllW + iBdCnt) % iBbCnt > 0
905
+ file.write([-1].pack('V') * (iBbCnt - ((iAllW + iBdCnt) % iBbCnt)))
906
+ end
907
+ #2.Extra BDList
908
+ if iBdCnt > i1stBdL
909
+ iN = 0
910
+ iNb = 0
911
+ i1stBdL.upto(iBdCnt-1) do |i|
912
+ if iN >= iBbCnt-1
913
+ iN = 0
914
+ iNb += 1
915
+ file.write([iAll+iBdCnt+iNb].pack("V"))
916
+ end
917
+ file.write([iBsize+iSbdSize+iPpsCnt+i].pack("V"))
918
+ iN += 1
919
+ end
920
+ if (iBdCnt-i1stBdL) % (iBbCnt-1) > 0
921
+ file.write([-1].pack("V") * ((iBbCnt-1) - ((iBdCnt-i1stBdL) % (iBbCnt-1))))
922
+ end
923
+ file.write([-2].pack('V'))
924
+ end
925
+ end
926
+
927
+ def calc_idbcnt_idbexl_ibbleftover(iBBleftover, iBlCnt, iBdCnt, iBdExL)
928
+ while true
929
+ iBdCnt = iBBleftover / iBlCnt
930
+ iBdCnt += 1 if iBBleftover % iBlCnt > 0
931
+ iBdExL = iBdCnt / iBlCnt
932
+ iBdExL += 1 if iBdCnt % iBlCnt > 0
933
+ iBBleftover += iBdExL
934
+ break if iBdCnt == iBBleftover / iBlCnt + (iBBleftover % iBlCnt > 0 ? 1 : 0)
935
+ end
936
+ [iBdCnt, iBdExL, iBBleftover]
937
+ end
938
+ private :calc_idbcnt_idbexl_ibbleftover
939
+ end
940
+
941
+ class OLEStorageLitePPSFile < OLEStorageLitePPS #:nodoc:
942
+ def initialize(sNm, data = '')
943
+ super(
944
+ nil,
945
+ sNm || '',
946
+ PPS_TYPE_FILE,
947
+ nil,
948
+ nil,
949
+ nil,
950
+ nil,
951
+ nil,
952
+ nil,
953
+ nil,
954
+ data || '',
955
+ nil
956
+ )
957
+ end
958
+
959
+ def set_file(sFile = '')
960
+ if sFile.nil? or sFile == ''
961
+ @pps_file = Tempfile.new('OLEStorageLitePPSFile')
962
+ elsif sFile.respond_to?(:write)
963
+ @pps_file = sFile
964
+ elsif sFile.respond_to?(:to_str)
965
+ #File Name
966
+ @pps_file = open(sFile, "r+")
967
+ return nil unless @pps_file
968
+ else
969
+ return nil
970
+ end
971
+ @pps_file.seek(0, IO::SEEK_END)
972
+ @pps_file.binmode
973
+ end
974
+
975
+ def append (data)
976
+ return if data.nil?
977
+ if @pps_file
978
+ @pps_file << data
979
+ @pps_file.flush
980
+ else
981
+ @data << data
982
+ end
983
+ end
984
+ end