File Coverage

blib/lib/OLE/Storage_Lite.pm
Criterion Covered Total %
statement 63 659 9.5
branch 3 266 1.1
condition 0 48 0.0
subroutine 17 57 29.8
pod 6 8 75.0
total 89 1038 8.5


line stmt bran cond sub pod time code
1             # OLE::Storage_Lite
2             # by Kawai, Takanori (Hippo2000) 2000.11.4, 8, 14
3             # This Program is Still ALPHA version.
4              
5 2     2   12101 use 5.006;
  2         8  
6              
7             #//////////////////////////////////////////////////////////////////////////////
8             # OLE::Storage_Lite::PPS Object
9             #//////////////////////////////////////////////////////////////////////////////
10             #==============================================================================
11             # OLE::Storage_Lite::PPS
12             #==============================================================================
13             package OLE::Storage_Lite::PPS;
14 2     2   13 use strict;
  2         4  
  2         2440  
15             our $VERSION = '0.24';
16              
17             #------------------------------------------------------------------------------
18             # new (OLE::Storage_Lite::PPS)
19             #------------------------------------------------------------------------------
20             sub new ($$$$$$$$$$;$$) {
21             #1. Constructor for General Usage
22 0     0   0 my($sClass, $iNo, $sNm, $iType, $iPrev, $iNext, $iDir,
23             $raTime1st, $raTime2nd, $iStart, $iSize, $sData, $raChild) = @_;
24              
25 0 0       0 if($iType == OLE::Storage_Lite::PpsType_File()) { #FILE
    0          
    0          
26 0         0 return OLE::Storage_Lite::PPS::File->_new
27             ($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd,
28             $iStart, $iSize, $sData, $raChild);
29             }
30             elsif($iType == OLE::Storage_Lite::PpsType_Dir()) { #DIRECTORY
31 0         0 return OLE::Storage_Lite::PPS::Dir->_new
32             ($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd,
33             $iStart, $iSize, $sData, $raChild);
34             }
35             elsif($iType == OLE::Storage_Lite::PpsType_Root()) { #ROOT
36 0         0 return OLE::Storage_Lite::PPS::Root->_new
37             ($iNo, $sNm, $iType, $iPrev, $iNext, $iDir, $raTime1st, $raTime2nd,
38             $iStart, $iSize, $sData, $raChild);
39             }
40             else {
41 0         0 die "Error PPS:$iType $sNm\n";
42             }
43             }
44             #------------------------------------------------------------------------------
45             # _new (OLE::Storage_Lite::PPS)
46             # for OLE::Storage_Lite
47             #------------------------------------------------------------------------------
48             sub _new ($$$$$$$$$$$;$$) {
49 0     0   0 my($sClass, $iNo, $sNm, $iType, $iPrev, $iNext, $iDir,
50             $raTime1st, $raTime2nd, $iStart, $iSize, $sData, $raChild) = @_;
51             #1. Constructor for OLE::Storage_Lite
52 0         0 my $oThis = {
53             No => $iNo,
54             Name => $sNm,
55             Type => $iType,
56             PrevPps => $iPrev,
57             NextPps => $iNext,
58             DirPps => $iDir,
59             Time1st => $raTime1st,
60             Time2nd => $raTime2nd,
61             StartBlock => $iStart,
62             Size => $iSize,
63             Data => $sData,
64             Child => $raChild,
65             };
66 0         0 bless $oThis, $sClass;
67 0         0 return $oThis;
68             }
69             #------------------------------------------------------------------------------
70             # _DataLen (OLE::Storage_Lite::PPS)
71             # Check for update
72             #------------------------------------------------------------------------------
73             sub _DataLen($) {
74 0     0   0 my($oSelf) =@_;
75 0 0       0 return 0 unless(defined($oSelf->{Data}));
76             return ($oSelf->{_PPS_FILE})?
77 0 0       0 ($oSelf->{_PPS_FILE}->stat())[7] : length($oSelf->{Data});
78             }
79             #------------------------------------------------------------------------------
80             # _makeSmallData (OLE::Storage_Lite::PPS)
81             #------------------------------------------------------------------------------
82             sub _makeSmallData($$$) {
83 0     0   0 my($oThis, $aList, $rhInfo) = @_;
84 0         0 my ($sRes);
85 0         0 my $FILE = $rhInfo->{_FILEH_};
86 0         0 my $iSmBlk = 0;
87              
88 0         0 foreach my $oPps (@$aList) {
89             #1. Make SBD, small data string
90 0 0       0 if($oPps->{Type}==OLE::Storage_Lite::PpsType_File()) {
91 0 0       0 next if($oPps->{Size}<=0);
92 0 0       0 if($oPps->{Size} < $rhInfo->{_SMALL_SIZE}) {
93             my $iSmbCnt = int($oPps->{Size} / $rhInfo->{_SMALL_BLOCK_SIZE})
94 0 0       0 + (($oPps->{Size} % $rhInfo->{_SMALL_BLOCK_SIZE})? 1: 0);
95             #1.1 Add to SBD
96 0         0 for (my $i = 0; $i<($iSmbCnt-1); $i++) {
97 0         0 print {$FILE} (pack("V", $i+$iSmBlk+1));
  0         0  
98             }
99 0         0 print {$FILE} (pack("V", -2));
  0         0  
100              
101             #1.2 Add to Data String(this will be written for RootEntry)
102             #Check for update
103 0 0       0 if($oPps->{_PPS_FILE}) {
104 0         0 my $sBuff;
105 0         0 $oPps->{_PPS_FILE}->seek(0, 0); #To The Top
106 0         0 while($oPps->{_PPS_FILE}->read($sBuff, 4096)) {
107 0         0 $sRes .= $sBuff;
108             }
109             }
110             else {
111 0         0 $sRes .= $oPps->{Data};
112             }
113             $sRes .= ("\x00" x
114             ($rhInfo->{_SMALL_BLOCK_SIZE} - ($oPps->{Size}% $rhInfo->{_SMALL_BLOCK_SIZE})))
115 0 0       0 if($oPps->{Size}% $rhInfo->{_SMALL_BLOCK_SIZE});
116             #1.3 Set for PPS
117 0         0 $oPps->{StartBlock} = $iSmBlk;
118 0         0 $iSmBlk += $iSmbCnt;
119             }
120             }
121             }
122 0         0 my $iSbCnt = int($rhInfo->{_BIG_BLOCK_SIZE}/ OLE::Storage_Lite::LongIntSize());
123 0 0       0 print {$FILE} (pack("V", -1) x ($iSbCnt - ($iSmBlk % $iSbCnt)))
  0         0  
124             if($iSmBlk % $iSbCnt);
125             #2. Write SBD with adjusting length for block
126 0         0 return $sRes;
127             }
128             #------------------------------------------------------------------------------
129             # _savePpsWk (OLE::Storage_Lite::PPS)
130             #------------------------------------------------------------------------------
131             sub _savePpsWk($$)
132             {
133 0     0   0 my($oThis, $rhInfo) = @_;
134             #1. Write PPS
135 0         0 my $FILE = $rhInfo->{_FILEH_};
136 0         0 print {$FILE} (
137             $oThis->{Name}
138             . ("\x00" x (64 - length($oThis->{Name}))) #64
139             , pack("v", length($oThis->{Name}) + 2) #66
140             , pack("c", $oThis->{Type}) #67
141             , pack("c", 0x00) #UK #68
142             , pack("V", $oThis->{PrevPps}) #Prev #72
143             , pack("V", $oThis->{NextPps}) #Next #76
144             , pack("V", $oThis->{DirPps}) #Dir #80
145             , "\x00\x09\x02\x00" #84
146             , "\x00\x00\x00\x00" #88
147             , "\xc0\x00\x00\x00" #92
148             , "\x00\x00\x00\x46" #96
149             , "\x00\x00\x00\x00" #100
150             , OLE::Storage_Lite::LocalDate2OLE($oThis->{Time1st}) #108
151             , OLE::Storage_Lite::LocalDate2OLE($oThis->{Time2nd}) #116
152             , pack("V", defined($oThis->{StartBlock})?
153             $oThis->{StartBlock}:0) #116
154             , pack("V", defined($oThis->{Size})?
155 0 0       0 $oThis->{Size} : 0) #124
    0          
156             , pack("V", 0), #128
157             );
158             }
159              
160             #//////////////////////////////////////////////////////////////////////////////
161             # OLE::Storage_Lite::PPS::Root Object
162             #//////////////////////////////////////////////////////////////////////////////
163             #==============================================================================
164             # OLE::Storage_Lite::PPS::Root
165             #==============================================================================
166             package OLE::Storage_Lite::PPS::Root;
167 2     2   16 use strict;
  2         10  
  2         79  
168 2     2   892 use IO::File;
  2         21438  
  2         356  
169 2     2   20 use IO::Handle;
  2         3  
  2         103  
170 2     2   12 use Fcntl;
  2         4  
  2         8383  
171             our @ISA = qw(OLE::Storage_Lite::PPS);
172             our $VERSION = '0.24';
173             sub _savePpsSetPnt($$$);
174             sub _savePpsSetPnt2($$$);
175             #------------------------------------------------------------------------------
176             # new (OLE::Storage_Lite::PPS::Root)
177             #------------------------------------------------------------------------------
178             sub new ($;$$$) {
179 0     0   0 my($sClass, $raTime1st, $raTime2nd, $raChild) = @_;
180 0         0 OLE::Storage_Lite::PPS::_new(
181             $sClass,
182             undef,
183             OLE::Storage_Lite::Asc2Ucs('Root Entry'),
184             5,
185             undef,
186             undef,
187             undef,
188             $raTime1st,
189             $raTime2nd,
190             undef,
191             undef,
192             undef,
193             $raChild);
194             }
195             #------------------------------------------------------------------------------
196             # save (OLE::Storage_Lite::PPS::Root)
197             #------------------------------------------------------------------------------
198             sub save($$;$$) {
199 0     0   0 my($oThis, $sFile, $bNoAs, $rhInfo) = @_;
200             #0.Initial Setting for saving
201 0 0       0 $rhInfo = {} unless($rhInfo);
202             $rhInfo->{_BIG_BLOCK_SIZE} = 2**
203             (($rhInfo->{_BIG_BLOCK_SIZE})?
204 0 0       0 _adjust2($rhInfo->{_BIG_BLOCK_SIZE}) : 9);
205             $rhInfo->{_SMALL_BLOCK_SIZE}= 2 **
206             (($rhInfo->{_SMALL_BLOCK_SIZE})?
207 0 0       0 _adjust2($rhInfo->{_SMALL_BLOCK_SIZE}): 6);
208 0         0 $rhInfo->{_SMALL_SIZE} = 0x1000;
209 0         0 $rhInfo->{_PPS_SIZE} = 0x80;
210              
211 0         0 my $closeFile = 1;
212              
213             #1.Open File
214             #1.1 $sFile is Ref of scalar
215 0 0       0 if(ref($sFile) eq 'SCALAR') {
    0          
    0          
216 0         0 require IO::Scalar;
217 0         0 my $oIo = new IO::Scalar $sFile, O_WRONLY;
218 0         0 $rhInfo->{_FILEH_} = $oIo;
219             }
220             #1.1.1 $sFile is a IO::Scalar object
221             # Now handled as a filehandle ref below.
222              
223             #1.2 $sFile is a IO::Handle object
224             elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) {
225             # Not all filehandles support binmode() so try it in an eval.
226 0         0 eval{ binmode $sFile };
  0         0  
227 0         0 $rhInfo->{_FILEH_} = $sFile;
228             }
229             #1.3 $sFile is a simple filename string
230             elsif(!ref($sFile)) {
231 0 0       0 if($sFile ne '-') {
232 0         0 my $oIo = new IO::File;
233 0 0       0 $oIo->open($sFile, "w") || return undef;
234 0         0 binmode($oIo);
235 0         0 $rhInfo->{_FILEH_} = $oIo;
236             }
237             else {
238 0         0 my $oIo = new IO::Handle;
239 0 0       0 $oIo->fdopen(fileno(STDOUT),"w") || return undef;
240 0         0 binmode($oIo);
241 0         0 $rhInfo->{_FILEH_} = $oIo;
242             }
243             }
244             #1.4 Assume that if $sFile is a ref then it is a valid filehandle
245             else {
246             # Not all filehandles support binmode() so try it in an eval.
247 0         0 eval{ binmode $sFile };
  0         0  
248 0         0 $rhInfo->{_FILEH_} = $sFile;
249             # Caller controls filehandle closing
250 0         0 $closeFile = 0;
251             }
252              
253 0         0 my $iBlk = 0;
254             #1. Make an array of PPS (for Save)
255 0         0 my @aList=();
256 0 0       0 if($bNoAs) {
257 0         0 _savePpsSetPnt2([$oThis], \@aList, $rhInfo);
258             }
259             else {
260 0         0 _savePpsSetPnt([$oThis], \@aList, $rhInfo);
261             }
262 0         0 my ($iSBDcnt, $iBBcnt, $iPPScnt) = $oThis->_calcSize(\@aList, $rhInfo);
263              
264             #2.Save Header
265 0         0 $oThis->_saveHeader($rhInfo, $iSBDcnt, $iBBcnt, $iPPScnt);
266              
267             #3.Make Small Data string (write SBD)
268 0         0 my $sSmWk = $oThis->_makeSmallData(\@aList, $rhInfo);
269 0         0 $oThis->{Data} = $sSmWk; #Small Data become RootEntry Data
270              
271             #4. Write BB
272 0         0 my $iBBlk = $iSBDcnt;
273 0         0 $oThis->_saveBigData(\$iBBlk, \@aList, $rhInfo);
274              
275             #5. Write PPS
276 0         0 $oThis->_savePps(\@aList, $rhInfo);
277              
278             #6. Write BD and BDList and Adding Header information
279 0         0 $oThis->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt, $rhInfo);
280              
281             #7.Close File
282 0 0       0 return $rhInfo->{_FILEH_}->close if $closeFile;
283             }
284             #------------------------------------------------------------------------------
285             # _calcSize (OLE::Storage_Lite::PPS)
286             #------------------------------------------------------------------------------
287             sub _calcSize($$)
288             {
289 0     0   0 my($oThis, $raList, $rhInfo) = @_;
290              
291             #0. Calculate Basic Setting
292 0         0 my ($iSBDcnt, $iBBcnt, $iPPScnt) = (0,0,0);
293 0         0 my $iSmallLen = 0;
294 0         0 my $iSBcnt = 0;
295 0         0 foreach my $oPps (@$raList) {
296 0 0       0 if($oPps->{Type}==OLE::Storage_Lite::PpsType_File()) {
297 0         0 $oPps->{Size} = $oPps->_DataLen(); #Mod
298 0 0       0 if($oPps->{Size} < $rhInfo->{_SMALL_SIZE}) {
299             $iSBcnt += int($oPps->{Size} / $rhInfo->{_SMALL_BLOCK_SIZE})
300 0 0       0 + (($oPps->{Size} % $rhInfo->{_SMALL_BLOCK_SIZE})? 1: 0);
301             }
302             else {
303             $iBBcnt +=
304             (int($oPps->{Size}/ $rhInfo->{_BIG_BLOCK_SIZE}) +
305 0 0       0 (($oPps->{Size}% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0));
306             }
307             }
308             }
309 0         0 $iSmallLen = $iSBcnt * $rhInfo->{_SMALL_BLOCK_SIZE};
310 0         0 my $iSlCnt = int($rhInfo->{_BIG_BLOCK_SIZE}/ OLE::Storage_Lite::LongIntSize());
311 0 0       0 $iSBDcnt = int($iSBcnt / $iSlCnt)+ (($iSBcnt % $iSlCnt)? 1:0);
312             $iBBcnt += (int($iSmallLen/ $rhInfo->{_BIG_BLOCK_SIZE}) +
313 0 0       0 (( $iSmallLen% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0));
314 0         0 my $iCnt = scalar(@$raList);
315 0         0 my $iBdCnt = $rhInfo->{_BIG_BLOCK_SIZE}/OLE::Storage_Lite::PpsSize();
316 0 0       0 $iPPScnt = (int($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0));
317 0         0 return ($iSBDcnt, $iBBcnt, $iPPScnt);
318             }
319             #------------------------------------------------------------------------------
320             # _adjust2 (OLE::Storage_Lite::PPS::Root)
321             #------------------------------------------------------------------------------
322             sub _adjust2($) {
323 0     0   0 my($i2) = @_;
324 0         0 my $iWk;
325 0         0 $iWk = log($i2)/log(2);
326 0 0       0 return ($iWk > int($iWk))? int($iWk)+1:$iWk;
327             }
328             #------------------------------------------------------------------------------
329             # _saveHeader (OLE::Storage_Lite::PPS::Root)
330             #------------------------------------------------------------------------------
331             sub _saveHeader($$$$$) {
332 0     0   0 my($oThis, $rhInfo, $iSBDcnt, $iBBcnt, $iPPScnt) = @_;
333 0         0 my $FILE = $rhInfo->{_FILEH_};
334              
335             #0. Calculate Basic Setting
336 0         0 my $iBlCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize();
337 0         0 my $i1stBdL = int(($rhInfo->{_BIG_BLOCK_SIZE} - 0x4C) / OLE::Storage_Lite::LongIntSize());
338 0         0 my $i1stBdMax = $i1stBdL * $iBlCnt - $i1stBdL;
339 0         0 my $iBdExL = 0;
340 0         0 my $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
341 0         0 my $iAllW = $iAll;
342 0 0       0 my $iBdCntW = int($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0);
343 0 0       0 my $iBdCnt = int(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0);
344 0         0 my $i;
345              
346 0 0       0 if ($iBdCnt > $i1stBdL) {
347             #0.1 Calculate BD count
348 0         0 $iBlCnt--; #the BlCnt is reduced in the count of the last sect is used for a pointer the next Bl
349 0         0 my $iBBleftover = $iAll - $i1stBdMax;
350              
351 0 0       0 if ($iAll >$i1stBdMax) {
352 0         0 while(1) {
353 0 0       0 $iBdCnt = int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0);
354 0 0       0 $iBdExL = int(($iBdCnt) / $iBlCnt) + ((($iBdCnt) % $iBlCnt)? 1: 0);
355 0         0 $iBBleftover = $iBBleftover + $iBdExL;
356 0 0       0 last if($iBdCnt == (int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0)));
    0          
357             }
358             }
359 0         0 $iBdCnt += $i1stBdL;
360             #print "iBdCnt = $iBdCnt \n";
361             }
362             #1.Save Header
363 0 0       0 print {$FILE} (
  0         0  
364             "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
365             , "\x00\x00\x00\x00" x 4
366             , pack("v", 0x3b)
367             , pack("v", 0x03)
368             , pack("v", -2)
369             , pack("v", 9)
370             , pack("v", 6)
371             , pack("v", 0)
372             , "\x00\x00\x00\x00" x 2
373             , pack("V", $iBdCnt),
374             , pack("V", $iBBcnt+$iSBDcnt), #ROOT START
375             , pack("V", 0)
376             , pack("V", 0x1000)
377             , pack("V", $iSBDcnt ? 0 : -2) #Small Block Depot
378             , pack("V", $iSBDcnt)
379             );
380             #2. Extra BDList Start, Count
381 0 0       0 if($iAll <= $i1stBdMax) {
382 0         0 print {$FILE} (
  0         0  
383             pack("V", -2), #Extra BDList Start
384             pack("V", 0), #Extra BDList Count
385             );
386             }
387             else {
388 0         0 print {$FILE} (
  0         0  
389             pack("V", $iAll+$iBdCnt),
390             pack("V", $iBdExL),
391             );
392             }
393              
394             #3. BDList
395 0   0     0 for($i=0; $i<$i1stBdL and $i < $iBdCnt; $i++) {
396 0         0 print {$FILE} (pack("V", $iAll+$i));
  0         0  
397             }
398 0 0       0 print {$FILE} ((pack("V", -1)) x($i1stBdL-$i)) if($i<$i1stBdL);
  0         0  
399             }
400             #------------------------------------------------------------------------------
401             # _saveBigData (OLE::Storage_Lite::PPS)
402             #------------------------------------------------------------------------------
403             sub _saveBigData($$$$) {
404 0     0   0 my($oThis, $iStBlk, $raList, $rhInfo) = @_;
405 0         0 my $iRes = 0;
406 0         0 my $FILE = $rhInfo->{_FILEH_};
407              
408             #1.Write Big (ge 0x1000) Data into Block
409 0         0 foreach my $oPps (@$raList) {
410 0 0       0 if($oPps->{Type}!=OLE::Storage_Lite::PpsType_Dir()) {
411             #print "PPS: $oPps DEF:", defined($oPps->{Data}), "\n";
412 0         0 $oPps->{Size} = $oPps->_DataLen(); #Mod
413 0 0 0     0 if(($oPps->{Size} >= $rhInfo->{_SMALL_SIZE}) ||
      0        
414             (($oPps->{Type} == OLE::Storage_Lite::PpsType_Root()) && defined($oPps->{Data}))) {
415             #1.1 Write Data
416             #Check for update
417 0 0       0 if($oPps->{_PPS_FILE}) {
418 0         0 my $sBuff;
419 0         0 my $iLen = 0;
420 0         0 $oPps->{_PPS_FILE}->seek(0, 0); #To The Top
421 0         0 while($oPps->{_PPS_FILE}->read($sBuff, 4096)) {
422 0         0 $iLen += length($sBuff);
423 0         0 print {$FILE} ($sBuff); #Check for update
  0         0  
424             }
425             }
426             else {
427 0         0 print {$FILE} ($oPps->{Data});
  0         0  
428             }
429 0         0 print {$FILE} (
430             "\x00" x
431             ($rhInfo->{_BIG_BLOCK_SIZE} -
432             ($oPps->{Size} % $rhInfo->{_BIG_BLOCK_SIZE}))
433 0 0       0 ) if ($oPps->{Size} % $rhInfo->{_BIG_BLOCK_SIZE});
434             #1.2 Set For PPS
435 0         0 $oPps->{StartBlock} = $$iStBlk;
436             $$iStBlk +=
437             (int($oPps->{Size}/ $rhInfo->{_BIG_BLOCK_SIZE}) +
438 0 0       0 (($oPps->{Size}% $rhInfo->{_BIG_BLOCK_SIZE})? 1: 0));
439             }
440             }
441             }
442             }
443             #------------------------------------------------------------------------------
444             # _savePps (OLE::Storage_Lite::PPS::Root)
445             #------------------------------------------------------------------------------
446             sub _savePps($$$)
447             {
448 0     0   0 my($oThis, $raList, $rhInfo) = @_;
449             #0. Initial
450 0         0 my $FILE = $rhInfo->{_FILEH_};
451             #2. Save PPS
452 0         0 foreach my $oItem (@$raList) {
453 0         0 $oItem->_savePpsWk($rhInfo);
454             }
455             #3. Adjust for Block
456 0         0 my $iCnt = scalar(@$raList);
457 0         0 my $iBCnt = $rhInfo->{_BIG_BLOCK_SIZE} / $rhInfo->{_PPS_SIZE};
458 0 0       0 print {$FILE} ("\x00" x (($iBCnt - ($iCnt % $iBCnt)) * $rhInfo->{_PPS_SIZE}))
  0         0  
459             if($iCnt % $iBCnt);
460 0 0       0 return int($iCnt / $iBCnt) + (($iCnt % $iBCnt)? 1: 0);
461             }
462             #------------------------------------------------------------------------------
463             # _savePpsSetPnt2 (OLE::Storage_Lite::PPS::Root)
464             # For Test
465             #------------------------------------------------------------------------------
466             sub _savePpsSetPnt2($$$)
467             {
468 0     0   0 my($aThis, $raList, $rhInfo) = @_;
469             #1. make Array as Children-Relations
470             #1.1 if No Children
471 0 0       0 if($#$aThis < 0) {
    0          
472 0         0 return 0xFFFFFFFF;
473             }
474             elsif($#$aThis == 0) {
475             #1.2 Just Only one
476 0         0 push @$raList, $aThis->[0];
477 0         0 $aThis->[0]->{No} = $#$raList;
478 0         0 $aThis->[0]->{PrevPps} = 0xFFFFFFFF;
479 0         0 $aThis->[0]->{NextPps} = 0xFFFFFFFF;
480 0         0 $aThis->[0]->{DirPps} = _savePpsSetPnt2($aThis->[0]->{Child}, $raList, $rhInfo);
481 0         0 return $aThis->[0]->{No};
482             }
483             else {
484             #1.3 Array
485 0         0 my $iCnt = $#$aThis + 1;
486             #1.3.1 Define Center
487 0         0 my $iPos = 0; #int($iCnt/ 2); #$iCnt
488              
489 0         0 my @aWk = @$aThis;
490 0 0       0 my @aPrev = ($#$aThis > 1)? splice(@aWk, 1, 1) : (); #$iPos);
491 0         0 my @aNext = splice(@aWk, 1); #, $iCnt - $iPos -1);
492 0         0 $aThis->[$iPos]->{PrevPps} = _savePpsSetPnt2(
493             \@aPrev, $raList, $rhInfo);
494 0         0 push @$raList, $aThis->[$iPos];
495 0         0 $aThis->[$iPos]->{No} = $#$raList;
496              
497             #1.3.2 Divide a array into Previous,Next
498 0         0 $aThis->[$iPos]->{NextPps} = _savePpsSetPnt2(
499             \@aNext, $raList, $rhInfo);
500 0         0 $aThis->[$iPos]->{DirPps} = _savePpsSetPnt2($aThis->[$iPos]->{Child}, $raList, $rhInfo);
501 0         0 return $aThis->[$iPos]->{No};
502             }
503             }
504             #------------------------------------------------------------------------------
505             # _savePpsSetPnt2 (OLE::Storage_Lite::PPS::Root)
506             # For Test
507             #------------------------------------------------------------------------------
508             sub _savePpsSetPnt2s($$$)
509             {
510 0     0   0 my($aThis, $raList, $rhInfo) = @_;
511             #1. make Array as Children-Relations
512             #1.1 if No Children
513 0 0       0 if($#$aThis < 0) {
    0          
514 0         0 return 0xFFFFFFFF;
515             }
516             elsif($#$aThis == 0) {
517             #1.2 Just Only one
518 0         0 push @$raList, $aThis->[0];
519 0         0 $aThis->[0]->{No} = $#$raList;
520 0         0 $aThis->[0]->{PrevPps} = 0xFFFFFFFF;
521 0         0 $aThis->[0]->{NextPps} = 0xFFFFFFFF;
522 0         0 $aThis->[0]->{DirPps} = _savePpsSetPnt2($aThis->[0]->{Child}, $raList, $rhInfo);
523 0         0 return $aThis->[0]->{No};
524             }
525             else {
526             #1.3 Array
527 0         0 my $iCnt = $#$aThis + 1;
528             #1.3.1 Define Center
529 0         0 my $iPos = 0; #int($iCnt/ 2); #$iCnt
530 0         0 push @$raList, $aThis->[$iPos];
531 0         0 $aThis->[$iPos]->{No} = $#$raList;
532 0         0 my @aWk = @$aThis;
533             #1.3.2 Divide a array into Previous,Next
534 0         0 my @aPrev = splice(@aWk, 0, $iPos);
535 0         0 my @aNext = splice(@aWk, 1, $iCnt - $iPos -1);
536 0         0 $aThis->[$iPos]->{PrevPps} = _savePpsSetPnt2(
537             \@aPrev, $raList, $rhInfo);
538 0         0 $aThis->[$iPos]->{NextPps} = _savePpsSetPnt2(
539             \@aNext, $raList, $rhInfo);
540 0         0 $aThis->[$iPos]->{DirPps} = _savePpsSetPnt2($aThis->[$iPos]->{Child}, $raList, $rhInfo);
541 0         0 return $aThis->[$iPos]->{No};
542             }
543             }
544             #------------------------------------------------------------------------------
545             # _savePpsSetPnt (OLE::Storage_Lite::PPS::Root)
546             #------------------------------------------------------------------------------
547             sub _savePpsSetPnt($$$)
548             {
549 0     0   0 my($aThis, $raList, $rhInfo) = @_;
550             #1. make Array as Children-Relations
551             #1.1 if No Children
552 0 0       0 if($#$aThis < 0) {
    0          
553 0         0 return 0xFFFFFFFF;
554             }
555             elsif($#$aThis == 0) {
556             #1.2 Just Only one
557 0         0 push @$raList, $aThis->[0];
558 0         0 $aThis->[0]->{No} = $#$raList;
559 0         0 $aThis->[0]->{PrevPps} = 0xFFFFFFFF;
560 0         0 $aThis->[0]->{NextPps} = 0xFFFFFFFF;
561 0         0 $aThis->[0]->{DirPps} = _savePpsSetPnt($aThis->[0]->{Child}, $raList, $rhInfo);
562 0         0 return $aThis->[0]->{No};
563             }
564             else {
565             #1.3 Array
566 0         0 my $iCnt = $#$aThis + 1;
567             #1.3.1 Define Center
568 0         0 my $iPos = int($iCnt/ 2); #$iCnt
569 0         0 push @$raList, $aThis->[$iPos];
570 0         0 $aThis->[$iPos]->{No} = $#$raList;
571 0         0 my @aWk = @$aThis;
572             #1.3.2 Divide a array into Previous,Next
573 0         0 my @aPrev = splice(@aWk, 0, $iPos);
574 0         0 my @aNext = splice(@aWk, 1, $iCnt - $iPos -1);
575 0         0 $aThis->[$iPos]->{PrevPps} = _savePpsSetPnt(
576             \@aPrev, $raList, $rhInfo);
577 0         0 $aThis->[$iPos]->{NextPps} = _savePpsSetPnt(
578             \@aNext, $raList, $rhInfo);
579 0         0 $aThis->[$iPos]->{DirPps} = _savePpsSetPnt($aThis->[$iPos]->{Child}, $raList, $rhInfo);
580 0         0 return $aThis->[$iPos]->{No};
581             }
582             }
583             #------------------------------------------------------------------------------
584             # _savePpsSetPnt (OLE::Storage_Lite::PPS::Root)
585             #------------------------------------------------------------------------------
586             sub _savePpsSetPnt1($$$)
587             {
588 0     0   0 my($aThis, $raList, $rhInfo) = @_;
589             #1. make Array as Children-Relations
590             #1.1 if No Children
591 0 0       0 if($#$aThis < 0) {
    0          
592 0         0 return 0xFFFFFFFF;
593             }
594             elsif($#$aThis == 0) {
595             #1.2 Just Only one
596 0         0 push @$raList, $aThis->[0];
597 0         0 $aThis->[0]->{No} = $#$raList;
598 0         0 $aThis->[0]->{PrevPps} = 0xFFFFFFFF;
599 0         0 $aThis->[0]->{NextPps} = 0xFFFFFFFF;
600 0         0 $aThis->[0]->{DirPps} = _savePpsSetPnt($aThis->[0]->{Child}, $raList, $rhInfo);
601 0         0 return $aThis->[0]->{No};
602             }
603             else {
604             #1.3 Array
605 0         0 my $iCnt = $#$aThis + 1;
606             #1.3.1 Define Center
607 0         0 my $iPos = int($iCnt/ 2); #$iCnt
608 0         0 push @$raList, $aThis->[$iPos];
609 0         0 $aThis->[$iPos]->{No} = $#$raList;
610 0         0 my @aWk = @$aThis;
611             #1.3.2 Divide a array into Previous,Next
612 0         0 my @aPrev = splice(@aWk, 0, $iPos);
613 0         0 my @aNext = splice(@aWk, 1, $iCnt - $iPos -1);
614 0         0 $aThis->[$iPos]->{PrevPps} = _savePpsSetPnt(
615             \@aPrev, $raList, $rhInfo);
616 0         0 $aThis->[$iPos]->{NextPps} = _savePpsSetPnt(
617             \@aNext, $raList, $rhInfo);
618 0         0 $aThis->[$iPos]->{DirPps} = _savePpsSetPnt($aThis->[$iPos]->{Child}, $raList, $rhInfo);
619 0         0 return $aThis->[$iPos]->{No};
620             }
621             }
622             #------------------------------------------------------------------------------
623             # _saveBbd (OLE::Storage_Lite)
624             #------------------------------------------------------------------------------
625             sub _saveBbd($$$$)
626             {
627 0     0   0 my($oThis, $iSbdSize, $iBsize, $iPpsCnt, $rhInfo) = @_;
628 0         0 my $FILE = $rhInfo->{_FILEH_};
629             #0. Calculate Basic Setting
630 0         0 my $iBbCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::LongIntSize();
631 0         0 my $iBlCnt = $iBbCnt - 1;
632 0         0 my $i1stBdL = int(($rhInfo->{_BIG_BLOCK_SIZE} - 0x4C) / OLE::Storage_Lite::LongIntSize());
633 0         0 my $i1stBdMax = $i1stBdL * $iBbCnt - $i1stBdL;
634 0         0 my $iBdExL = 0;
635 0         0 my $iAll = $iBsize + $iPpsCnt + $iSbdSize;
636 0         0 my $iAllW = $iAll;
637 0 0       0 my $iBdCntW = int($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0);
638 0         0 my $iBdCnt = 0;
639 0         0 my $i;
640             #0.1 Calculate BD count
641 0         0 my $iBBleftover = $iAll - $i1stBdMax;
642 0 0       0 if ($iAll >$i1stBdMax) {
643              
644 0         0 while(1) {
645 0 0       0 $iBdCnt = int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0);
646 0 0       0 $iBdExL = int(($iBdCnt) / $iBlCnt) + ((($iBdCnt) % $iBlCnt)? 1: 0);
647 0         0 $iBBleftover = $iBBleftover + $iBdExL;
648 0 0       0 last if($iBdCnt == (int(($iBBleftover) / $iBlCnt) + ((($iBBleftover) % $iBlCnt)? 1: 0)));
    0          
649             }
650             }
651 0         0 $iAllW += $iBdExL;
652 0         0 $iBdCnt += $i1stBdL;
653             #print "iBdCnt = $iBdCnt \n";
654              
655             #1. Making BD
656             #1.1 Set for SBD
657 0 0       0 if($iSbdSize > 0) {
658 0         0 for ($i = 0; $i<($iSbdSize-1); $i++) {
659 0         0 print {$FILE} (pack("V", $i+1));
  0         0  
660             }
661 0         0 print {$FILE} (pack("V", -2));
  0         0  
662             }
663             #1.2 Set for B
664 0         0 for ($i = 0; $i<($iBsize-1); $i++) {
665 0         0 print {$FILE} (pack("V", $i+$iSbdSize+1));
  0         0  
666             }
667 0         0 print {$FILE} (pack("V", -2));
  0         0  
668              
669             #1.3 Set for PPS
670 0         0 for ($i = 0; $i<($iPpsCnt-1); $i++) {
671 0         0 print {$FILE} (pack("V", $i+$iSbdSize+$iBsize+1));
  0         0  
672             }
673 0         0 print {$FILE} (pack("V", -2));
  0         0  
674             #1.4 Set for BBD itself ( 0xFFFFFFFD : BBD)
675 0         0 for($i=0; $i<$iBdCnt;$i++) {
676 0         0 print {$FILE} (pack("V", 0xFFFFFFFD));
  0         0  
677             }
678             #1.5 Set for ExtraBDList
679 0         0 for($i=0; $i<$iBdExL;$i++) {
680 0         0 print {$FILE} (pack("V", 0xFFFFFFFC));
  0         0  
681             }
682             #1.6 Adjust for Block
683 0 0       0 print {$FILE} (pack("V", -1) x ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)))
  0         0  
684             if(($iAllW + $iBdCnt) % $iBbCnt);
685             #2.Extra BDList
686 0 0       0 if($iBdCnt > $i1stBdL) {
687 0         0 my $iN=0;
688 0         0 my $iNb=0;
689 0         0 for($i=$i1stBdL;$i<$iBdCnt; $i++, $iN++) {
690 0 0       0 if($iN>=($iBbCnt-1)) {
691 0         0 $iN = 0;
692 0         0 $iNb++;
693 0         0 print {$FILE} (pack("V", $iAll+$iBdCnt+$iNb));
  0         0  
694             }
695 0         0 print {$FILE} (pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i));
  0         0  
696             }
697 0 0       0 print {$FILE} (pack("V", -1) x (($iBbCnt-1) - (($iBdCnt-$i1stBdL) % ($iBbCnt-1))))
  0         0  
698             if(($iBdCnt-$i1stBdL) % ($iBbCnt-1));
699 0         0 print {$FILE} (pack("V", -2));
  0         0  
700             }
701             }
702              
703             #//////////////////////////////////////////////////////////////////////////////
704             # OLE::Storage_Lite::PPS::File Object
705             #//////////////////////////////////////////////////////////////////////////////
706             #==============================================================================
707             # OLE::Storage_Lite::PPS::File
708             #==============================================================================
709             package OLE::Storage_Lite::PPS::File;
710 2     2   16 use strict;
  2         4  
  2         847  
711             our @ISA = qw(OLE::Storage_Lite::PPS);
712             our $VERSION = '0.24';
713             #------------------------------------------------------------------------------
714             # new (OLE::Storage_Lite::PPS::File)
715             #------------------------------------------------------------------------------
716             sub new ($$$) {
717 0     0   0 my($sClass, $sNm, $sData) = @_;
718 0         0 OLE::Storage_Lite::PPS::_new(
719             $sClass,
720             undef,
721             $sNm,
722             2,
723             undef,
724             undef,
725             undef,
726             undef,
727             undef,
728             undef,
729             undef,
730             $sData,
731             undef);
732             }
733             #------------------------------------------------------------------------------
734             # newFile (OLE::Storage_Lite::PPS::File)
735             #------------------------------------------------------------------------------
736             sub newFile ($$;$) {
737 0     0   0 my($sClass, $sNm, $sFile) = @_;
738 0         0 my $oSelf =
739             OLE::Storage_Lite::PPS::_new(
740             $sClass,
741             undef,
742             $sNm,
743             2,
744             undef,
745             undef,
746             undef,
747             undef,
748             undef,
749             undef,
750             undef,
751             '',
752             undef);
753             #
754 0 0 0     0 if((!defined($sFile)) or ($sFile eq '')) {
    0          
    0          
755 0         0 $oSelf->{_PPS_FILE} = IO::File->new_tmpfile();
756             }
757             elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) {
758 0         0 $oSelf->{_PPS_FILE} = $sFile;
759             }
760             elsif(!ref($sFile)) {
761             #File Name
762 0         0 $oSelf->{_PPS_FILE} = new IO::File;
763 0 0       0 return undef unless($oSelf->{_PPS_FILE});
764 0 0       0 $oSelf->{_PPS_FILE}->open("$sFile", "r+") || return undef;
765             }
766             else {
767 0         0 return undef;
768             }
769 0 0       0 if($oSelf->{_PPS_FILE}) {
770 0         0 $oSelf->{_PPS_FILE}->seek(0, 2);
771 0         0 binmode($oSelf->{_PPS_FILE});
772 0         0 $oSelf->{_PPS_FILE}->autoflush(1);
773             }
774 0         0 return $oSelf;
775             }
776             #------------------------------------------------------------------------------
777             # append (OLE::Storage_Lite::PPS::File)
778             #------------------------------------------------------------------------------
779             sub append ($$) {
780 0     0   0 my($oSelf, $sData) = @_;
781 0 0       0 if($oSelf->{_PPS_FILE}) {
782 0         0 print {$oSelf->{_PPS_FILE}} $sData;
  0         0  
783             }
784             else {
785 0         0 $oSelf->{Data} .= $sData;
786             }
787             }
788              
789             #//////////////////////////////////////////////////////////////////////////////
790             # OLE::Storage_Lite::PPS::Dir Object
791             #//////////////////////////////////////////////////////////////////////////////
792             #------------------------------------------------------------------------------
793             # new (OLE::Storage_Lite::PPS::Dir)
794             #------------------------------------------------------------------------------
795             package OLE::Storage_Lite::PPS::Dir;
796 2     2   19 use strict;
  2         3  
  2         235  
797             our @ISA = qw(OLE::Storage_Lite::PPS);
798             our $VERSION = '0.24';
799             sub new ($$;$$$) {
800 0     0   0 my($sClass, $sName, $raTime1st, $raTime2nd, $raChild) = @_;
801 0         0 OLE::Storage_Lite::PPS::_new(
802             $sClass,
803             undef,
804             $sName,
805             1,
806             undef,
807             undef,
808             undef,
809             $raTime1st,
810             $raTime2nd,
811             undef,
812             undef,
813             undef,
814             $raChild);
815             }
816             #==============================================================================
817             # OLE::Storage_Lite
818             #==============================================================================
819             package OLE::Storage_Lite;
820              
821 2     2   9 use strict;
  2         4  
  2         49  
822 2     2   8 use Carp;
  2         4  
  2         140  
823 2     2   9 use IO::File;
  2         3  
  2         262  
824 2     2   10 use List::Util qw(first);
  2         3  
  2         284  
825 2     2   1179 use Time::Local 'timegm';
  2         5019  
  2         212  
826              
827             our $VERSION = '0.24';
828             sub _getPpsSearch($$$$$;$);
829             sub _getPpsTree($$$;$);
830             #------------------------------------------------------------------------------
831             # Const for OLE::Storage_Lite
832             #------------------------------------------------------------------------------
833             #0. Constants
834             use constant {
835 2         7266 PpsType_Root => 5,
836             PpsType_Dir => 1,
837             PpsType_File => 2,
838             DataSizeSmall => 0x1000,
839             LongIntSize => 4,
840             PpsSize => 0x80,
841             # 0xFFFFFFFC : BDList, 0xFFFFFFFD : BBD,
842             # 0xFFFFFFFE: End of Chain 0xFFFFFFFF : unused
843             NormalBlockEnd => 0xFFFFFFFC,
844 2     2   13 };
  2         4  
845             #------------------------------------------------------------------------------
846             # new OLE::Storage_Lite
847             #------------------------------------------------------------------------------
848             sub new($$) {
849 0     0 1 0 my($sClass, $sFile) = @_;
850 0         0 my $oThis = {
851             _FILE => $sFile,
852             };
853 0         0 bless $oThis;
854 0         0 return $oThis;
855             }
856             #------------------------------------------------------------------------------
857             # getPpsTree: OLE::Storage_Lite
858             #------------------------------------------------------------------------------
859             sub getPpsTree($;$)
860             {
861 0     0 1 0 my($oThis, $bData) = @_;
862             #0.Init
863 0         0 my $rhInfo = _initParse($oThis->{_FILE});
864 0 0       0 return undef unless($rhInfo);
865             #1. Get Data
866 0         0 my ($oPps) = _getPpsTree(0, $rhInfo, $bData);
867 0         0 close(IN);
868 0         0 return $oPps;
869             }
870             #------------------------------------------------------------------------------
871             # getSearch: OLE::Storage_Lite
872             #------------------------------------------------------------------------------
873             sub getPpsSearch($$;$$)
874             {
875 0     0 1 0 my($oThis, $raName, $bData, $iCase) = @_;
876             #0.Init
877 0         0 my $rhInfo = _initParse($oThis->{_FILE});
878 0 0       0 return undef unless($rhInfo);
879             #1. Get Data
880 0         0 my @aList = _getPpsSearch(0, $rhInfo, $raName, $bData, $iCase);
881 0         0 close(IN);
882 0         0 return @aList;
883             }
884             #------------------------------------------------------------------------------
885             # getNthPps: OLE::Storage_Lite
886             #------------------------------------------------------------------------------
887             sub getNthPps($$;$)
888             {
889 0     0 1 0 my($oThis, $iNo, $bData) = @_;
890             #0.Init
891 0         0 my $rhInfo = _initParse($oThis->{_FILE});
892 0 0       0 return undef unless($rhInfo);
893             #1. Get Data
894 0         0 my $oPps = _getNthPps($iNo, $rhInfo, $bData);
895 0         0 close IN;
896 0         0 return $oPps;
897             }
898             #------------------------------------------------------------------------------
899             # _initParse: OLE::Storage_Lite
900             #------------------------------------------------------------------------------
901             sub _initParse($) {
902 0     0   0 my($sFile)=@_;
903 0         0 my $oIo;
904             #1. $sFile is Ref of scalar
905 0 0       0 if(ref($sFile) eq 'SCALAR') {
    0          
    0          
906 0         0 require IO::Scalar;
907 0         0 $oIo = new IO::Scalar;
908 0         0 $oIo->open($sFile);
909             }
910             #2. $sFile is a IO::Handle object
911             elsif(UNIVERSAL::isa($sFile, 'IO::Handle')) {
912 0         0 $oIo = $sFile;
913 0         0 binmode($oIo);
914             }
915             #3. $sFile is a simple filename string
916             elsif(!ref($sFile)) {
917 0         0 $oIo = new IO::File;
918 0 0       0 $oIo->open($sFile, "r") || return undef;
919 0         0 binmode($oIo);
920             }
921             #4 Assume that if $sFile is a ref then it is a valid filehandle
922             else {
923 0         0 $oIo = $sFile;
924             # Not all filehandles support binmode() so try it in an eval.
925 0         0 eval{ binmode $oIo };
  0         0  
926             }
927 0         0 return _getHeaderInfo($oIo);
928             }
929             #------------------------------------------------------------------------------
930             # _getPpsTree: OLE::Storage_Lite
931             #------------------------------------------------------------------------------
932             sub _getPpsTree($$$;$) {
933 0     0   0 my($iNo, $rhInfo, $bData, $raDone) = @_;
934 0 0       0 if(defined($raDone)) {
935 0 0       0 return () if(exists($raDone->{$iNo}));
936             }
937             else {
938 0         0 $raDone={};
939             }
940 0         0 $raDone->{$iNo} = undef;
941              
942 0         0 my $iRootBlock = $rhInfo->{_ROOT_START} ;
943             #1. Get Information about itself
944 0         0 my $oPps = _getNthPps($iNo, $rhInfo, $bData);
945             #2. Child
946 0 0       0 if($oPps->{DirPps} != 0xFFFFFFFF) {
947 0         0 my @aChildL = _getPpsTree($oPps->{DirPps}, $rhInfo, $bData, $raDone);
948 0         0 $oPps->{Child} = \@aChildL;
949             }
950             else {
951 0         0 $oPps->{Child} = undef;
952             }
953             #3. Previous,Next PPSs
954 0         0 my @aList = ();
955             push @aList, _getPpsTree($oPps->{PrevPps}, $rhInfo, $bData, $raDone)
956 0 0       0 if($oPps->{PrevPps} != 0xFFFFFFFF);
957 0         0 push @aList, $oPps;
958             push @aList, _getPpsTree($oPps->{NextPps}, $rhInfo, $bData, $raDone)
959 0 0       0 if($oPps->{NextPps} != 0xFFFFFFFF);
960 0         0 return @aList;
961             }
962             #------------------------------------------------------------------------------
963             # _getPpsSearch: OLE::Storage_Lite
964             #------------------------------------------------------------------------------
965             sub _getPpsSearch($$$$$;$) {
966 0     0   0 my($iNo, $rhInfo, $raName, $bData, $iCase, $raDone) = @_;
967 0         0 my $iRootBlock = $rhInfo->{_ROOT_START} ;
968 0         0 my @aRes;
969             #1. Check it self
970 0 0       0 if(defined($raDone)) {
971 0 0       0 return () if(exists($raDone->{$iNo}));
972             }
973             else {
974 0         0 $raDone={};
975             }
976 0         0 $raDone->{$iNo} = undef;
977 0         0 my $oPps = _getNthPps($iNo, $rhInfo, undef);
978             # if(first {$_ eq $oPps->{Name}} @$raName) {
979 0 0 0 0   0 if(($iCase && (first {/^\Q$oPps->{Name}\E$/i} @$raName)) ||
  0   0     0  
980 0     0   0 (first {$_ eq $oPps->{Name}} @$raName)) {
981 0 0       0 $oPps = _getNthPps($iNo, $rhInfo, $bData) if ($bData);
982 0         0 @aRes = ($oPps);
983             }
984             else {
985 0         0 @aRes = ();
986             }
987             #2. Check Child, Previous, Next PPSs
988             push @aRes, _getPpsSearch($oPps->{DirPps}, $rhInfo, $raName, $bData, $iCase, $raDone)
989 0 0       0 if($oPps->{DirPps} != 0xFFFFFFFF) ;
990             push @aRes, _getPpsSearch($oPps->{PrevPps}, $rhInfo, $raName, $bData, $iCase, $raDone)
991 0 0       0 if($oPps->{PrevPps} != 0xFFFFFFFF );
992             push @aRes, _getPpsSearch($oPps->{NextPps}, $rhInfo, $raName, $bData, $iCase, $raDone)
993 0 0       0 if($oPps->{NextPps} != 0xFFFFFFFF);
994 0         0 return @aRes;
995             }
996             #===================================================================
997             # Get Header Info (BASE Information about that file)
998             #===================================================================
999             sub _getHeaderInfo($){
1000 0     0   0 my($FILE) = @_;
1001 0         0 my($iWk);
1002 0         0 my $rhInfo = {};
1003 0         0 $rhInfo->{_FILEH_} = $FILE;
1004 0         0 my $sWk;
1005             #0. Check ID
1006 0         0 $rhInfo->{_FILEH_}->seek(0, 0);
1007 0         0 $rhInfo->{_FILEH_}->read($sWk, 8);
1008 0 0       0 return undef unless($sWk eq "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1");
1009             #BIG BLOCK SIZE
1010 0         0 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x1E, 2, "v");
1011 0 0       0 return undef unless(defined($iWk));
1012 0         0 $rhInfo->{_BIG_BLOCK_SIZE} = 2 ** $iWk;
1013             #SMALL BLOCK SIZE
1014 0         0 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x20, 2, "v");
1015 0 0       0 return undef unless(defined($iWk));
1016 0         0 $rhInfo->{_SMALL_BLOCK_SIZE} = 2 ** $iWk;
1017             #BDB Count
1018 0         0 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x2C, 4, "V");
1019 0 0       0 return undef unless(defined($iWk));
1020 0         0 $rhInfo->{_BDB_COUNT} = $iWk;
1021             #START BLOCK
1022 0         0 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x30, 4, "V");
1023 0 0       0 return undef unless(defined($iWk));
1024 0         0 $rhInfo->{_ROOT_START} = $iWk;
1025             #MIN SIZE OF BB
1026             # $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x38, 4, "V");
1027             # return undef unless(defined($iWk));
1028             # $rhInfo->{_MIN_SIZE_BB} = $iWk;
1029             #SMALL BD START
1030 0         0 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x3C, 4, "V");
1031 0 0       0 return undef unless(defined($iWk));
1032 0         0 $rhInfo->{_SBD_START} = $iWk;
1033             #SMALL BD COUNT
1034 0         0 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x40, 4, "V");
1035 0 0       0 return undef unless(defined($iWk));
1036 0         0 $rhInfo->{_SBD_COUNT} = $iWk;
1037             #EXTRA BBD START
1038 0         0 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x44, 4, "V");
1039 0 0       0 return undef unless(defined($iWk));
1040 0         0 $rhInfo->{_EXTRA_BBD_START} = $iWk;
1041             #EXTRA BD COUNT
1042 0         0 $iWk = _getInfoFromFile($rhInfo->{_FILEH_}, 0x48, 4, "V");
1043 0 0       0 return undef unless(defined($iWk));
1044 0         0 $rhInfo->{_EXTRA_BBD_COUNT} = $iWk;
1045             #GET BBD INFO
1046 0         0 $rhInfo->{_BBD_INFO}= _getBbdInfo($rhInfo);
1047             #GET ROOT PPS
1048 0         0 my $oRoot = _getNthPps(0, $rhInfo, undef);
1049 0         0 $rhInfo->{_SB_START} = $oRoot->{StartBlock};
1050 0         0 $rhInfo->{_SB_SIZE} = $oRoot->{Size};
1051             # cache lookaheads for huge performance improvement in some cases
1052 0         0 my $iNextCount = keys(%{$rhInfo->{_BBD_INFO}});
  0         0  
1053 0         0 my $iBlockNo = $rhInfo->{_ROOT_START};
1054 0         0 my $iBigBlkSize=$rhInfo->{_BIG_BLOCK_SIZE};
1055 0         0 $rhInfo->{_BBD_ROOT_START}= [$iBlockNo];
1056 0         0 for(1..$iNextCount) {
1057 0   0     0 $iBlockNo = $rhInfo->{_BBD_INFO}->{$iBlockNo} // $iBlockNo+1;
1058 0 0       0 last unless $iBlockNo < OLE::Storage_Lite::NormalBlockEnd();
1059 0         0 $rhInfo->{_BBD_ROOT_START}->[$_] = $iBlockNo;
1060             }
1061 0         0 $iBlockNo = $rhInfo->{_SB_START};
1062 0         0 $rhInfo->{_BBD_SB_START}= [($iBlockNo+1)*$iBigBlkSize];
1063 0         0 for(1..$iNextCount) {
1064 0   0     0 $iBlockNo = $rhInfo->{_BBD_INFO}->{$iBlockNo} // $iBlockNo+1;
1065 0 0       0 last unless $iBlockNo < OLE::Storage_Lite::NormalBlockEnd();
1066 0         0 $rhInfo->{_BBD_SB_START}->[$_] = ($iBlockNo+1)*$iBigBlkSize;
1067             }
1068 0         0 $iBlockNo = $rhInfo->{_SBD_START};
1069 0         0 $rhInfo->{_BBD_SBD_START}= [($iBlockNo+1)*$iBigBlkSize];
1070 0         0 for(1..$iNextCount) {
1071 0   0     0 $iBlockNo = $rhInfo->{_BBD_INFO}->{$iBlockNo} // $iBlockNo+1;
1072 0 0       0 last unless $iBlockNo < OLE::Storage_Lite::NormalBlockEnd();
1073 0         0 $rhInfo->{_BBD_SBD_START}->[$_] = ($iBlockNo+1)*$iBigBlkSize;
1074             }
1075 0         0 my @aKeys= sort({$a<=>$b} keys(%{$rhInfo->{_BBD_INFO}}));
  0         0  
  0         0  
1076 0         0 $rhInfo->{_BBD_INFO_SORTED}= \@aKeys;
1077 0         0 return $rhInfo;
1078             }
1079             #------------------------------------------------------------------------------
1080             # _getInfoFromFile
1081             #------------------------------------------------------------------------------
1082             sub _getInfoFromFile($$$$) {
1083 0     0   0 my($FILE, $iPos, $iLen, $sFmt) =@_;
1084 0         0 my($sWk);
1085 0 0       0 return undef unless($FILE);
1086 0 0       0 return undef if($FILE->seek($iPos, 0)==0);
1087 0 0       0 return undef if($FILE->read($sWk, $iLen)!=$iLen);
1088 0         0 return unpack($sFmt, $sWk);
1089             }
1090             #------------------------------------------------------------------------------
1091             # _getBbdInfo
1092             #------------------------------------------------------------------------------
1093             sub _getBbdInfo($) {
1094 0     0   0 my($rhInfo) =@_;
1095 0         0 my @aBdList = ();
1096 0         0 my $iBdbCnt = $rhInfo->{_BDB_COUNT};
1097 0         0 my $iBigBlkSize = $rhInfo->{_BIG_BLOCK_SIZE};
1098 0         0 my $iGetCnt;
1099             my $sWk;
1100 0         0 my $i1stCnt = int(($iBigBlkSize - 0x4C) / OLE::Storage_Lite::LongIntSize());
1101 0         0 my $iBdlCnt = int($iBigBlkSize / OLE::Storage_Lite::LongIntSize()) - 1;
1102              
1103             #1. 1st BDlist
1104 0         0 $rhInfo->{_FILEH_}->seek(0x4C, 0);
1105 0 0       0 $iGetCnt = ($iBdbCnt < $i1stCnt)? $iBdbCnt: $i1stCnt;
1106 0         0 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize()*$iGetCnt);
1107 0         0 push @aBdList, unpack("V$iGetCnt", $sWk);
1108 0         0 $iBdbCnt -= $iGetCnt;
1109             #2. Extra BDList
1110 0         0 my $iBlock = $rhInfo->{_EXTRA_BBD_START};
1111 0   0     0 while(($iBdbCnt> 0) && $iBlock < OLE::Storage_Lite::NormalBlockEnd()){
1112 0         0 $rhInfo->{_FILEH_}->seek(($iBlock+1)*$iBigBlkSize, 0);
1113 0 0       0 $iGetCnt= ($iBdbCnt < $iBdlCnt)? $iBdbCnt: $iBdlCnt;
1114 0         0 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize()*$iGetCnt);
1115 0         0 push @aBdList, unpack("V$iGetCnt", $sWk);
1116 0         0 $iBdbCnt -= $iGetCnt;
1117 0         0 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::LongIntSize());
1118 0         0 $iBlock = unpack("V", $sWk);
1119             }
1120             #3.Get BDs
1121 0         0 my @aWk;
1122             my %hBd;
1123 0         0 my $iBlkNo = 0;
1124 0         0 my $iBdL;
1125             my $i;
1126 0         0 my $iBdCnt = int($iBigBlkSize / OLE::Storage_Lite::LongIntSize());
1127 0         0 foreach $iBdL (@aBdList) {
1128 0         0 $rhInfo->{_FILEH_}->seek(($iBdL+1)*$iBigBlkSize, 0);
1129 0         0 $rhInfo->{_FILEH_}->read($sWk, $iBigBlkSize);
1130 0         0 @aWk = unpack("V$iBdCnt", $sWk);
1131 0         0 for($i=0;$i<$iBdCnt;$i++, $iBlkNo++) {
1132 0 0       0 if($aWk[$i] != ($iBlkNo+1)){
1133 0         0 $hBd{$iBlkNo} = $aWk[$i];
1134             }
1135             }
1136             }
1137 0         0 return \%hBd;
1138             }
1139             #------------------------------------------------------------------------------
1140             # getNthPps (OLE::Storage_Lite)
1141             #------------------------------------------------------------------------------
1142             sub _getNthPps($$$){
1143 0     0   0 my($iPos, $rhInfo, $bData) = @_;
1144 0         0 my($iPpsBlock, $iPpsPos);
1145 0         0 my $sWk;
1146 0         0 my $iBlock;
1147              
1148 0         0 my $iBaseCnt = $rhInfo->{_BIG_BLOCK_SIZE} / OLE::Storage_Lite::PpsSize();
1149 0         0 $iPpsBlock = int($iPos / $iBaseCnt);
1150 0         0 $iPpsPos = $iPos % $iBaseCnt;
1151              
1152             $iBlock = $rhInfo->{_BBD_ROOT_START}->[$iPpsBlock] //
1153 0   0     0 _getNthBlockNo($rhInfo->{_ROOT_START}, $iPpsBlock, $rhInfo);
1154 0 0       0 return undef unless(defined($iBlock));
1155              
1156             $rhInfo->{_FILEH_}->seek(($iBlock+1)*$rhInfo->{_BIG_BLOCK_SIZE}+
1157 0         0 (OLE::Storage_Lite::PpsSize()*$iPpsPos), 0);
1158 0         0 $rhInfo->{_FILEH_}->read($sWk, OLE::Storage_Lite::PpsSize());
1159 0 0       0 return undef unless($sWk);
1160 0         0 my ($iNmSize, $iType, undef, $lPpsPrev, $lPpsNext, $lDirPps) =
1161             unpack("vCCVVV", substr($sWk, 0x40, 2+2+3*OLE::Storage_Lite::LongIntSize()));
1162 0 0       0 $iNmSize = ($iNmSize > 2)? $iNmSize - 2 : $iNmSize;
1163 0         0 my $sNm= substr($sWk, 0, $iNmSize);
1164 0 0 0     0 my @raTime1st =
    0 0        
1165             (($iType == OLE::Storage_Lite::PpsType_Root()) or ($iType == OLE::Storage_Lite::PpsType_Dir()))?
1166             OLEDate2Local(substr($sWk, 0x64, 8)) : undef ,
1167             my @raTime2nd =
1168             (($iType == OLE::Storage_Lite::PpsType_Root()) or ($iType == OLE::Storage_Lite::PpsType_Dir()))?
1169             OLEDate2Local(substr($sWk, 0x6C, 8)) : undef,
1170             my($iStart, $iSize) = unpack("VV", substr($sWk, 0x74, 8));
1171 0 0       0 if($bData) {
1172 0         0 my $sData = _getData($iType, $iStart, $iSize, $rhInfo);
1173 0         0 return OLE::Storage_Lite::PPS->new(
1174             $iPos, $sNm, $iType, $lPpsPrev, $lPpsNext, $lDirPps,
1175             \@raTime1st, \@raTime2nd, $iStart, $iSize, $sData, undef);
1176             }
1177             else {
1178 0         0 return OLE::Storage_Lite::PPS->new(
1179             $iPos, $sNm, $iType, $lPpsPrev, $lPpsNext, $lDirPps,
1180             \@raTime1st, \@raTime2nd, $iStart, $iSize, undef, undef);
1181             }
1182             }
1183             #------------------------------------------------------------------------------
1184             # _getNthBlockNo (OLE::Storage_Lite)
1185             #------------------------------------------------------------------------------
1186             sub _getNthBlockNo($$$){
1187 0     0   0 my($iBlockNo, $iNth, $rhInfo) = @_;
1188 0         0 my $rhBbdInfo = $rhInfo->{_BBD_INFO};
1189 0         0 for(1..$iNth) {
1190 0   0     0 $iBlockNo = $rhBbdInfo->{$iBlockNo} // $iBlockNo+1;
1191 0 0       0 return undef unless $iBlockNo < OLE::Storage_Lite::NormalBlockEnd();
1192             }
1193 0         0 return $iBlockNo;
1194             }
1195             #------------------------------------------------------------------------------
1196             # _getData (OLE::Storage_Lite)
1197             #------------------------------------------------------------------------------
1198             sub _getData($$$$)
1199             {
1200 0     0   0 my($iType, $iBlock, $iSize, $rhInfo) = @_;
1201 0 0       0 if ($iType == OLE::Storage_Lite::PpsType_File()) {
    0          
    0          
1202 0 0       0 if($iSize < OLE::Storage_Lite::DataSizeSmall()) {
1203 0         0 return _getSmallData($iBlock, $iSize, $rhInfo);
1204             }
1205             else {
1206 0         0 return _getBigData($iBlock, $iSize, $rhInfo);
1207             }
1208             }
1209             elsif($iType == OLE::Storage_Lite::PpsType_Root()) { #Root
1210 0         0 return _getBigData($iBlock, $iSize, $rhInfo);
1211             }
1212             elsif($iType == OLE::Storage_Lite::PpsType_Dir()) { # Directory
1213 0         0 return undef;
1214             }
1215             }
1216             #------------------------------------------------------------------------------
1217             # _getBigData (OLE::Storage_Lite)
1218             #------------------------------------------------------------------------------
1219             sub _getBigData($$$)
1220             {
1221 0     0   0 my($iBlock, $iSize, $rhInfo) = @_;
1222 0         0 my($iRest, $sWk, $sRes);
1223              
1224 0 0       0 return '' unless($iBlock < OLE::Storage_Lite::NormalBlockEnd());
1225 0         0 $iRest = $iSize;
1226 0         0 my($i, $iGetSize, $iNext);
1227 0         0 $sRes = '';
1228 0         0 my $aKeys= $rhInfo->{_BBD_INFO_SORTED};
1229              
1230 0         0 while ($iRest > 0) {
1231             # lower_bound binary search
1232 0         0 my $iCount = @$aKeys;
1233 0         0 my $iFirst = 0;
1234 0         0 while ($iCount > 0) {
1235 0         0 my $iStep = $iCount >> 1;
1236 0         0 my $iIndex = $iFirst + $iStep;
1237 0 0       0 if ($$aKeys[$iIndex] < $iBlock) {
1238 0         0 $iFirst = ++$iIndex;
1239 0         0 $iCount -= $iStep + 1;
1240             } else {
1241 0         0 $iCount = $iStep;
1242             }
1243             }
1244 0         0 my $iNKey = $$aKeys[$iFirst];
1245 0         0 $i = $iNKey - $iBlock;
1246 0 0       0 croak "Invalid block read" if ($i < 0);
1247 0         0 $iNext = $rhInfo->{_BBD_INFO}{$iNKey};
1248 0         0 $rhInfo->{_FILEH_}->seek(($iBlock+1)*$rhInfo->{_BIG_BLOCK_SIZE}, 0);
1249 0         0 my $iGetSize = ($rhInfo->{_BIG_BLOCK_SIZE} * ($i+1));
1250 0 0       0 $iGetSize = $iRest if($iRest < $iGetSize);
1251 0         0 $rhInfo->{_FILEH_}->read( $sWk, $iGetSize);
1252 0         0 $sRes .= $sWk;
1253 0         0 $iRest -= $iGetSize;
1254 0         0 $iBlock= $iNext;
1255             }
1256 0         0 return $sRes;
1257             }
1258             #------------------------------------------------------------------------------
1259             # _getSmallData (OLE::Storage_Lite)
1260             #------------------------------------------------------------------------------
1261             sub _getSmallData($$$)
1262             {
1263 0     0   0 my($iSmBlock, $iSize, $rhInfo) = @_;
1264 0         0 my($sRes, $sWk);
1265             my($iBigBlkSize, $iSmallBlkSize, $rhFd) =
1266 0         0 @$rhInfo{qw(_BIG_BLOCK_SIZE _SMALL_BLOCK_SIZE _FILEH_)};
1267              
1268 0         0 $sRes = '';
1269 0         0 while ($iSize > 0) {
1270 0         0 my $iBaseCnt = $iBigBlkSize / $iSmallBlkSize;
1271 0         0 my $iNth = int($iSmBlock/$iBaseCnt);
1272 0         0 my $iPos = $iSmBlock % $iBaseCnt;
1273             my $iBlk = $rhInfo->{_BBD_SB_START}->[$iNth] //
1274 0   0     0 ((_getNthBlockNo($rhInfo->{_SB_START}, $iNth, $rhInfo)+1)*$iBigBlkSize);
1275              
1276 0         0 $rhFd->seek($iBlk+($iPos*$iSmallBlkSize), 0);
1277 0 0       0 if ($iSize > $iSmallBlkSize) {
1278 0         0 $rhFd->read($sWk, $iSmallBlkSize);
1279 0         0 $sRes .= $sWk;
1280 0         0 $iSize -= $iSmallBlkSize;
1281             } else {
1282 0         0 $rhFd->read($sWk, $iSize);
1283 0         0 $sRes .= $sWk;
1284 0         0 last;
1285             }
1286             # get next small block
1287 0         0 $iBaseCnt = $iBigBlkSize / OLE::Storage_Lite::LongIntSize();
1288 0         0 $iNth = int($iSmBlock/$iBaseCnt);
1289 0         0 $iPos = $iSmBlock % $iBaseCnt;
1290             $iBlk = $rhInfo->{_BBD_SBD_START}->[$iNth] //
1291 0   0     0 ((_getNthBlockNo($rhInfo->{_SBD_START}, $iNth, $rhInfo)+1)*$iBigBlkSize);
1292 0         0 $rhFd->seek($iBlk+($iPos*OLE::Storage_Lite::LongIntSize()), 0);
1293 0         0 $rhFd->read($sWk, OLE::Storage_Lite::LongIntSize());
1294 0         0 $iSmBlock = unpack("V", $sWk);
1295             }
1296 0         0 return $sRes;
1297             }
1298             #------------------------------------------------------------------------------
1299             # Asc2Ucs: OLE::Storage_Lite
1300             #------------------------------------------------------------------------------
1301             sub Asc2Ucs($)
1302             {
1303 0     0 1 0 return join("\x00", split //, $_[0]) . "\x00";
1304             }
1305             #------------------------------------------------------------------------------
1306             # Ucs2Asc: OLE::Storage_Lite
1307             #------------------------------------------------------------------------------
1308             sub Ucs2Asc($)
1309             {
1310 0     0 1 0 return pack('c*', unpack('v*', $_[0]));
1311             }
1312              
1313             #------------------------------------------------------------------------------
1314             # OLEDate2Local()
1315             #
1316             # Convert from a Window FILETIME structure to a localtime array. FILETIME is
1317             # a 64-bit value representing the number of 100-nanosecond intervals since
1318             # January 1 1601.
1319             #
1320             # We first convert the FILETIME to seconds and then subtract the difference
1321             # between the 1601 epoch and the 1970 Unix epoch.
1322             #
1323             sub OLEDate2Local {
1324              
1325 99     99 0 83424 my $oletime = shift;
1326              
1327             # Unpack the FILETIME into high and low longs.
1328 99         353 my ( $lo, $hi ) = unpack 'V2', $oletime;
1329              
1330             # Convert the longs to a double.
1331 99         218 my $nanoseconds = $hi * 2**32 + $lo;
1332              
1333             # Convert the 100 nanosecond units into seconds.
1334 99         191 my $time = $nanoseconds / 1e7;
1335              
1336             # Subtract the number of seconds between the 1601 and 1970 epochs.
1337 99         186 $time -= 11644473600;
1338              
1339             # Convert to a localtime (actually gmtime) structure.
1340 99         356 my @localtime = gmtime($time);
1341              
1342 99         380 return @localtime;
1343             }
1344              
1345             #------------------------------------------------------------------------------
1346             # LocalDate2OLE()
1347             #
1348             # Convert from a localtime array to a Window FILETIME structure. FILETIME is
1349             # a 64-bit value representing the number of 100-nanosecond intervals since
1350             # January 1 1601.
1351             #
1352             # We first convert the localtime (actually gmtime) to seconds and then add the
1353             # difference between the 1601 epoch and the 1970 Unix epoch. We convert that to
1354             # 100 nanosecond units, divide it into high and low longs and return it as a
1355             # packed 64bit structure.
1356             #
1357             sub LocalDate2OLE {
1358              
1359 99     99 0 408562 my $localtime = shift;
1360              
1361 99 50       356 return "\x00" x 8 unless $localtime;
1362              
1363             # Convert from localtime (actually gmtime) to seconds.
1364 99         171 my @localtime_copy = @{$localtime};
  99         332  
1365 99 100       361 $localtime_copy[5] += 1900 unless $localtime_copy[5] > 99;
1366 99         431 my $time = timegm( @localtime_copy );
1367              
1368             # Add the number of seconds between the 1601 and 1970 epochs.
1369 99         4840 $time += 11644473600;
1370              
1371             # The FILETIME seconds are in units of 100 nanoseconds.
1372 99         223 my $nanoseconds = $time * 1E7;
1373              
1374 2     2   1075 use POSIX 'fmod';
  2         14579  
  2         20  
1375              
1376             # Pack the total nanoseconds into 64 bits...
1377 99         207 my $hi = int( $nanoseconds / 2**32 );
1378 99         471 my $lo = fmod($nanoseconds, 2**32);
1379              
1380 99         296 my $oletime = pack "VV", $lo, $hi;
1381              
1382 99         436 return $oletime;
1383             }
1384              
1385             1;
1386             __END__