| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_filter.hxx" |
| |
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ |
| |
| #include <string.h> // memset(), ... |
| #ifndef UNX |
| #include <io.h> // access() |
| #endif |
| #include <osl/endian.h> |
| #include <rtl/tencinfo.h> //rtl_getTextEncodingFromWindowsCodePage |
| #include "msvbasic.hxx" |
| |
| #include <com/sun/star/script/ModuleType.hpp> |
| |
| using namespace ::com::sun::star::script; |
| |
| /* |
| A few urls which may in the future be of some use |
| http://www.virusbtn.com/vb2000/Programme/papers/bontchev.pdf |
| */ |
| |
| /* class VBA_Impl: |
| * The VBA class provides a set of methods to handle Visual Basic For |
| * Applications streams, the constructor is given the root ole2 stream |
| * of the document, Open reads the VBA project file and figures out |
| * the number of VBA streams, and the offset of the data within them. |
| * Decompress decompresses a particular numbered stream, NoStreams returns |
| * this number, and StreamName can give you the streams name. Decompress |
| * will call Output when it has a 4096 byte collection of data to output, |
| * and also with the final remainder of data if there is still some left |
| * at the end of compression. Output is virtual to allow custom handling |
| * of each chunk of decompressed data. So inherit from this to do something |
| * useful with the data. |
| * |
| * cmc |
| * */ |
| const int MINVBASTRING = 6; |
| |
| VBA_Impl::VBA_Impl(SvStorage &rIn, bool bCmmntd) |
| : aVBAStrings(0), |
| sComment(RTL_CONSTASCII_USTRINGPARAM("Rem ")), |
| xStor(&rIn), pOffsets(0), nOffsets(0), meCharSet(RTL_TEXTENCODING_MS_1252), |
| bCommented(bCmmntd), mbMac(false), nLines(0) |
| { |
| } |
| |
| VBA_Impl::~VBA_Impl() |
| { |
| delete [] pOffsets; |
| for (sal_uLong i=0;i<aVBAStrings.GetSize();++i) |
| delete aVBAStrings.Get(i); |
| } |
| |
| sal_uInt8 VBA_Impl::ReadPString(SvStorageStreamRef &xVBAProject, |
| bool bIsUnicode) |
| { |
| sal_uInt16 nIdLen, nOut16; |
| sal_uInt8 nType = 0, nOut8; |
| String sReference; |
| |
| *xVBAProject >> nIdLen; |
| |
| if (nIdLen < MINVBASTRING) //Error recovery |
| xVBAProject->SeekRel(-2); //undo 2 byte len |
| else |
| { |
| for(sal_uInt16 i=0; i < nIdLen / (bIsUnicode ? 2 : 1); i++) |
| { |
| if (bIsUnicode) |
| *xVBAProject >> nOut16; |
| else |
| { |
| *xVBAProject >> nOut8; |
| nOut16 = nOut8; |
| } |
| sReference += nOut16; |
| if (i==2) |
| { |
| if ((nOut16 == 'G') || (nOut16 == 'H') || (nOut16 == 'C') || |
| nOut16 == 'D') |
| { |
| nType = static_cast<sal_uInt8>(nOut16); |
| } |
| if (nType == 0) |
| { |
| //Error recovery, 2byte len + 3 characters of used type |
| xVBAProject->SeekRel(-(2 + 3 * (bIsUnicode ? 2 : 1))); |
| break; |
| } |
| } |
| } |
| maReferences.push_back(sReference); |
| } |
| return nType; |
| } |
| |
| void VBA_Impl::Output( int nLen, const sal_uInt8*pData ) |
| { |
| /* |
| Each StarBasic module is tragically limited to the maximum len of a |
| string and WordBasic is not, so each overlarge module must be split |
| */ |
| String sTemp((const sal_Char *)pData, (xub_StrLen)nLen, |
| meCharSet); |
| int nTmp = sTemp.GetTokenCount('\x0D'); |
| int nIndex = aVBAStrings.GetSize()-1; |
| if (aVBAStrings.Get(nIndex)->Len() + |
| nLen + ((nLines+nTmp) * sComment.Len()) >= STRING_MAXLEN) |
| { |
| //DBG_ASSERT(0,"New Module String\n"); |
| //we are too large for our boots, break out into another |
| //string |
| nLines=0; |
| nIndex++; |
| aVBAStrings.SetSize(nIndex+1); |
| aVBAStrings.Put(nIndex,new String); |
| } |
| *(aVBAStrings.Get(nIndex)) += sTemp; |
| nLines+=nTmp; |
| } |
| |
| |
| int VBA_Impl::ReadVBAProject(const SvStorageRef &rxVBAStorage) |
| { |
| SvStorageStreamRef xVBAProject; |
| xVBAProject = rxVBAStorage->OpenSotStream( |
| String( RTL_CONSTASCII_USTRINGPARAM( "_VBA_PROJECT" ) ), |
| STREAM_STD_READ | STREAM_NOCREATE ); |
| |
| if( !xVBAProject.Is() || SVSTREAM_OK != xVBAProject->GetError() ) |
| { |
| DBG_WARNING("Not able to find vba project, cannot find macros"); |
| return 0; |
| } |
| |
| static const sal_uInt8 aKnownId[] = {0xCC, 0x61}; |
| sal_uInt8 aId[2]; |
| xVBAProject->Read( aId, sizeof(aId) ); |
| if (memcmp( aId, aKnownId, sizeof(aId))) |
| { |
| DBG_WARNING("unrecognized VBA macro project type"); |
| return 0; |
| } |
| |
| static const sal_uInt8 aOffice2007LE[] = { 0x88, 0x00, 0x00, 0x01, 0x00, 0xFF }; |
| static const sal_uInt8 aOffice2003LE_2[] = { 0x79, 0x00, 0x00, 0x01, 0x00, 0xFF }; |
| static const sal_uInt8 aOffice2003LE[] = { 0x76, 0x00, 0x00, 0x01, 0x00, 0xFF }; |
| static const sal_uInt8 aOfficeXPLE[] = { 0x73, 0x00, 0x00, 0x01, 0x00, 0xFF }; |
| static const sal_uInt8 aOfficeXPBE[] = { 0x63, 0x00, 0x00, 0x0E, 0x00, 0xFF }; |
| static const sal_uInt8 aOffice2000LE[] = { 0x6D, 0x00, 0x00, 0x01, 0x00, 0xFF }; |
| static const sal_uInt8 aOffice98BE[] = { 0x60, 0x00, 0x00, 0x0E, 0x00, 0xFF }; |
| static const sal_uInt8 aOffice97LE[] = { 0x5E, 0x00, 0x00, 0x01, 0x00, 0xFF }; |
| |
| sal_uInt8 aProduct[6]; |
| xVBAProject->Read( aProduct, sizeof(aProduct) ); |
| |
| bool bIsUnicode; |
| if (!(memcmp(aProduct, aOffice2007LE, sizeof(aProduct))) || |
| !(memcmp(aProduct, aOffice2003LE, sizeof(aProduct))) || |
| !(memcmp(aProduct, aOffice2003LE_2, sizeof(aProduct))) || |
| !(memcmp(aProduct, aOfficeXPLE, sizeof(aProduct))) || |
| !(memcmp(aProduct, aOffice2000LE, sizeof(aProduct))) || |
| !(memcmp(aProduct, aOffice97LE, sizeof(aProduct))) ) |
| { |
| xVBAProject->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); |
| bIsUnicode = true; |
| } |
| else if (!(memcmp(aProduct, aOfficeXPBE, sizeof(aProduct))) || |
| !(memcmp(aProduct, aOffice98BE, sizeof(aProduct))) ) |
| { |
| xVBAProject->SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN ); |
| mbMac = true; |
| bIsUnicode = false; |
| } |
| else |
| { |
| switch (aProduct[3]) |
| { |
| case 0x1: |
| xVBAProject->SetNumberFormatInt(NUMBERFORMAT_INT_LITTLEENDIAN); |
| bIsUnicode = true; |
| DBG_ASSERT(!this, "unrecognized VBA macro version, report to cmc. Guessing at unicode little endian"); |
| break; |
| case 0xe: |
| xVBAProject->SetNumberFormatInt(NUMBERFORMAT_INT_BIGENDIAN); |
| mbMac = true; |
| bIsUnicode = false; |
| DBG_ASSERT(!this, "unrecognized VBA macro version, report to cmc. Guessing at 8bit big endian"); |
| break; |
| default: |
| DBG_ASSERT(!this, "totally unrecognized VBA macro version, report to cmc"); |
| return 0; |
| } |
| } |
| |
| sal_uInt32 nLidA; //Language identifiers |
| sal_uInt32 nLidB; |
| sal_uInt16 nCharSet; |
| sal_uInt16 nLenA; |
| sal_uInt32 nUnknownB; |
| sal_uInt32 nUnknownC; |
| sal_uInt16 nLenB; |
| sal_uInt16 nLenC; |
| sal_uInt16 nLenD; |
| |
| *xVBAProject >> nLidA >> nLidB >> nCharSet >> nLenA >> nUnknownB; |
| *xVBAProject >> nUnknownC >> nLenB >> nLenC >> nLenD; |
| |
| meCharSet = rtl_getTextEncodingFromWindowsCodePage(nCharSet); |
| |
| DBG_ASSERT(meCharSet != RTL_TEXTENCODING_DONTKNOW, |
| "don't know what vba charset to use"); |
| if (meCharSet == RTL_TEXTENCODING_DONTKNOW) |
| meCharSet = RTL_TEXTENCODING_MS_1252; |
| |
| if (nLenD != 0x02) |
| { |
| DBG_WARNING("Warning VBA number is different, please report"); |
| return 0; |
| } |
| |
| /* |
| A sequence of string that are prepended with a len and then begin with G |
| or H, there are also those that begin with C or D. If a string begins with |
| C or D, it is really two strings, one right after the other. Each string |
| then has a 12 bytes suffix |
| |
| Recognizing the end of the sequence is done by finding a str len of < 6 |
| which does not appear to be the beginning of an object id. Admittedly this |
| isn't a great test, but nothing in the header appears to count the number |
| of strings, and nothing else seems to match. So it'll have to do, its |
| protected by a number of secondry tests to prove its a valid string, and |
| everything gives up if this isn't proven. |
| */ |
| bool bPredictsTrailingTwenty = false; |
| while (1) |
| { |
| sal_uInt8 nType = ReadPString(xVBAProject,bIsUnicode); |
| //Type C and D seem to come as pairs, so skip the following one |
| if (nType == 'C' || nType == 'D') |
| { |
| nType = ReadPString(xVBAProject,bIsUnicode); |
| DBG_ASSERT( nType == 'C' || nType == 'D', |
| "VBA: This must be a 'C' or 'D' string!" ); |
| if (nType != 'C' && nType != 'D') |
| return 0; |
| } |
| if (!nType) |
| break; |
| xVBAProject->SeekRel(10); |
| sal_uInt16 nPredictsTrailingTwenty; |
| *xVBAProject >> nPredictsTrailingTwenty; |
| if (nPredictsTrailingTwenty) |
| bPredictsTrailingTwenty = true; |
| if (bPredictsTrailingTwenty) |
| { |
| sal_uInt16 nTestIsNotString; |
| *xVBAProject >> nTestIsNotString; |
| if (nTestIsNotString < MINVBASTRING) |
| { |
| DBG_ASSERT(nTestIsNotString <= 1, |
| "Haven't seen a len like this in VBA, report to CMC"); |
| xVBAProject->SeekRel(18); |
| bPredictsTrailingTwenty = false; |
| } |
| else |
| xVBAProject->SeekRel(-2); |
| } |
| } |
| |
| sal_Int16 nInt16s; |
| *xVBAProject >> nInt16s; |
| DBG_ASSERT( nInt16s >= 0, "VBA: Bad no of records in VBA Project, panic!" ); |
| if (!nInt16s) |
| return 0; |
| |
| xVBAProject->SeekRel(2*nInt16s); |
| |
| sal_Int16 nInt32s; |
| *xVBAProject >> nInt32s; |
| DBG_ASSERT( nInt32s >= 0, "VBA: Bad no of records in VBA Project, panic!" ); |
| if (!nInt32s) |
| return 0; |
| xVBAProject->SeekRel(4*nInt32s); |
| |
| xVBAProject->SeekRel(2); |
| for(int k=0;k<3;k++) |
| { |
| sal_uInt16 nLen; |
| *xVBAProject >> nLen; |
| if (nLen != 0xFFFF) |
| xVBAProject->SeekRel(nLen); |
| } |
| xVBAProject->SeekRel(100); //Seems fixed len |
| |
| *xVBAProject >> nOffsets; |
| DBG_ASSERT( nOffsets != 0xFFFF, "VBA: Bad nOffsets, panic!!" ); |
| if ((nOffsets == 0xFFFF) || (nOffsets == 0)) |
| return 0; |
| pOffsets = new VBAOffset_Impl[ nOffsets ]; |
| |
| int i, j; |
| for( i=0; i < nOffsets; i++) |
| { |
| sal_uInt16 nLen; |
| *xVBAProject >> nLen; |
| |
| if (bIsUnicode) |
| { |
| sal_Unicode* pBuf = pOffsets[i].sName.AllocBuffer( nLen / 2 ); |
| xVBAProject->Read( (sal_Char*)pBuf, nLen ); |
| |
| #ifdef OSL_BIGENDIAN |
| for( j = 0; j < nLen / 2; ++j, ++pBuf ) |
| *pBuf = SWAPSHORT( *pBuf ); |
| #endif // ifdef OSL_BIGENDIAN |
| } |
| else |
| { |
| ByteString aByteStr; |
| sal_Char* pByteData = aByteStr.AllocBuffer( nLen ); |
| sal_Size nWasRead = xVBAProject->Read( pByteData, nLen ); |
| if( nWasRead != nLen ) |
| aByteStr.ReleaseBufferAccess(); |
| pOffsets[i].sName += String( aByteStr, meCharSet); |
| } |
| |
| *xVBAProject >> nLen; |
| xVBAProject->SeekRel( nLen ); |
| |
| //begin section, another problem area |
| *xVBAProject >> nLen; |
| if ( nLen == 0xFFFF) |
| { |
| xVBAProject->SeekRel(2); |
| *xVBAProject >> nLen; |
| xVBAProject->SeekRel( nLen ); |
| } |
| else |
| xVBAProject->SeekRel( nLen+2 ); |
| |
| *xVBAProject >> nLen; |
| DBG_ASSERT( nLen == 0xFFFF, "VBA: Bad field in VBA Project, panic!!" ); |
| if ( nLen != 0xFFFF) |
| return 0; |
| |
| xVBAProject->SeekRel(6); |
| sal_uInt16 nOctects; |
| *xVBAProject >> nOctects; |
| for(j=0;j<nOctects;j++) |
| xVBAProject->SeekRel(8); |
| |
| xVBAProject->SeekRel(5); |
| //end section |
| |
| *xVBAProject >> pOffsets[i].nOffset; |
| xVBAProject->SeekRel(2); |
| } |
| |
| return nOffsets; |
| } |
| |
| |
| /* #117718# For a given Module name return its type, |
| * Form, Class, Document, Normal or Unknown |
| * |
| */ |
| |
| ModType VBA_Impl::GetModuleType( const UniString& rModuleName ) |
| { |
| ModuleTypeHash::iterator iter = mhModHash.find( rModuleName ); |
| ModuleTypeHash::iterator iterEnd = mhModHash.end(); |
| if ( iter != iterEnd ) |
| { |
| return iter->second; |
| } |
| return ModuleType::UNKNOWN; |
| } |
| |
| bool VBA_Impl::Open( const String &rToplevel, const String &rSublevel ) |
| { |
| /* beginning test for vba stuff */ |
| bool bRet = false; |
| SvStorageRef xMacros= xStor->OpenSotStorage( rToplevel, |
| STREAM_READWRITE | STREAM_NOCREATE | |
| STREAM_SHARE_DENYALL ); |
| if( !xMacros.Is() || SVSTREAM_OK != xMacros->GetError() ) |
| { |
| DBG_WARNING("No Macros Storage"); |
| } |
| else |
| { |
| xVBA = xMacros->OpenSotStorage( rSublevel, |
| STREAM_READWRITE | STREAM_NOCREATE | |
| STREAM_SHARE_DENYALL ); |
| if( !xVBA.Is() || SVSTREAM_OK != xVBA->GetError() ) |
| { |
| DBG_WARNING("No Visual Basic in Storage"); |
| } |
| else |
| { |
| if (ReadVBAProject(xVBA)) |
| bRet = true; |
| } |
| /* #117718# |
| * Information regarding the type of module is contained in the |
| * "PROJECT" stream, this stream consists of a number of ascii lines |
| * entries are of the form Key=Value, the ones that we are interested |
| * in have the keys; Class, BaseClass & Module indicating the module |
| * ( value ) is either a Class Module, Form Module or a plain VB Module. */ |
| SvStorageStreamRef xProject = xMacros->OpenSotStream( |
| String( RTL_CONSTASCII_USTRINGPARAM( "PROJECT" ) ) ); |
| SvStorageStream* pStp = xProject; |
| UniString tmp; |
| static const String sThisDoc( RTL_CONSTASCII_USTRINGPARAM( "ThisDocument" ) ); |
| static const String sModule( RTL_CONSTASCII_USTRINGPARAM( "Module" ) ); |
| static const String sClass( RTL_CONSTASCII_USTRINGPARAM( "Class" ) ); |
| static const String sBaseClass( RTL_CONSTASCII_USTRINGPARAM( "BaseClass" ) ); |
| static const String sDocument( RTL_CONSTASCII_USTRINGPARAM( "Document" ) ); |
| mhModHash[ sThisDoc ] = ModuleType::CLASS; |
| while ( pStp->ReadByteStringLine( tmp, meCharSet ) ) |
| { |
| xub_StrLen index = tmp.Search( '=' ); |
| if ( index != STRING_NOTFOUND ) |
| { |
| String key = tmp.Copy( 0, index ); |
| String value = tmp.Copy( index + 1 ); |
| if ( key == sClass ) |
| { |
| mhModHash[ value ] = ModuleType::CLASS; |
| OSL_TRACE("Module %s is of type Class", |
| ::rtl::OUStringToOString( value , |
| RTL_TEXTENCODING_ASCII_US ).pData->buffer ); |
| } |
| else if ( key == sBaseClass ) |
| { |
| mhModHash[ value ] = ModuleType::FORM; |
| OSL_TRACE("Module %s is of type Form", |
| ::rtl::OUStringToOString( value , |
| RTL_TEXTENCODING_ASCII_US ).pData->buffer ); |
| } |
| else if ( key == sDocument ) |
| { |
| /* #i37965# DR 2004-12-03: add "Document", used i.e. |
| in Excel for macros attached to sheet or document. */ |
| |
| // value is of form <name>/&H<identifier>, strip the identifier |
| value.Erase( value.Search( '/' ) ); |
| |
| mhModHash[ value ] = ModuleType::DOCUMENT; |
| OSL_TRACE("Module %s is of type Document VBA", |
| ::rtl::OUStringToOString( value , |
| RTL_TEXTENCODING_ASCII_US ).pData->buffer ); |
| } |
| else if ( key == sModule ) |
| { |
| mhModHash[ value ] = ModuleType::NORMAL; |
| OSL_TRACE("Module %s is of type Normal VBA", |
| ::rtl::OUStringToOString( value , |
| RTL_TEXTENCODING_ASCII_US ).pData->buffer ); |
| } |
| } |
| } |
| } |
| /* end test for vba stuff */ |
| return bRet; |
| } |
| |
| const StringArray &VBA_Impl::Decompress(sal_uInt16 nIndex, int *pOverflow) |
| { |
| DBG_ASSERT( nIndex < nOffsets, "Index out of range" ); |
| SvStorageStreamRef xVBAStream; |
| aVBAStrings.SetSize(1); |
| aVBAStrings.Put(0,new String); |
| |
| xVBAStream = xVBA->OpenSotStream( pOffsets[nIndex].sName, |
| STREAM_STD_READ | STREAM_NOCREATE ); |
| if (pOverflow) |
| *pOverflow=0; |
| |
| if( !xVBAStream.Is() || SVSTREAM_OK != xVBAStream->GetError() ) |
| { |
| DBG_WARNING("Not able to open vb module "); |
| } |
| else |
| { |
| xVBAStream->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); |
| DecompressVBA( nIndex, xVBAStream ); |
| /* |
| * if len was too big for a single string set that variable ? |
| * if ((len > XX) && (pOverflow)) |
| *pOverflow=1; |
| */ |
| if (bCommented) |
| { |
| String sTempStringa; |
| if (mbMac) |
| sTempStringa = String( RTL_CONSTASCII_USTRINGPARAM( "\x0D" ) ); |
| else |
| sTempStringa = String( RTL_CONSTASCII_USTRINGPARAM( "\x0D\x0A" ) ); |
| String sTempStringb(sTempStringa); |
| sTempStringb+=sComment; |
| for(sal_uLong i=0;i<aVBAStrings.GetSize();i++) |
| { |
| aVBAStrings.Get(i)->SearchAndReplaceAll( |
| sTempStringa,sTempStringb); |
| aVBAStrings.Get(i)->Insert(sComment,0); |
| } |
| } |
| } |
| return aVBAStrings; |
| } |
| |
| |
| int VBA_Impl::DecompressVBA( int nIndex, SvStorageStreamRef &xVBAStream ) |
| { |
| sal_uInt8 nLeadbyte; |
| sal_uInt16 nToken; |
| unsigned int nPos = 0; |
| int nLen, nDistance, nShift, nClean=1; |
| |
| xVBAStream->Seek( pOffsets[ nIndex ].nOffset + 3 ); |
| |
| while(xVBAStream->Read(&nLeadbyte,1)) |
| { |
| for(int nPosition=0x01;nPosition < 0x100;nPosition=nPosition<<1) |
| { |
| //we see if the leadbyte has flagged this location as a dataunit |
| //which is actually a token which must be looked up in the history |
| if (nLeadbyte & nPosition) |
| { |
| *xVBAStream >> nToken; |
| |
| if (nClean == 0) |
| nClean=1; |
| |
| //For some reason the division of the token into the length |
| //field of the data to be inserted, and the distance back into |
| //the history differs depending on how full the history is |
| int nPos2 = nPos % nWINDOWLEN; |
| if (nPos2 <= 0x10) |
| nShift = 12; |
| else if (nPos2 <= 0x20) |
| nShift = 11; |
| else if (nPos2 <= 0x40) |
| nShift = 10; |
| else if (nPos2 <= 0x80) |
| nShift = 9; |
| else if (nPos2 <= 0x100) |
| nShift = 8; |
| else if (nPos2 <= 0x200) |
| nShift = 7; |
| else if (nPos2 <= 0x400) |
| nShift = 6; |
| else if (nPos2 <= 0x800) |
| nShift = 5; |
| else |
| nShift = 4; |
| |
| int i; |
| nLen=0; |
| for(i=0;i<nShift;i++) |
| nLen |= nToken & (1<<i); |
| |
| nLen += 3; |
| |
| nDistance = nToken >> nShift; |
| |
| //read the len of data from the history, wrapping around the |
| //nWINDOWLEN boundary if necessary data read from the history |
| //is also copied into the recent part of the history as well. |
| for (i = 0; i < nLen; i++) |
| { |
| unsigned char c; |
| c = aHistory[(nPos-nDistance-1) % nWINDOWLEN]; |
| aHistory[nPos % nWINDOWLEN] = c; |
| nPos++; |
| } |
| } |
| else |
| { |
| // special boundary case code, not guarantueed to be correct |
| // seems to work though, there is something wrong with the |
| // compression scheme (or maybe a feature) where when the data |
| // ends on a nWINDOWLEN boundary and the excess bytes in the 8 |
| // dataunit list are discarded, and not interpreted as tokens |
| // or normal data. |
| if ((nPos != 0) && ((nPos % nWINDOWLEN) == 0) && (nClean)) |
| { |
| xVBAStream->SeekRel(2); |
| nClean=0; |
| Output(nWINDOWLEN, aHistory); |
| break; |
| } |
| //This is the normal case for when the data unit is not a |
| //token to be looked up, but instead some normal data which |
| //can be output, and placed in the history. |
| if (xVBAStream->Read(&aHistory[nPos % nWINDOWLEN],1)) |
| nPos++; |
| |
| if (nClean == 0) |
| nClean=1; |
| } |
| } |
| } |
| if (nPos % nWINDOWLEN) |
| Output(nPos % nWINDOWLEN,aHistory); |
| return(nPos); |
| } |
| |
| /* vi:set tabstop=4 shiftwidth=4 expandtab: */ |