writeexcel 0.3.5 → 0.4.0

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