1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_filter.hxx"
26
27 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
28
29 #include <string.h> // memset(), ...
30 #ifndef UNX
31 #include <io.h> // access()
32 #endif
33 #include <osl/endian.h>
34 #include <rtl/tencinfo.h> //rtl_getTextEncodingFromWindowsCodePage
35 #include "msvbasic.hxx"
36
37 #include <com/sun/star/script/ModuleType.hpp>
38
39 using namespace ::com::sun::star::script;
40
41 /*
42 A few urls which may in the future be of some use
43 http://www.virusbtn.com/vb2000/Programme/papers/bontchev.pdf
44 */
45
46 /* class VBA_Impl:
47 * The VBA class provides a set of methods to handle Visual Basic For
48 * Applications streams, the constructor is given the root ole2 stream
49 * of the document, Open reads the VBA project file and figures out
50 * the number of VBA streams, and the offset of the data within them.
51 * Decompress decompresses a particular numbered stream, NoStreams returns
52 * this number, and StreamName can give you the streams name. Decompress
53 * will call Output when it has a 4096 byte collection of data to output,
54 * and also with the final remainder of data if there is still some left
55 * at the end of compression. Output is virtual to allow custom handling
56 * of each chunk of decompressed data. So inherit from this to do something
57 * useful with the data.
58 *
59 * cmc
60 * */
61 const int MINVBASTRING = 6;
62
VBA_Impl(SvStorage & rIn,bool bCmmntd)63 VBA_Impl::VBA_Impl(SvStorage &rIn, bool bCmmntd)
64 : aVBAStrings(0),
65 sComment(RTL_CONSTASCII_USTRINGPARAM("Rem ")),
66 xStor(&rIn), pOffsets(0), nOffsets(0), meCharSet(RTL_TEXTENCODING_MS_1252),
67 bCommented(bCmmntd), mbMac(false), nLines(0)
68 {
69 }
70
~VBA_Impl()71 VBA_Impl::~VBA_Impl()
72 {
73 delete [] pOffsets;
74 for (sal_uLong i=0;i<aVBAStrings.GetSize();++i)
75 delete aVBAStrings.Get(i);
76 }
77
ReadPString(SvStorageStreamRef & xVBAProject,bool bIsUnicode)78 sal_uInt8 VBA_Impl::ReadPString(SvStorageStreamRef &xVBAProject,
79 bool bIsUnicode)
80 {
81 sal_uInt16 nIdLen, nOut16;
82 sal_uInt8 nType = 0, nOut8;
83 String sReference;
84
85 *xVBAProject >> nIdLen;
86
87 if (nIdLen < MINVBASTRING) //Error recovery
88 xVBAProject->SeekRel(-2); //undo 2 byte len
89 else
90 {
91 for(sal_uInt16 i=0; i < nIdLen / (bIsUnicode ? 2 : 1); i++)
92 {
93 if (bIsUnicode)
94 *xVBAProject >> nOut16;
95 else
96 {
97 *xVBAProject >> nOut8;
98 nOut16 = nOut8;
99 }
100 sReference += nOut16;
101 if (i==2)
102 {
103 if ((nOut16 == 'G') || (nOut16 == 'H') || (nOut16 == 'C') ||
104 nOut16 == 'D')
105 {
106 nType = static_cast<sal_uInt8>(nOut16);
107 }
108 if (nType == 0)
109 {
110 //Error recovery, 2byte len + 3 characters of used type
111 xVBAProject->SeekRel(-(2 + 3 * (bIsUnicode ? 2 : 1)));
112 break;
113 }
114 }
115 }
116 maReferences.push_back(sReference);
117 }
118 return nType;
119 }
120
Output(int nLen,const sal_uInt8 * pData)121 void VBA_Impl::Output( int nLen, const sal_uInt8*pData )
122 {
123 /*
124 Each StarBasic module is tragically limited to the maximum len of a
125 string and WordBasic is not, so each overlarge module must be split
126 */
127 String sTemp((const sal_Char *)pData, (xub_StrLen)nLen,
128 meCharSet);
129 int nTmp = sTemp.GetTokenCount('\x0D');
130 int nIndex = aVBAStrings.GetSize()-1;
131 if (aVBAStrings.Get(nIndex)->Len() +
132 nLen + ((nLines+nTmp) * sComment.Len()) >= STRING_MAXLEN)
133 {
134 //DBG_ASSERT(0,"New Module String\n");
135 //we are too large for our boots, break out into another
136 //string
137 nLines=0;
138 nIndex++;
139 aVBAStrings.SetSize(nIndex+1);
140 aVBAStrings.Put(nIndex,new String);
141 }
142 *(aVBAStrings.Get(nIndex)) += sTemp;
143 nLines+=nTmp;
144 }
145
146
ReadVBAProject(const SvStorageRef & rxVBAStorage)147 int VBA_Impl::ReadVBAProject(const SvStorageRef &rxVBAStorage)
148 {
149 SvStorageStreamRef xVBAProject;
150 xVBAProject = rxVBAStorage->OpenSotStream(
151 String( RTL_CONSTASCII_USTRINGPARAM( "_VBA_PROJECT" ) ),
152 STREAM_STD_READ | STREAM_NOCREATE );
153
154 if( !xVBAProject.Is() || SVSTREAM_OK != xVBAProject->GetError() )
155 {
156 DBG_WARNING("Not able to find vba project, cannot find macros");
157 return 0;
158 }
159
160 static const sal_uInt8 aKnownId[] = {0xCC, 0x61};
161 sal_uInt8 aId[2];
162 xVBAProject->Read( aId, sizeof(aId) );
163 if (memcmp( aId, aKnownId, sizeof(aId)))
164 {
165 DBG_WARNING("unrecognized VBA macro project type");
166 return 0;
167 }
168
169 static const sal_uInt8 aOffice2007LE[] = { 0x88, 0x00, 0x00, 0x01, 0x00, 0xFF };
170 static const sal_uInt8 aOffice2003LE_2[] = { 0x79, 0x00, 0x00, 0x01, 0x00, 0xFF };
171 static const sal_uInt8 aOffice2003LE[] = { 0x76, 0x00, 0x00, 0x01, 0x00, 0xFF };
172 static const sal_uInt8 aOfficeXPLE[] = { 0x73, 0x00, 0x00, 0x01, 0x00, 0xFF };
173 static const sal_uInt8 aOfficeXPBE[] = { 0x63, 0x00, 0x00, 0x0E, 0x00, 0xFF };
174 static const sal_uInt8 aOffice2000LE[] = { 0x6D, 0x00, 0x00, 0x01, 0x00, 0xFF };
175 static const sal_uInt8 aOffice98BE[] = { 0x60, 0x00, 0x00, 0x0E, 0x00, 0xFF };
176 static const sal_uInt8 aOffice97LE[] = { 0x5E, 0x00, 0x00, 0x01, 0x00, 0xFF };
177
178 sal_uInt8 aProduct[6];
179 xVBAProject->Read( aProduct, sizeof(aProduct) );
180
181 bool bIsUnicode;
182 if (!(memcmp(aProduct, aOffice2007LE, sizeof(aProduct))) ||
183 !(memcmp(aProduct, aOffice2003LE, sizeof(aProduct))) ||
184 !(memcmp(aProduct, aOffice2003LE_2, sizeof(aProduct))) ||
185 !(memcmp(aProduct, aOfficeXPLE, sizeof(aProduct))) ||
186 !(memcmp(aProduct, aOffice2000LE, sizeof(aProduct))) ||
187 !(memcmp(aProduct, aOffice97LE, sizeof(aProduct))) )
188 {
189 xVBAProject->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
190 bIsUnicode = true;
191 }
192 else if (!(memcmp(aProduct, aOfficeXPBE, sizeof(aProduct))) ||
193 !(memcmp(aProduct, aOffice98BE, sizeof(aProduct))) )
194 {
195 xVBAProject->SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
196 mbMac = true;
197 bIsUnicode = false;
198 }
199 else
200 {
201 switch (aProduct[3])
202 {
203 case 0x1:
204 xVBAProject->SetNumberFormatInt(NUMBERFORMAT_INT_LITTLEENDIAN);
205 bIsUnicode = true;
206 DBG_ASSERT(!this, "unrecognized VBA macro version, report to cmc. Guessing at unicode little endian");
207 break;
208 case 0xe:
209 xVBAProject->SetNumberFormatInt(NUMBERFORMAT_INT_BIGENDIAN);
210 mbMac = true;
211 bIsUnicode = false;
212 DBG_ASSERT(!this, "unrecognized VBA macro version, report to cmc. Guessing at 8bit big endian");
213 break;
214 default:
215 DBG_ASSERT(!this, "totally unrecognized VBA macro version, report to cmc");
216 return 0;
217 }
218 }
219
220 sal_uInt32 nLidA; //Language identifiers
221 sal_uInt32 nLidB;
222 sal_uInt16 nCharSet;
223 sal_uInt16 nLenA;
224 sal_uInt32 nUnknownB;
225 sal_uInt32 nUnknownC;
226 sal_uInt16 nLenB;
227 sal_uInt16 nLenC;
228 sal_uInt16 nLenD;
229
230 *xVBAProject >> nLidA >> nLidB >> nCharSet >> nLenA >> nUnknownB;
231 *xVBAProject >> nUnknownC >> nLenB >> nLenC >> nLenD;
232
233 meCharSet = rtl_getTextEncodingFromWindowsCodePage(nCharSet);
234
235 DBG_ASSERT(meCharSet != RTL_TEXTENCODING_DONTKNOW,
236 "don't know what vba charset to use");
237 if (meCharSet == RTL_TEXTENCODING_DONTKNOW)
238 meCharSet = RTL_TEXTENCODING_MS_1252;
239
240 if (nLenD != 0x02)
241 {
242 DBG_WARNING("Warning VBA number is different, please report");
243 return 0;
244 }
245
246 /*
247 A sequence of string that are prepended with a len and then begin with G
248 or H, there are also those that begin with C or D. If a string begins with
249 C or D, it is really two strings, one right after the other. Each string
250 then has a 12 bytes suffix
251
252 Recognizing the end of the sequence is done by finding a str len of < 6
253 which does not appear to be the beginning of an object id. Admittedly this
254 isn't a great test, but nothing in the header appears to count the number
255 of strings, and nothing else seems to match. So it'll have to do, its
256 protected by a number of secondry tests to prove its a valid string, and
257 everything gives up if this isn't proven.
258 */
259 bool bPredictsTrailingTwenty = false;
260 while (1)
261 {
262 sal_uInt8 nType = ReadPString(xVBAProject,bIsUnicode);
263 //Type C and D seem to come as pairs, so skip the following one
264 if (nType == 'C' || nType == 'D')
265 {
266 nType = ReadPString(xVBAProject,bIsUnicode);
267 DBG_ASSERT( nType == 'C' || nType == 'D',
268 "VBA: This must be a 'C' or 'D' string!" );
269 if (nType != 'C' && nType != 'D')
270 return 0;
271 }
272 if (!nType)
273 break;
274 xVBAProject->SeekRel(10);
275 sal_uInt16 nPredictsTrailingTwenty;
276 *xVBAProject >> nPredictsTrailingTwenty;
277 if (nPredictsTrailingTwenty)
278 bPredictsTrailingTwenty = true;
279 if (bPredictsTrailingTwenty)
280 {
281 sal_uInt16 nTestIsNotString;
282 *xVBAProject >> nTestIsNotString;
283 if (nTestIsNotString < MINVBASTRING)
284 {
285 DBG_ASSERT(nTestIsNotString <= 1,
286 "Haven't seen a len like this in VBA, report to CMC");
287 xVBAProject->SeekRel(18);
288 bPredictsTrailingTwenty = false;
289 }
290 else
291 xVBAProject->SeekRel(-2);
292 }
293 }
294
295 sal_Int16 nInt16s;
296 *xVBAProject >> nInt16s;
297 DBG_ASSERT( nInt16s >= 0, "VBA: Bad no of records in VBA Project, panic!" );
298 if (!nInt16s)
299 return 0;
300
301 xVBAProject->SeekRel(2*nInt16s);
302
303 sal_Int16 nInt32s;
304 *xVBAProject >> nInt32s;
305 DBG_ASSERT( nInt32s >= 0, "VBA: Bad no of records in VBA Project, panic!" );
306 if (!nInt32s)
307 return 0;
308 xVBAProject->SeekRel(4*nInt32s);
309
310 xVBAProject->SeekRel(2);
311 for(int k=0;k<3;k++)
312 {
313 sal_uInt16 nLen;
314 *xVBAProject >> nLen;
315 if (nLen != 0xFFFF)
316 xVBAProject->SeekRel(nLen);
317 }
318 xVBAProject->SeekRel(100); //Seems fixed len
319
320 *xVBAProject >> nOffsets;
321 DBG_ASSERT( nOffsets != 0xFFFF, "VBA: Bad nOffsets, panic!!" );
322 if ((nOffsets == 0xFFFF) || (nOffsets == 0))
323 return 0;
324 pOffsets = new VBAOffset_Impl[ nOffsets ];
325
326 int i, j;
327 for( i=0; i < nOffsets; i++)
328 {
329 sal_uInt16 nLen;
330 *xVBAProject >> nLen;
331
332 if (bIsUnicode)
333 {
334 sal_Unicode* pBuf = pOffsets[i].sName.AllocBuffer( nLen / 2 );
335 xVBAProject->Read( (sal_Char*)pBuf, nLen );
336
337 #ifdef OSL_BIGENDIAN
338 for( j = 0; j < nLen / 2; ++j, ++pBuf )
339 *pBuf = SWAPSHORT( *pBuf );
340 #endif // ifdef OSL_BIGENDIAN
341 }
342 else
343 {
344 ByteString aByteStr;
345 sal_Char* pByteData = aByteStr.AllocBuffer( nLen );
346 sal_Size nWasRead = xVBAProject->Read( pByteData, nLen );
347 if( nWasRead != nLen )
348 aByteStr.ReleaseBufferAccess();
349 pOffsets[i].sName += String( aByteStr, meCharSet);
350 }
351
352 *xVBAProject >> nLen;
353 xVBAProject->SeekRel( nLen );
354
355 //begin section, another problem area
356 *xVBAProject >> nLen;
357 if ( nLen == 0xFFFF)
358 {
359 xVBAProject->SeekRel(2);
360 *xVBAProject >> nLen;
361 xVBAProject->SeekRel( nLen );
362 }
363 else
364 xVBAProject->SeekRel( nLen+2 );
365
366 *xVBAProject >> nLen;
367 DBG_ASSERT( nLen == 0xFFFF, "VBA: Bad field in VBA Project, panic!!" );
368 if ( nLen != 0xFFFF)
369 return 0;
370
371 xVBAProject->SeekRel(6);
372 sal_uInt16 nOctects;
373 *xVBAProject >> nOctects;
374 for(j=0;j<nOctects;j++)
375 xVBAProject->SeekRel(8);
376
377 xVBAProject->SeekRel(5);
378 //end section
379
380 *xVBAProject >> pOffsets[i].nOffset;
381 xVBAProject->SeekRel(2);
382 }
383
384 return nOffsets;
385 }
386
387
388 /* #117718# For a given Module name return its type,
389 * Form, Class, Document, Normal or Unknown
390 *
391 */
392
GetModuleType(const UniString & rModuleName)393 ModType VBA_Impl::GetModuleType( const UniString& rModuleName )
394 {
395 ModuleTypeHash::iterator iter = mhModHash.find( rModuleName );
396 ModuleTypeHash::iterator iterEnd = mhModHash.end();
397 if ( iter != iterEnd )
398 {
399 return iter->second;
400 }
401 return ModuleType::UNKNOWN;
402 }
403
Open(const String & rToplevel,const String & rSublevel)404 bool VBA_Impl::Open( const String &rToplevel, const String &rSublevel )
405 {
406 /* beginning test for vba stuff */
407 bool bRet = false;
408 SvStorageRef xMacros= xStor->OpenSotStorage( rToplevel,
409 STREAM_READWRITE | STREAM_NOCREATE |
410 STREAM_SHARE_DENYALL );
411 if( !xMacros.Is() || SVSTREAM_OK != xMacros->GetError() )
412 {
413 DBG_WARNING("No Macros Storage");
414 }
415 else
416 {
417 xVBA = xMacros->OpenSotStorage( rSublevel,
418 STREAM_READWRITE | STREAM_NOCREATE |
419 STREAM_SHARE_DENYALL );
420 if( !xVBA.Is() || SVSTREAM_OK != xVBA->GetError() )
421 {
422 DBG_WARNING("No Visual Basic in Storage");
423 }
424 else
425 {
426 if (ReadVBAProject(xVBA))
427 bRet = true;
428 }
429 /* #117718#
430 * Information regarding the type of module is contained in the
431 * "PROJECT" stream, this stream consists of a number of ascii lines
432 * entries are of the form Key=Value, the ones that we are interested
433 * in have the keys; Class, BaseClass & Module indicating the module
434 * ( value ) is either a Class Module, Form Module or a plain VB Module. */
435 SvStorageStreamRef xProject = xMacros->OpenSotStream(
436 String( RTL_CONSTASCII_USTRINGPARAM( "PROJECT" ) ) );
437 SvStorageStream* pStp = xProject;
438 UniString tmp;
439 static const String sThisDoc( RTL_CONSTASCII_USTRINGPARAM( "ThisDocument" ) );
440 static const String sModule( RTL_CONSTASCII_USTRINGPARAM( "Module" ) );
441 static const String sClass( RTL_CONSTASCII_USTRINGPARAM( "Class" ) );
442 static const String sBaseClass( RTL_CONSTASCII_USTRINGPARAM( "BaseClass" ) );
443 static const String sDocument( RTL_CONSTASCII_USTRINGPARAM( "Document" ) );
444 mhModHash[ sThisDoc ] = ModuleType::CLASS;
445 while ( pStp->ReadByteStringLine( tmp, meCharSet ) )
446 {
447 xub_StrLen index = tmp.Search( '=' );
448 if ( index != STRING_NOTFOUND )
449 {
450 String key = tmp.Copy( 0, index );
451 String value = tmp.Copy( index + 1 );
452 if ( key == sClass )
453 {
454 mhModHash[ value ] = ModuleType::CLASS;
455 OSL_TRACE("Module %s is of type Class",
456 ::rtl::OUStringToOString( value ,
457 RTL_TEXTENCODING_ASCII_US ).pData->buffer );
458 }
459 else if ( key == sBaseClass )
460 {
461 mhModHash[ value ] = ModuleType::FORM;
462 OSL_TRACE("Module %s is of type Form",
463 ::rtl::OUStringToOString( value ,
464 RTL_TEXTENCODING_ASCII_US ).pData->buffer );
465 }
466 else if ( key == sDocument )
467 {
468 /* #i37965# DR 2004-12-03: add "Document", used i.e.
469 in Excel for macros attached to sheet or document. */
470
471 // value is of form <name>/&H<identifier>, strip the identifier
472 value.Erase( value.Search( '/' ) );
473
474 mhModHash[ value ] = ModuleType::DOCUMENT;
475 OSL_TRACE("Module %s is of type Document VBA",
476 ::rtl::OUStringToOString( value ,
477 RTL_TEXTENCODING_ASCII_US ).pData->buffer );
478 }
479 else if ( key == sModule )
480 {
481 mhModHash[ value ] = ModuleType::NORMAL;
482 OSL_TRACE("Module %s is of type Normal VBA",
483 ::rtl::OUStringToOString( value ,
484 RTL_TEXTENCODING_ASCII_US ).pData->buffer );
485 }
486 }
487 }
488 }
489 /* end test for vba stuff */
490 return bRet;
491 }
492
Decompress(sal_uInt16 nIndex,int * pOverflow)493 const StringArray &VBA_Impl::Decompress(sal_uInt16 nIndex, int *pOverflow)
494 {
495 DBG_ASSERT( nIndex < nOffsets, "Index out of range" );
496 SvStorageStreamRef xVBAStream;
497 aVBAStrings.SetSize(1);
498 aVBAStrings.Put(0,new String);
499
500 xVBAStream = xVBA->OpenSotStream( pOffsets[nIndex].sName,
501 STREAM_STD_READ | STREAM_NOCREATE );
502 if (pOverflow)
503 *pOverflow=0;
504
505 if( !xVBAStream.Is() || SVSTREAM_OK != xVBAStream->GetError() )
506 {
507 DBG_WARNING("Not able to open vb module ");
508 }
509 else
510 {
511 xVBAStream->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
512 DecompressVBA( nIndex, xVBAStream );
513 /*
514 * if len was too big for a single string set that variable ?
515 * if ((len > XX) && (pOverflow))
516 *pOverflow=1;
517 */
518 if (bCommented)
519 {
520 String sTempStringa;
521 if (mbMac)
522 sTempStringa = String( RTL_CONSTASCII_USTRINGPARAM( "\x0D" ) );
523 else
524 sTempStringa = String( RTL_CONSTASCII_USTRINGPARAM( "\x0D\x0A" ) );
525 String sTempStringb(sTempStringa);
526 sTempStringb+=sComment;
527 for(sal_uLong i=0;i<aVBAStrings.GetSize();i++)
528 {
529 aVBAStrings.Get(i)->SearchAndReplaceAll(
530 sTempStringa,sTempStringb);
531 aVBAStrings.Get(i)->Insert(sComment,0);
532 }
533 }
534 }
535 return aVBAStrings;
536 }
537
538
DecompressVBA(int nIndex,SvStorageStreamRef & xVBAStream)539 int VBA_Impl::DecompressVBA( int nIndex, SvStorageStreamRef &xVBAStream )
540 {
541 sal_uInt8 nLeadbyte;
542 sal_uInt16 nToken;
543 unsigned int nPos = 0;
544 int nLen, nDistance, nShift, nClean=1;
545
546 xVBAStream->Seek( pOffsets[ nIndex ].nOffset + 3 );
547
548 while(xVBAStream->Read(&nLeadbyte,1))
549 {
550 for(int nPosition=0x01;nPosition < 0x100;nPosition=nPosition<<1)
551 {
552 //we see if the leadbyte has flagged this location as a dataunit
553 //which is actually a token which must be looked up in the history
554 if (nLeadbyte & nPosition)
555 {
556 *xVBAStream >> nToken;
557
558 if (nClean == 0)
559 nClean=1;
560
561 //For some reason the division of the token into the length
562 //field of the data to be inserted, and the distance back into
563 //the history differs depending on how full the history is
564 int nPos2 = nPos % nWINDOWLEN;
565 if (nPos2 <= 0x10)
566 nShift = 12;
567 else if (nPos2 <= 0x20)
568 nShift = 11;
569 else if (nPos2 <= 0x40)
570 nShift = 10;
571 else if (nPos2 <= 0x80)
572 nShift = 9;
573 else if (nPos2 <= 0x100)
574 nShift = 8;
575 else if (nPos2 <= 0x200)
576 nShift = 7;
577 else if (nPos2 <= 0x400)
578 nShift = 6;
579 else if (nPos2 <= 0x800)
580 nShift = 5;
581 else
582 nShift = 4;
583
584 int i;
585 nLen=0;
586 for(i=0;i<nShift;i++)
587 nLen |= nToken & (1<<i);
588
589 nLen += 3;
590
591 nDistance = nToken >> nShift;
592
593 //read the len of data from the history, wrapping around the
594 //nWINDOWLEN boundary if necessary data read from the history
595 //is also copied into the recent part of the history as well.
596 for (i = 0; i < nLen; i++)
597 {
598 unsigned char c;
599 c = aHistory[(nPos-nDistance-1) % nWINDOWLEN];
600 aHistory[nPos % nWINDOWLEN] = c;
601 nPos++;
602 }
603 }
604 else
605 {
606 // special boundary case code, not guarantueed to be correct
607 // seems to work though, there is something wrong with the
608 // compression scheme (or maybe a feature) where when the data
609 // ends on a nWINDOWLEN boundary and the excess bytes in the 8
610 // dataunit list are discarded, and not interpreted as tokens
611 // or normal data.
612 if ((nPos != 0) && ((nPos % nWINDOWLEN) == 0) && (nClean))
613 {
614 xVBAStream->SeekRel(2);
615 nClean=0;
616 Output(nWINDOWLEN, aHistory);
617 break;
618 }
619 //This is the normal case for when the data unit is not a
620 //token to be looked up, but instead some normal data which
621 //can be output, and placed in the history.
622 if (xVBAStream->Read(&aHistory[nPos % nWINDOWLEN],1))
623 nPos++;
624
625 if (nClean == 0)
626 nClean=1;
627 }
628 }
629 }
630 if (nPos % nWINDOWLEN)
631 Output(nPos % nWINDOWLEN,aHistory);
632 return(nPos);
633 }
634
635 /* vi:set tabstop=4 shiftwidth=4 expandtab: */
636