diff options
Diffstat (limited to 'basic/source/basmgr/basmgr.cxx')
-rw-r--r-- | basic/source/basmgr/basmgr.cxx | 2119 |
1 files changed, 2119 insertions, 0 deletions
diff --git a/basic/source/basmgr/basmgr.cxx b/basic/source/basmgr/basmgr.cxx new file mode 100644 index 0000000000..c619bbf1fb --- /dev/null +++ b/basic/source/basmgr/basmgr.cxx @@ -0,0 +1,2119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <utility> +#include <vcl/errinf.hxx> +#include <tools/stream.hxx> +#include <sot/storage.hxx> +#include <tools/urlobj.hxx> +#include <svl/hint.hxx> +#include <basic/sbx.hxx> +#include <basic/sbmeth.hxx> +#include <sot/storinfo.hxx> +#include <unotools/pathoptions.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <basic/sbmod.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +#include <basic/sberrors.hxx> +#include <basic/sbuno.hxx> +#include <basic/basmgr.hxx> +#include <global.hxx> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/script/XPersistentLibraryContainer.hpp> + +#include <scriptcont.hxx> + +#include <memory> +#include <vector> + +#define LIB_SEP 0x01 +#define LIBINFO_SEP 0x02 +#define LIBINFO_ID 0x1491 +#define PASSWORD_MARKER 0x31452134 + + +// Library API, implemented for XML import/export + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/script/XStarBasicAccess.hpp> +#include <com/sun/star/script/XStarBasicModuleInfo.hpp> +#include <com/sun/star/script/XStarBasicDialogInfo.hpp> +#include <com/sun/star/script/XStarBasicLibraryInfo.hpp> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <com/sun/star/script/ModuleInfo.hpp> +#include <com/sun/star/script/vba/XVBACompatibility.hpp> +#include <com/sun/star/script/vba/XVBAModuleInfo.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> + +#include <cppuhelper/implbase.hxx> + +using com::sun::star::uno::Reference; +using namespace com::sun::star; +using namespace com::sun::star::script; +using namespace cppu; + +typedef WeakImplHelper< container::XNameContainer > NameContainerHelper; +typedef WeakImplHelper< script::XStarBasicModuleInfo > ModuleInfoHelper; +typedef WeakImplHelper< script::XStarBasicAccess > StarBasicAccessHelper; + +// Version 1 +// sal_uInt32 nEndPos +// sal_uInt16 nId +// sal_uInt16 nVer +// bool bDoLoad +// String LibName +// String AbsStorageName +// String RelStorageName +// Version 2 +// + bool bReference + +constexpr OUString szStdLibName = u"Standard"_ustr; +constexpr OUString szBasicStorage = u"StarBASIC"_ustr; +constexpr OUString szOldManagerStream = u"BasicManager"_ustr; +constexpr OUString szManagerStream = u"BasicManager2"_ustr; +constexpr OUString szImbedded = u"LIBIMBEDDED"_ustr; +constexpr OString szCryptingKey = "CryptedBasic"_ostr; + + +const StreamMode eStreamReadMode = StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYALL; +const StreamMode eStorageReadMode = StreamMode::READ | StreamMode::SHARE_DENYWRITE; + +// BasMgrContainerListenerImpl + + +typedef ::cppu::WeakImplHelper< container::XContainerListener > ContainerListenerHelper; + +class BasMgrContainerListenerImpl: public ContainerListenerHelper +{ + BasicManager* mpMgr; + OUString maLibName; // empty -> no lib, but lib container + +public: + BasMgrContainerListenerImpl( BasicManager* pMgr, OUString aLibName ) + : mpMgr( pMgr ) + , maLibName(std::move( aLibName )) {} + + static void insertLibraryImpl( const uno::Reference< script::XLibraryContainer >& xScriptCont, BasicManager* pMgr, + const uno::Any& aLibAny, const OUString& aLibName ); + static void addLibraryModulesImpl( BasicManager const * pMgr, const uno::Reference< container::XNameAccess >& xLibNameAccess, + std::u16string_view aLibName ); + + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + + // XContainerListener + virtual void SAL_CALL elementInserted( const container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const container::ContainerEvent& Event ) override; +}; + + +// BasMgrContainerListenerImpl + + +void BasMgrContainerListenerImpl::insertLibraryImpl( const uno::Reference< script::XLibraryContainer >& xScriptCont, + BasicManager* pMgr, const uno::Any& aLibAny, const OUString& aLibName ) +{ + Reference< container::XNameAccess > xLibNameAccess; + aLibAny >>= xLibNameAccess; + + if( !pMgr->GetLib( aLibName ) ) + { + StarBASIC* pLib = + pMgr->CreateLibForLibContainer( aLibName, xScriptCont ); + DBG_ASSERT( pLib, "XML Import: Basic library could not be created"); + } + + uno::Reference< container::XContainer> xLibContainer( xLibNameAccess, uno::UNO_QUERY ); + if( xLibContainer.is() ) + { + // Register listener for library + Reference< container::XContainerListener > xLibraryListener + = new BasMgrContainerListenerImpl( pMgr, aLibName ); + xLibContainer->addContainerListener( xLibraryListener ); + } + + if( xScriptCont->isLibraryLoaded( aLibName ) ) + { + addLibraryModulesImpl( pMgr, xLibNameAccess, aLibName ); + } +} + + +void BasMgrContainerListenerImpl::addLibraryModulesImpl( BasicManager const * pMgr, + const uno::Reference< container::XNameAccess >& xLibNameAccess, std::u16string_view aLibName ) +{ + uno::Sequence< OUString > aModuleNames = xLibNameAccess->getElementNames(); + sal_Int32 nModuleCount = aModuleNames.getLength(); + + StarBASIC* pLib = pMgr->GetLib( aLibName ); + DBG_ASSERT( pLib, "BasMgrContainerListenerImpl::addLibraryModulesImpl: Unknown lib!"); + if( !pLib ) + return; + + const OUString* pNames = aModuleNames.getConstArray(); + for( sal_Int32 j = 0 ; j < nModuleCount ; j++ ) + { + OUString aModuleName = pNames[ j ]; + uno::Any aElement = xLibNameAccess->getByName( aModuleName ); + OUString aMod; + aElement >>= aMod; + uno::Reference< vba::XVBAModuleInfo > xVBAModuleInfo( xLibNameAccess, uno::UNO_QUERY ); + if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo( aModuleName ) ) + { + ModuleInfo aInfo = xVBAModuleInfo->getModuleInfo( aModuleName ); + pLib->MakeModule( aModuleName, aInfo, aMod ); + } + else + pLib->MakeModule( aModuleName, aMod ); + } + + pLib->SetModified( false ); +} + + +// XEventListener + + +void SAL_CALL BasMgrContainerListenerImpl::disposing( const lang::EventObject& ) {} + +// XContainerListener + + +void SAL_CALL BasMgrContainerListenerImpl::elementInserted( const container::ContainerEvent& Event ) +{ + bool bLibContainer = maLibName.isEmpty(); + OUString aName; + Event.Accessor >>= aName; + + if( bLibContainer ) + { + uno::Reference< script::XLibraryContainer > xScriptCont( Event.Source, uno::UNO_QUERY ); + if (xScriptCont.is()) + insertLibraryImpl(xScriptCont, mpMgr, Event.Element, aName); + StarBASIC* pLib = mpMgr->GetLib( aName ); + if ( pLib ) + { + uno::Reference< vba::XVBACompatibility > xVBACompat( xScriptCont, uno::UNO_QUERY ); + if ( xVBACompat.is() ) + pLib->SetVBAEnabled( xVBACompat->getVBACompatibilityMode() ); + } + } + else + { + + StarBASIC* pLib = mpMgr->GetLib( maLibName ); + DBG_ASSERT( pLib, "BasMgrContainerListenerImpl::elementInserted: Unknown lib!"); + if( pLib ) + { + SbModule* pMod = pLib->FindModule( aName ); + if( !pMod ) + { + OUString aMod; + Event.Element >>= aMod; + uno::Reference< vba::XVBAModuleInfo > xVBAModuleInfo( Event.Source, uno::UNO_QUERY ); + if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo( aName ) ) + { + ModuleInfo aInfo = xVBAModuleInfo->getModuleInfo( aName ); + pLib->MakeModule( aName, aInfo, aMod ); + } + else + pLib->MakeModule( aName, aMod ); + pLib->SetModified( false ); + } + } + } +} + + +void SAL_CALL BasMgrContainerListenerImpl::elementReplaced( const container::ContainerEvent& Event ) +{ + OUString aName; + Event.Accessor >>= aName; + + // Replace not possible for library container + DBG_ASSERT( !maLibName.isEmpty(), "library container fired elementReplaced()"); + + StarBASIC* pLib = mpMgr->GetLib( maLibName ); + if( !pLib ) + return; + + SbModule* pMod = pLib->FindModule( aName ); + OUString aMod; + Event.Element >>= aMod; + + if( pMod ) + pMod->SetSource32( aMod ); + else + pLib->MakeModule( aName, aMod ); + + pLib->SetModified( false ); +} + + +void SAL_CALL BasMgrContainerListenerImpl::elementRemoved( const container::ContainerEvent& Event ) +{ + OUString aName; + Event.Accessor >>= aName; + + bool bLibContainer = maLibName.isEmpty(); + if( bLibContainer ) + { + StarBASIC* pLib = mpMgr->GetLib( aName ); + if( pLib ) + { + sal_uInt16 nLibId = mpMgr->GetLibId( aName ); + mpMgr->RemoveLib( nLibId, false ); + } + } + else + { + StarBASIC* pLib = mpMgr->GetLib( maLibName ); + SbModule* pMod = pLib ? pLib->FindModule( aName ) : nullptr; + if( pMod ) + { + pLib->Remove( pMod ); + pLib->SetModified( false ); + } + } +} + +BasicError::BasicError( ErrCodeMsg nId, BasicErrorReason nR ) +{ + nErrorId = nId; + nReason = nR; +} + +BasicError::BasicError( const BasicError& rErr ) +{ + nErrorId = rErr.nErrorId; + nReason = rErr.nReason; +} + + +class BasicLibInfo +{ +private: + StarBASICRef xLib; + OUString aLibName; + OUString aStorageName; // string is sufficient, unique at runtime + OUString aRelStorageName; + OUString aPassword; + + bool bDoLoad; + bool bReference; + + // Lib represents library in new UNO library container + uno::Reference< script::XLibraryContainer > mxScriptCont; + +public: + BasicLibInfo(); + + bool IsReference() const { return bReference; } + void SetReference(bool b) { bReference = b; } + + bool IsExtern() const { return aStorageName != szImbedded; } + + void SetStorageName( const OUString& rName ) { aStorageName = rName; } + const OUString& GetStorageName() const { return aStorageName; } + + void SetRelStorageName( const OUString& rN ) { aRelStorageName = rN; } + const OUString& GetRelStorageName() const { return aRelStorageName; } + + StarBASICRef GetLib() const + { + if( mxScriptCont.is() && mxScriptCont->hasByName( aLibName ) && + !mxScriptCont->isLibraryLoaded( aLibName ) ) + return StarBASICRef(); + return xLib; + } + StarBASICRef& GetLibRef() { return xLib; } + void SetLib( StarBASIC* pBasic ) { xLib = pBasic; } + + const OUString& GetLibName() const { return aLibName; } + void SetLibName( const OUString& rName ) { aLibName = rName; } + + // Only temporary for Load/Save + bool DoLoad() const { return bDoLoad; } + + bool HasPassword() const { return !aPassword.isEmpty(); } + const OUString& GetPassword() const { return aPassword; } + void SetPassword( const OUString& rNewPassword ) + { aPassword = rNewPassword; } + + static BasicLibInfo* Create( SotStorageStream& rSStream ); + + const uno::Reference< script::XLibraryContainer >& GetLibraryContainer() const + { return mxScriptCont; } + void SetLibraryContainer( const uno::Reference< script::XLibraryContainer >& xScriptCont ) + { mxScriptCont = xScriptCont; } +}; + + +BasicLibInfo::BasicLibInfo() + : aStorageName(szImbedded) + , aRelStorageName(szImbedded) + , bDoLoad(false) + , bReference(false) +{ +} + +BasicLibInfo* BasicLibInfo::Create( SotStorageStream& rSStream ) +{ + BasicLibInfo* pInfo = new BasicLibInfo; + + sal_uInt32 nEndPos; + sal_uInt16 nId; + sal_uInt16 nVer; + + rSStream.ReadUInt32( nEndPos ); + rSStream.ReadUInt16( nId ); + rSStream.ReadUInt16( nVer ); + + DBG_ASSERT( nId == LIBINFO_ID, "No BasicLibInfo?!" ); + if( nId == LIBINFO_ID ) + { + // Reload? + bool bDoLoad; + rSStream.ReadCharAsBool( bDoLoad ); + pInfo->bDoLoad = bDoLoad; + + // The name of the lib... + OUString aName = rSStream.ReadUniOrByteString(rSStream.GetStreamCharSet()); + pInfo->SetLibName( aName ); + + // Absolute path... + OUString aStorageName = rSStream.ReadUniOrByteString(rSStream.GetStreamCharSet()); + pInfo->SetStorageName( aStorageName ); + + // Relative path... + OUString aRelStorageName = rSStream.ReadUniOrByteString(rSStream.GetStreamCharSet()); + pInfo->SetRelStorageName( aRelStorageName ); + + if ( nVer >= 2 ) + { + bool bReferenz; + rSStream.ReadCharAsBool( bReferenz ); + pInfo->SetReference(bReferenz); + } + + rSStream.Seek( nEndPos ); + } + return pInfo; +} + +BasicManager::BasicManager( SotStorage& rStorage, std::u16string_view rBaseURL, StarBASIC* pParentFromStdLib, OUString const * pLibPath, bool bDocMgr ) : mbDocMgr( bDocMgr ) +{ + if( pLibPath ) + { + aBasicLibPath = *pLibPath; + } + OUString aStorName( rStorage.GetName() ); + maStorageName = INetURLObject(aStorName, INetProtocol::File).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + + // If there is no Manager Stream, no further actions are necessary + if ( rStorage.IsStream( szManagerStream ) ) + { + LoadBasicManager( rStorage, rBaseURL ); + // StdLib contains Parent: + StarBASIC* pStdLib = GetStdLib(); + DBG_ASSERT( pStdLib, "Standard-Lib not loaded?" ); + if ( !pStdLib ) + { + // Should never happen, but if it happens we won't crash... + pStdLib = new StarBASIC( nullptr, mbDocMgr ); + + if (maLibs.empty()) + CreateLibInfo(); + + BasicLibInfo& rStdLibInfo = *maLibs.front(); + + rStdLibInfo.SetLib( pStdLib ); + StarBASICRef xStdLib = rStdLibInfo.GetLib(); + xStdLib->SetName( szStdLibName ); + rStdLibInfo.SetLibName( szStdLibName ); + xStdLib->SetFlag( SbxFlagBits::DontStore | SbxFlagBits::ExtSearch ); + xStdLib->SetModified( false ); + } + else + { + pStdLib->SetParent( pParentFromStdLib ); + // The other get StdLib as parent: + + for ( sal_uInt16 nBasic = 1; nBasic < GetLibCount(); nBasic++ ) + { + StarBASIC* pBasic = GetLib( nBasic ); + if ( pBasic ) + { + pStdLib->Insert( pBasic ); + pBasic->SetFlag( SbxFlagBits::ExtSearch ); + } + } + // Modified through insert + pStdLib->SetModified( false ); + } + } + else + { + ImpCreateStdLib( pParentFromStdLib ); + if ( rStorage.IsStream( szOldManagerStream ) ) + LoadOldBasicManager( rStorage ); + } +} + +static void copyToLibraryContainer( StarBASIC* pBasic, const LibraryContainerInfo& rInfo ) +{ + uno::Reference< script::XLibraryContainer > xScriptCont( rInfo.mxScriptCont ); + if ( !xScriptCont.is() ) + return; + + OUString aLibName = pBasic->GetName(); + if( !xScriptCont->hasByName( aLibName ) ) + xScriptCont->createLibrary( aLibName ); + + uno::Any aLibAny = xScriptCont->getByName( aLibName ); + uno::Reference< container::XNameContainer > xLib; + aLibAny >>= xLib; + if ( !xLib.is() ) + return; + + for ( const auto& pModule: pBasic->GetModules() ) + { + OUString aModName = pModule->GetName(); + if( !xLib->hasByName( aModName ) ) + { + OUString aSource = pModule->GetSource32(); + uno::Any aSourceAny; + aSourceAny <<= aSource; + xLib->insertByName( aModName, aSourceAny ); + } + } +} + +const uno::Reference< script::XPersistentLibraryContainer >& BasicManager::GetDialogLibraryContainer() const +{ + return maContainerInfo.mxDialogCont; +} + +const uno::Reference< script::XPersistentLibraryContainer >& BasicManager::GetScriptLibraryContainer() const +{ + return maContainerInfo.mxScriptCont; +} + +void BasicManager::SetLibraryContainerInfo( const LibraryContainerInfo& rInfo ) +{ + maContainerInfo = rInfo; + + uno::Reference< script::XLibraryContainer > xScriptCont( maContainerInfo.mxScriptCont ); + if( xScriptCont.is() ) + { + // Register listener for lib container + uno::Reference< container::XContainerListener > xLibContainerListener + = new BasMgrContainerListenerImpl( this, "" ); + + uno::Reference< container::XContainer> xLibContainer( xScriptCont, uno::UNO_QUERY ); + xLibContainer->addContainerListener( xLibContainerListener ); + + const uno::Sequence< OUString > aScriptLibNames = xScriptCont->getElementNames(); + + if( aScriptLibNames.hasElements() ) + { + for(const auto& rScriptLibName : aScriptLibNames) + { + uno::Any aLibAny = xScriptCont->getByName( rScriptLibName ); + + if ( rScriptLibName == "Standard" || rScriptLibName == "VBAProject") + xScriptCont->loadLibrary( rScriptLibName ); + + BasMgrContainerListenerImpl::insertLibraryImpl + ( xScriptCont, this, aLibAny, rScriptLibName ); + } + } + else + { + // No libs? Maybe an 5.2 document already loaded + for (auto const& rpBasLibInfo : maLibs) + { + StarBASIC* pLib = rpBasLibInfo->GetLib().get(); + if( !pLib ) + { + bool bLoaded = ImpLoadLibrary( rpBasLibInfo.get(), nullptr ); + if( bLoaded ) + pLib = rpBasLibInfo->GetLib().get(); + } + if( pLib ) + { + copyToLibraryContainer( pLib, maContainerInfo ); + if (rpBasLibInfo->HasPassword()) + { + basic::SfxScriptLibraryContainer* pOldBasicPassword = + maContainerInfo.mpOldBasicPassword; + if( pOldBasicPassword ) + { + pOldBasicPassword->setLibraryPassword( + pLib->GetName(), rpBasLibInfo->GetPassword() ); + } + } + } + } + } + } + + SetGlobalUNOConstant( "BasicLibraries", uno::Any( maContainerInfo.mxScriptCont ) ); + SetGlobalUNOConstant( "DialogLibraries", uno::Any( maContainerInfo.mxDialogCont ) ); +} + +BasicManager::BasicManager( StarBASIC* pSLib, OUString const * pLibPath, bool bDocMgr ) : mbDocMgr( bDocMgr ) +{ + DBG_ASSERT( pSLib, "BasicManager cannot be created with a NULL-Pointer!" ); + + if( pLibPath ) + { + aBasicLibPath = *pLibPath; + } + BasicLibInfo* pStdLibInfo = CreateLibInfo(); + pStdLibInfo->SetLib( pSLib ); + StarBASICRef xStdLib = pStdLibInfo->GetLib(); + xStdLib->SetName(szStdLibName); + pStdLibInfo->SetLibName(szStdLibName ); + pSLib->SetFlag( SbxFlagBits::DontStore | SbxFlagBits::ExtSearch ); + + // Save is only necessary if basic has changed + xStdLib->SetModified( false ); +} + +void BasicManager::ImpMgrNotLoaded( const OUString& rStorageName ) +{ + // pErrInf is only destroyed if the error os processed by an + // ErrorHandler + ErrCodeMsg aErrInf( ERRCODE_BASMGR_MGROPEN, rStorageName, DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::OPENMGRSTREAM); + + // Create a stdlib otherwise we crash! + BasicLibInfo* pStdLibInfo = CreateLibInfo(); + pStdLibInfo->SetLib( new StarBASIC( nullptr, mbDocMgr ) ); + StarBASICRef xStdLib = pStdLibInfo->GetLib(); + xStdLib->SetName( szStdLibName ); + pStdLibInfo->SetLibName( szStdLibName ); + xStdLib->SetFlag( SbxFlagBits::DontStore | SbxFlagBits::ExtSearch ); + xStdLib->SetModified( false ); +} + + +void BasicManager::ImpCreateStdLib( StarBASIC* pParentFromStdLib ) +{ + BasicLibInfo* pStdLibInfo = CreateLibInfo(); + StarBASIC* pStdLib = new StarBASIC( pParentFromStdLib, mbDocMgr ); + pStdLibInfo->SetLib( pStdLib ); + pStdLib->SetName( szStdLibName ); + pStdLibInfo->SetLibName( szStdLibName ); + pStdLib->SetFlag( SbxFlagBits::DontStore | SbxFlagBits::ExtSearch ); +} + +void BasicManager::LoadBasicManager( SotStorage& rStorage, std::u16string_view rBaseURL ) +{ + tools::SvRef<SotStorageStream> xManagerStream = rStorage.OpenSotStream( szManagerStream, eStreamReadMode ); + + OUString aStorName( rStorage.GetName() ); + // #i13114 removed, DBG_ASSERT( aStorName.Len(), "No Storage Name!" ); + + if ( !xManagerStream.is() || xManagerStream->GetError() || ( xManagerStream->TellEnd() == 0 ) ) + { + ImpMgrNotLoaded( aStorName ); + return; + } + + maStorageName = INetURLObject(aStorName, INetProtocol::File).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + // #i13114 removed, DBG_ASSERT(aStorageName.Len() != 0, "Bad storage name"); + + OUString aRealStorageName = maStorageName; // for relative paths, can be modified through BaseURL + + if ( !rBaseURL.empty() ) + { + INetURLObject aObj( rBaseURL ); + if ( aObj.GetProtocol() == INetProtocol::File ) + { + aRealStorageName = aObj.PathToFileName(); + } + } + + xManagerStream->SetBufferSize( 1024 ); + xManagerStream->Seek( STREAM_SEEK_TO_BEGIN ); + + sal_uInt32 nEndPos; + xManagerStream->ReadUInt32( nEndPos ); + + sal_uInt16 nLibs; + xManagerStream->ReadUInt16( nLibs ); + // Plausibility! + if( nLibs & 0xF000 ) + { + SAL_WARN( "basic", "BasicManager-Stream defect!" ); + return; + } + const size_t nMinBasicLibSize(8); + const size_t nMaxPossibleLibs = xManagerStream->remainingSize() / nMinBasicLibSize; + if (nLibs > nMaxPossibleLibs) + { + SAL_WARN("basic", "Parsing error: " << nMaxPossibleLibs << + " max possible entries, but " << nLibs << " claimed, truncating"); + nLibs = nMaxPossibleLibs; + } + for (sal_uInt16 nL = 0; nL < nLibs; ++nL) + { + BasicLibInfo* pInfo = BasicLibInfo::Create( *xManagerStream ); + + // Correct absolute pathname if relative is existing. + // Always try relative first if there are two stands on disk + if ( !pInfo->GetRelStorageName().isEmpty() && pInfo->GetRelStorageName() != szImbedded ) + { + INetURLObject aObj( aRealStorageName, INetProtocol::File ); + aObj.removeSegment(); + bool bWasAbsolute = false; + aObj = aObj.smartRel2Abs( pInfo->GetRelStorageName(), bWasAbsolute ); + + //*** TODO: Replace if still necessary + //*** TODO-End + if ( ! aBasicLibPath.isEmpty() ) + { + // Search lib in path + OUString aSearchFile = pInfo->GetRelStorageName(); + OUString aSearchFileOldFormat(aSearchFile); + SvtPathOptions aPathCFG; + if( aPathCFG.SearchFile( aSearchFileOldFormat, SvtPathOptions::Paths::Basic ) ) + { + pInfo->SetStorageName( aSearchFile ); + } + } + } + + maLibs.push_back(std::unique_ptr<BasicLibInfo>(pInfo)); + // Libs from external files should be loaded only when necessary. + // But references are loaded at once, otherwise some big customers get into trouble + if ( pInfo->DoLoad() && + ( !pInfo->IsExtern() || pInfo->IsReference())) + { + ImpLoadLibrary( pInfo, &rStorage ); + } + } + + xManagerStream->Seek( nEndPos ); + xManagerStream->SetBufferSize( 0 ); + xManagerStream.clear(); +} + +void BasicManager::LoadOldBasicManager( SotStorage& rStorage ) +{ + tools::SvRef<SotStorageStream> xManagerStream = rStorage.OpenSotStream( szOldManagerStream, eStreamReadMode ); + + OUString aStorName( rStorage.GetName() ); + DBG_ASSERT( aStorName.getLength(), "No Storage Name!" ); + + if ( !xManagerStream.is() || xManagerStream->GetError() || ( xManagerStream->TellEnd() == 0 ) ) + { + ImpMgrNotLoaded( aStorName ); + return; + } + + xManagerStream->SetBufferSize( 1024 ); + xManagerStream->Seek( STREAM_SEEK_TO_BEGIN ); + sal_uInt32 nBasicStartOff, nBasicEndOff; + xManagerStream->ReadUInt32( nBasicStartOff ); + xManagerStream->ReadUInt32( nBasicEndOff ); + + DBG_ASSERT( !xManagerStream->GetError(), "Invalid Manager-Stream!" ); + + xManagerStream->Seek( nBasicStartOff ); + if (!ImplLoadBasic( *xManagerStream, maLibs.front()->GetLibRef() )) + { + ErrCodeMsg aErrInf( ERRCODE_BASMGR_MGROPEN, aStorName, DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::OPENMGRSTREAM); + // and it proceeds ... + } + xManagerStream->Seek( nBasicEndOff+1 ); // +1: 0x00 as separator + OUString aLibs = xManagerStream->ReadUniOrByteString(xManagerStream->GetStreamCharSet()); + xManagerStream->SetBufferSize( 0 ); + xManagerStream.clear(); // Close stream + + if ( aLibs.isEmpty() ) + return; + + INetURLObject aCurStorage( aStorName, INetProtocol::File ); + sal_Int32 nLibPos {0}; + do { + const OUString aLibInfo(aLibs.getToken(0, LIB_SEP, nLibPos)); + sal_Int32 nInfoPos {0}; + const OUString aLibName( aLibInfo.getToken( 0, LIBINFO_SEP, nInfoPos ) ); + DBG_ASSERT( nInfoPos >= 0, "Invalid Lib-Info!" ); + const OUString aLibAbsStorageName( aLibInfo.getToken( 0, LIBINFO_SEP, nInfoPos ) ); + // TODO: fail also here if there are no more tokens? + const OUString aLibRelStorageName( aLibInfo.getToken( 0, LIBINFO_SEP, nInfoPos ) ); + DBG_ASSERT( nInfoPos < 0, "Invalid Lib-Info!" ); + INetURLObject aLibAbsStorage( aLibAbsStorageName, INetProtocol::File ); + + INetURLObject aLibRelStorage( aStorName ); + aLibRelStorage.removeSegment(); + bool bWasAbsolute = false; + aLibRelStorage = aLibRelStorage.smartRel2Abs( aLibRelStorageName, bWasAbsolute); + DBG_ASSERT(!bWasAbsolute, "RelStorageName was absolute!" ); + + tools::SvRef<SotStorage> xStorageRef; + if ( aLibAbsStorage == aCurStorage || aLibRelStorageName == szImbedded ) + { + xStorageRef = &rStorage; + } + else + { + xStorageRef = new SotStorage( false, aLibAbsStorage.GetMainURL + ( INetURLObject::DecodeMechanism::NONE ), eStorageReadMode ); + if ( xStorageRef->GetError() != ERRCODE_NONE ) + xStorageRef = new SotStorage( false, aLibRelStorage. + GetMainURL( INetURLObject::DecodeMechanism::NONE ), eStorageReadMode ); + } + if ( xStorageRef.is() ) + { + AddLib( *xStorageRef, aLibName, false ); + } + else + { + ErrCodeMsg aErrInf( ERRCODE_BASMGR_LIBLOAD, aStorName, DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::STORAGENOTFOUND); + } + } while (nLibPos>=0); +} + +BasicManager::~BasicManager() +{ + // Notify listener if something needs to be saved + Broadcast( SfxHint( SfxHintId::Dying) ); +} + +bool BasicManager::HasExeCode( std::u16string_view sLib ) +{ + StarBASIC* pLib = GetLib(sLib); + if ( pLib ) + { + for (const auto& pModule: pLib->GetModules()) + { + if (pModule->HasExeCode()) + return true; + } + } + return false; +} + +BasicLibInfo* BasicManager::CreateLibInfo() +{ + maLibs.push_back(std::make_unique<BasicLibInfo>()); + return maLibs.back().get(); +} + +bool BasicManager::ImpLoadLibrary( BasicLibInfo* pLibInfo, SotStorage* pCurStorage ) +{ + try { + DBG_ASSERT( pLibInfo, "LibInfo!?" ); + + OUString aStorageName( pLibInfo->GetStorageName() ); + if ( aStorageName.isEmpty() || aStorageName == szImbedded ) + { + aStorageName = GetStorageName(); + } + tools::SvRef<SotStorage> xStorage; + // The current must not be opened again... + if ( pCurStorage ) + { + OUString aStorName( pCurStorage->GetName() ); + // #i13114 removed, DBG_ASSERT( aStorName.Len(), "No Storage Name!" ); + + INetURLObject aCurStorageEntry(aStorName, INetProtocol::File); + // #i13114 removed, DBG_ASSERT(aCurStorageEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE ).Len() != 0, "Bad storage name"); + + INetURLObject aStorageEntry(aStorageName, INetProtocol::File); + // #i13114 removed, DBG_ASSERT(aCurStorageEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE ).Len() != 0, "Bad storage name"); + + if ( aCurStorageEntry == aStorageEntry ) + { + xStorage = pCurStorage; + } + } + + if ( !xStorage.is() ) + { + xStorage = new SotStorage( false, aStorageName, eStorageReadMode ); + } + tools::SvRef<SotStorage> xBasicStorage = xStorage->OpenSotStorage( szBasicStorage, eStorageReadMode, false ); + + if ( !xBasicStorage.is() || xBasicStorage->GetError() ) + { + ErrCodeMsg aErrInf( ERRCODE_BASMGR_MGROPEN, xStorage->GetName(), DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::OPENLIBSTORAGE); + } + else + { + // In the Basic-Storage every lib is in a Stream... + tools::SvRef<SotStorageStream> xBasicStream = xBasicStorage->OpenSotStream( pLibInfo->GetLibName(), eStreamReadMode ); + if ( !xBasicStream.is() || xBasicStream->GetError() ) + { + ErrCodeMsg aErrInf( ERRCODE_BASMGR_LIBLOAD , pLibInfo->GetLibName(), DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::OPENLIBSTREAM); + } + else + { + bool bLoaded = false; + if ( xBasicStream->TellEnd() != 0 ) + { + if ( !pLibInfo->GetLib().is() ) + { + pLibInfo->SetLib( new StarBASIC( GetStdLib(), mbDocMgr ) ); + } + xBasicStream->SetBufferSize( 1024 ); + xBasicStream->Seek( STREAM_SEEK_TO_BEGIN ); + bLoaded = ImplLoadBasic( *xBasicStream, pLibInfo->GetLibRef() ); + xBasicStream->SetBufferSize( 0 ); + StarBASICRef xStdLib = pLibInfo->GetLib(); + xStdLib->SetName( pLibInfo->GetLibName() ); + xStdLib->SetModified( false ); + xStdLib->SetFlag( SbxFlagBits::DontStore ); + } + if ( !bLoaded ) + { + ErrCodeMsg aErrInf( ERRCODE_BASMGR_LIBLOAD, pLibInfo->GetLibName(), DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::BASICLOADERROR); + } + else + { + // Perhaps there are additional information in the stream... + xBasicStream->SetCryptMaskKey(szCryptingKey); + xBasicStream->RefreshBuffer(); + sal_uInt32 nPasswordMarker = 0; + xBasicStream->ReadUInt32( nPasswordMarker ); + if ( ( nPasswordMarker == PASSWORD_MARKER ) && !xBasicStream->eof() ) + { + OUString aPassword = xBasicStream->ReadUniOrByteString( + xBasicStream->GetStreamCharSet()); + pLibInfo->SetPassword( aPassword ); + } + xBasicStream->SetCryptMaskKey(OString()); + CheckModules( pLibInfo->GetLib().get(), pLibInfo->IsReference() ); + } + return bLoaded; + } + } + } + catch (const css::ucb::ContentCreationException&) + { + } + return false; +} + +bool BasicManager::ImplEncryptStream( SvStream& rStrm ) +{ + sal_uInt64 const nPos = rStrm.Tell(); + sal_uInt32 nCreator; + rStrm.ReadUInt32( nCreator ); + rStrm.Seek( nPos ); + bool bProtected = false; + if ( nCreator != SBXCR_SBX ) + { + // Should only be the case for encrypted Streams + bProtected = true; + rStrm.SetCryptMaskKey(szCryptingKey); + rStrm.RefreshBuffer(); + } + return bProtected; +} + +// This code is necessary to load the BASIC of Beta 1 +// TODO: Which Beta 1? +bool BasicManager::ImplLoadBasic( SvStream& rStrm, StarBASICRef& rOldBasic ) const +{ + bool bProtected = ImplEncryptStream( rStrm ); + SbxBaseRef xNew = SbxBase::Load( rStrm ); + bool bLoaded = false; + if( xNew.is() ) + { + if( auto pNew = dynamic_cast<StarBASIC*>( xNew.get() ) ) + { + // Use the Parent of the old BASICs + if( rOldBasic.is() ) + { + pNew->SetParent( rOldBasic->GetParent() ); + if( pNew->GetParent() ) + { + pNew->GetParent()->Insert( pNew ); + } + pNew->SetFlag( SbxFlagBits::ExtSearch ); + } + rOldBasic = pNew; + + // Fill new library container (5.2 -> 6.0) + copyToLibraryContainer( pNew, maContainerInfo ); + + pNew->SetModified( false ); + bLoaded = true; + } + } + if ( bProtected ) + { + rStrm.SetCryptMaskKey(OString()); + } + return bLoaded; +} + +void BasicManager::CheckModules( StarBASIC* pLib, bool bReference ) +{ + if ( !pLib ) + { + return; + } + bool bModified = pLib->IsModified(); + + for ( const auto& pModule: pLib->GetModules() ) + { + DBG_ASSERT(pModule, "Module not received!"); + if ( !pModule->IsCompiled() && !StarBASIC::GetErrorCode() ) + { + pModule->Compile(); + } + } + + // #67477, AB 8.12.99 On demand compile in referenced libs should not + // cause modified + if( !bModified && bReference ) + { + OSL_FAIL( "Referenced basic library is not compiled!" ); + pLib->SetModified( false ); + } +} + +StarBASIC* BasicManager::AddLib( SotStorage& rStorage, const OUString& rLibName, bool bReference ) +{ + OUString aStorName( rStorage.GetName() ); + DBG_ASSERT( !aStorName.isEmpty(), "No Storage Name!" ); + + OUString aStorageName = INetURLObject(aStorName, INetProtocol::File).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + DBG_ASSERT(!aStorageName.isEmpty(), "Bad storage name"); + + OUString aNewLibName( rLibName ); + while ( HasLib( aNewLibName ) ) + { + aNewLibName += "_"; + } + BasicLibInfo* pLibInfo = CreateLibInfo(); + // Use original name otherwise ImpLoadLibrary fails... + pLibInfo->SetLibName( rLibName ); + // but doesn't work this way if name exists twice + sal_uInt16 nLibId = static_cast<sal_uInt16>(maLibs.size()) - 1; + + // Set StorageName before load because it is compared with pCurStorage + pLibInfo->SetStorageName( aStorageName ); + bool bLoaded = ImpLoadLibrary( pLibInfo, &rStorage ); + + if ( bLoaded ) + { + if ( aNewLibName != rLibName ) + { + pLibInfo->SetLibName(aNewLibName); + } + if ( bReference ) + { + pLibInfo->GetLib()->SetModified( false ); // Don't save in this case + pLibInfo->SetRelStorageName( OUString() ); + pLibInfo->SetReference(true); + } + else + { + pLibInfo->GetLib()->SetModified( true ); // Must be saved after Add! + pLibInfo->SetStorageName( szImbedded ); // Save in BasicManager-Storage + } + } + else + { + RemoveLib( nLibId, false ); + pLibInfo = nullptr; + } + + return pLibInfo ? &*pLibInfo->GetLib() : nullptr; + +} + +bool BasicManager::IsReference( sal_uInt16 nLib ) +{ + DBG_ASSERT( nLib < maLibs.size(), "Lib does not exist!" ); + if ( nLib < maLibs.size() ) + { + return maLibs[nLib]->IsReference(); + } + return false; +} + +void BasicManager::RemoveLib( sal_uInt16 nLib ) +{ + // Only physical deletion if no reference + RemoveLib( nLib, !IsReference( nLib ) ); +} + +bool BasicManager::RemoveLib( sal_uInt16 nLib, bool bDelBasicFromStorage ) +{ + DBG_ASSERT( nLib, "Standard-Lib cannot be removed!" ); + + DBG_ASSERT( !nLib || nLib < maLibs.size(), "Lib not found!" ); + + if( !nLib || nLib < maLibs.size() ) + { + ErrCodeMsg aErrInf( ERRCODE_BASMGR_REMOVELIB, OUString(), DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::STDLIB); + return false; + } + + auto const itLibInfo = maLibs.begin() + nLib; + + // If one of the streams cannot be opened, this is not an error, + // because BASIC was never written before... + if (bDelBasicFromStorage && !(*itLibInfo)->IsReference() && + (!(*itLibInfo)->IsExtern() || SotStorage::IsStorageFile((*itLibInfo)->GetStorageName()))) + { + tools::SvRef<SotStorage> xStorage; + try + { + if (!(*itLibInfo)->IsExtern()) + { + xStorage = new SotStorage(false, GetStorageName()); + } + else + { + xStorage = new SotStorage(false, (*itLibInfo)->GetStorageName()); + } + } + catch (const css::ucb::ContentCreationException&) + { + TOOLS_WARN_EXCEPTION("basic", "BasicManager::RemoveLib:"); + } + + if (xStorage.is() && xStorage->IsStorage(szBasicStorage)) + { + tools::SvRef<SotStorage> xBasicStorage = xStorage->OpenSotStorage + ( szBasicStorage, StreamMode::STD_READWRITE, false ); + + if ( !xBasicStorage.is() || xBasicStorage->GetError() ) + { + ErrCodeMsg aErrInf( ERRCODE_BASMGR_REMOVELIB, OUString(), DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::OPENLIBSTORAGE); + } + else if (xBasicStorage->IsStream((*itLibInfo)->GetLibName())) + { + xBasicStorage->Remove((*itLibInfo)->GetLibName()); + xBasicStorage->Commit(); + + // If no further stream available, + // delete the SubStorage. + SvStorageInfoList aInfoList; + xBasicStorage->FillInfoList( &aInfoList ); + if ( aInfoList.empty() ) + { + xBasicStorage.clear(); + xStorage->Remove( szBasicStorage ); + xStorage->Commit(); + // If no further Streams or SubStorages available, + // delete the Storage, too. + aInfoList.clear(); + xStorage->FillInfoList( &aInfoList ); + if ( aInfoList.empty() ) + { + //OUString aName_( xStorage->GetName() ); + xStorage.clear(); + //*** TODO: Replace if still necessary + //SfxContentHelper::Kill( aName ); + //*** TODO-End + } + } + } + } + } + if ((*itLibInfo)->GetLib().is()) + { + GetStdLib()->Remove( (*itLibInfo)->GetLib().get() ); + } + maLibs.erase(itLibInfo); + return true; // Remove was successful, del unimportant +} + +sal_uInt16 BasicManager::GetLibCount() const +{ + return static_cast<sal_uInt16>(maLibs.size()); +} + +StarBASIC* BasicManager::GetLib( sal_uInt16 nLib ) const +{ + DBG_ASSERT( nLib < maLibs.size(), "Lib does not exist!" ); + if ( nLib < maLibs.size() ) + { + return maLibs[nLib]->GetLib().get(); + } + return nullptr; +} + +StarBASIC* BasicManager::GetStdLib() const +{ + StarBASIC* pLib = GetLib( 0 ); + return pLib; +} + +StarBASIC* BasicManager::GetLib( std::u16string_view rName ) const +{ + for (auto const& rpLib : maLibs) + { + if (rpLib->GetLibName().equalsIgnoreAsciiCase(rName)) // Check if available... + { + return rpLib->GetLib().get(); + } + } + return nullptr; +} + +sal_uInt16 BasicManager::GetLibId( std::u16string_view rName ) const +{ + for (size_t i = 0; i < maLibs.size(); i++) + { + if (maLibs[i]->GetLibName().equalsIgnoreAsciiCase( rName )) + { + return static_cast<sal_uInt16>(i); + } + } + return LIB_NOTFOUND; +} + +bool BasicManager::HasLib( std::u16string_view rName ) const +{ + for (const auto& rpLib : maLibs) + { + if (rpLib->GetLibName().equalsIgnoreAsciiCase(rName)) // Check if available... + { + return true; + } + } + return false; +} + +OUString BasicManager::GetLibName( sal_uInt16 nLib ) +{ + DBG_ASSERT( nLib < maLibs.size(), "Lib?!" ); + if ( nLib < maLibs.size() ) + { + return maLibs[nLib]->GetLibName(); + } + return OUString(); +} + +bool BasicManager::LoadLib( sal_uInt16 nLib ) +{ + bool bDone = false; + DBG_ASSERT( nLib < maLibs.size() , "Lib?!" ); + if ( nLib < maLibs.size() ) + { + BasicLibInfo& rLibInfo = *maLibs[nLib]; + uno::Reference< script::XLibraryContainer > xLibContainer = rLibInfo.GetLibraryContainer(); + if( xLibContainer.is() ) + { + OUString aLibName = rLibInfo.GetLibName(); + xLibContainer->loadLibrary( aLibName ); + bDone = xLibContainer->isLibraryLoaded( aLibName ); + } + else + { + bDone = ImpLoadLibrary( &rLibInfo, nullptr ); + StarBASIC* pLib = GetLib( nLib ); + if ( pLib ) + { + GetStdLib()->Insert( pLib ); + pLib->SetFlag( SbxFlagBits::ExtSearch ); + } + } + } + else + { + ErrCodeMsg aErrInf( ERRCODE_BASMGR_LIBLOAD, OUString(), DialogMask::ButtonsOk ); + aErrors.emplace_back(aErrInf, BasicErrorReason::LIBNOTFOUND); + } + return bDone; +} + +StarBASIC* BasicManager::CreateLib( const OUString& rLibName ) +{ + if ( GetLib( rLibName ) ) + { + return nullptr; + } + BasicLibInfo* pLibInfo = CreateLibInfo(); + StarBASIC* pNew = new StarBASIC( GetStdLib(), mbDocMgr ); + GetStdLib()->Insert( pNew ); + pNew->SetFlag( SbxFlagBits::ExtSearch | SbxFlagBits::DontStore ); + pLibInfo->SetLib( pNew ); + pLibInfo->SetLibName( rLibName ); + pLibInfo->GetLib()->SetName( rLibName ); + return pLibInfo->GetLib().get(); +} + +// For XML import/export: +StarBASIC* BasicManager::CreateLib( const OUString& rLibName, const OUString& Password, + const OUString& LinkTargetURL ) +{ + // Ask if lib exists because standard lib is always there + StarBASIC* pLib = GetLib( rLibName ); + if( !pLib ) + { + if( !LinkTargetURL.isEmpty()) + { + try + { + tools::SvRef<SotStorage> xStorage = new SotStorage(false, LinkTargetURL, StreamMode::READ | StreamMode::SHARE_DENYWRITE); + if (!xStorage->GetError()) + { + pLib = AddLib(*xStorage, rLibName, true); + } + } + catch (const css::ucb::ContentCreationException&) + { + TOOLS_WARN_EXCEPTION("basic", "BasicManager::RemoveLib:"); + } + DBG_ASSERT( pLib, "XML Import: Linked basic library could not be loaded"); + } + else + { + pLib = CreateLib( rLibName ); + if( Password.isEmpty()) + { + BasicLibInfo* pLibInfo = FindLibInfo( pLib ); + pLibInfo ->SetPassword( Password ); + } + } + //ExternalSourceURL ? + } + return pLib; +} + +StarBASIC* BasicManager::CreateLibForLibContainer( const OUString& rLibName, + const uno::Reference< script::XLibraryContainer >& xScriptCont ) +{ + if ( GetLib( rLibName ) ) + { + return nullptr; + } + BasicLibInfo* pLibInfo = CreateLibInfo(); + StarBASIC* pNew = new StarBASIC( GetStdLib(), mbDocMgr ); + GetStdLib()->Insert( pNew ); + pNew->SetFlag( SbxFlagBits::ExtSearch | SbxFlagBits::DontStore ); + pLibInfo->SetLib( pNew ); + pLibInfo->SetLibName( rLibName ); + pLibInfo->GetLib()->SetName( rLibName ); + pLibInfo->SetLibraryContainer( xScriptCont ); + return pNew; +} + + +BasicLibInfo* BasicManager::FindLibInfo( StarBASIC const * pBasic ) +{ + for (auto const& rpLib : maLibs) + { + if (rpLib->GetLib().get() == pBasic) + { + return rpLib.get(); + } + } + return nullptr; +} + + +bool BasicManager::IsBasicModified() const +{ + for (auto const& rpLib : maLibs) + { + if (rpLib->GetLib().is() && rpLib->GetLib()->IsModified()) + { + return true; + } + } + return false; +} + + +bool BasicManager::GetGlobalUNOConstant( const OUString& rName, uno::Any& aOut ) +{ + bool bRes = false; + StarBASIC* pStandardLib = GetStdLib(); + OSL_PRECOND( pStandardLib, "BasicManager::GetGlobalUNOConstant: no lib to read from!" ); + if ( pStandardLib ) + bRes = pStandardLib->GetUNOConstant( rName, aOut ); + return bRes; +} + +void BasicManager::SetGlobalUNOConstant( const OUString& rName, const uno::Any& _rValue, css::uno::Any* pOldValue ) +{ + StarBASIC* pStandardLib = GetStdLib(); + OSL_PRECOND( pStandardLib, "BasicManager::SetGlobalUNOConstant: no lib to insert into!" ); + if ( !pStandardLib ) + return; + + if (pOldValue) + { + // obtain the old value + SbxVariable* pVariable = pStandardLib->Find( rName, SbxClassType::Object ); + if ( pVariable ) + *pOldValue = sbxToUnoValue( pVariable ); + } + SbxObjectRef xUnoObj = GetSbUnoObject( _rValue.getValueType ().getTypeName () , _rValue ); + xUnoObj->SetName(rName); + xUnoObj->SetFlag( SbxFlagBits::DontStore ); + pStandardLib->Insert( xUnoObj.get() ); +} + +bool BasicManager::ImgVersion12PsswdBinaryLimitExceeded( std::vector< OUString >& _out_rModuleNames ) +{ + try + { + uno::Reference< container::XNameAccess > xScripts( GetScriptLibraryContainer(), uno::UNO_QUERY_THROW ); + uno::Reference< script::XLibraryContainerPassword > xPassword( GetScriptLibraryContainer(), uno::UNO_QUERY_THROW ); + + const uno::Sequence< OUString > aNames( xScripts->getElementNames() ); + for ( auto const & scriptElementName : aNames ) + { + if( !xPassword->isLibraryPasswordProtected( scriptElementName ) ) + continue; + + StarBASIC* pBasicLib = GetLib( scriptElementName ); + if ( !pBasicLib ) + continue; + + uno::Reference< container::XNameAccess > xScriptLibrary( xScripts->getByName( scriptElementName ), uno::UNO_QUERY_THROW ); + const uno::Sequence< OUString > aElementNames( xScriptLibrary->getElementNames() ); + + std::vector<OUString> aBigModules; + for ( auto const & libraryElementName : aElementNames ) + { + SbModule* pMod = pBasicLib->FindModule( libraryElementName ); + if ( pMod && pMod->ExceedsImgVersion12ModuleSize() ) + aBigModules.push_back(libraryElementName); + } + + if (!aBigModules.empty()) + { + _out_rModuleNames.swap(aBigModules); + return true; + } + } + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + } + return false; +} + + +namespace +{ + SbMethod* lcl_queryMacro( BasicManager* i_manager, OUString const& i_fullyQualifiedName ) + { + sal_Int32 nLast = 0; + const OUString sLibName {i_fullyQualifiedName.getToken( 0, '.', nLast )}; + const OUString sModule {i_fullyQualifiedName.getToken( 0, '.', nLast )}; + OUString sMacro; + if(nLast >= 0) + { + sMacro = i_fullyQualifiedName.copy(nLast); + } + else + { + sMacro = i_fullyQualifiedName; + } + + utl::TransliterationWrapper& rTransliteration = SbGlobal::GetTransliteration(); + sal_uInt16 nLibCount = i_manager->GetLibCount(); + for ( sal_uInt16 nLib = 0; nLib < nLibCount; ++nLib ) + { + if ( rTransliteration.isEqual( i_manager->GetLibName( nLib ), sLibName ) ) + { + StarBASIC* pLib = i_manager->GetLib( nLib ); + if( !pLib ) + { + bool const bLoaded = i_manager->LoadLib( nLib ); + if (bLoaded) + { + pLib = i_manager->GetLib( nLib ); + } + } + + if( pLib ) + { + for ( const auto& pMod: pLib->GetModules() ) + { + if ( rTransliteration.isEqual( pMod->GetName(), sModule ) ) + { + SbMethod* pMethod = static_cast<SbMethod*>(pMod->Find( sMacro, SbxClassType::Method )); + if( pMethod ) + { + return pMethod; + } + } + } + } + } + } + return nullptr; + } +} + +bool BasicManager::HasMacro( OUString const& i_fullyQualifiedName ) const +{ + return ( lcl_queryMacro( const_cast< BasicManager* >( this ), i_fullyQualifiedName ) != nullptr ); +} + +ErrCode BasicManager::ExecuteMacro( OUString const& i_fullyQualifiedName, SbxArray* i_arguments, SbxValue* i_retValue ) +{ + SbMethod* pMethod = lcl_queryMacro( this, i_fullyQualifiedName ); + ErrCode nError = ERRCODE_NONE; + if ( pMethod ) + { + if ( i_arguments ) + pMethod->SetParameters( i_arguments ); + nError = pMethod->Call( i_retValue ); + } + else + nError = ERRCODE_BASIC_PROC_UNDEFINED; + return nError; +} + +ErrCode BasicManager::ExecuteMacro( OUString const& i_fullyQualifiedName, std::u16string_view i_commaSeparatedArgs, SbxValue* i_retValue ) +{ + SbMethod* pMethod = lcl_queryMacro( this, i_fullyQualifiedName ); + if ( !pMethod ) + { + return ERRCODE_BASIC_PROC_UNDEFINED; + } + // arguments must be quoted + OUString sQuotedArgs; + OUStringBuffer sArgs( i_commaSeparatedArgs ); + if ( sArgs.getLength()<2 || sArgs[1] == '\"') + { + // no args or already quoted args + sQuotedArgs = sArgs.makeStringAndClear(); + } + else + { + // quote parameters + sArgs.remove( 0, 1 ); + sArgs.remove( sArgs.getLength() - 1, 1 ); + + OUStringBuffer aBuff; + OUString sArgs2 = sArgs.makeStringAndClear(); + + aBuff.append("("); + if (!sArgs2.isEmpty()) + { + + sal_Int32 nPos {0}; + for (;;) + { + aBuff.append( OUString::Concat("\"") + + o3tl::getToken(sArgs2, 0, ',', nPos) + + "\"" ); + if (nPos<0) + break; + aBuff.append( "," ); + } + } + aBuff.append( ")" ); + + sQuotedArgs = aBuff.makeStringAndClear(); + } + + // add quoted arguments and do the call + OUString sCall = "[" + + pMethod->GetName() + + sQuotedArgs + + "]"; + + SbxVariable* pRet = pMethod->GetParent()->Execute( sCall ); + if ( pRet && ( pRet != pMethod ) ) + { + *i_retValue = *pRet; + } + return SbxBase::GetError(); +} + +namespace { + +class ModuleInfo_Impl : public ModuleInfoHelper +{ + OUString maName; + OUString maLanguage; + OUString maSource; + +public: + ModuleInfo_Impl( OUString aName, OUString aLanguage, OUString aSource ) + : maName(std::move( aName )), maLanguage(std::move( aLanguage)), maSource(std::move( aSource )) {} + + // Methods XStarBasicModuleInfo + virtual OUString SAL_CALL getName() override + { return maName; } + virtual OUString SAL_CALL getLanguage() override + { return maLanguage; } + virtual OUString SAL_CALL getSource() override + { return maSource; } +}; + + +class DialogInfo_Impl : public WeakImplHelper< script::XStarBasicDialogInfo > +{ + OUString maName; + uno::Sequence< sal_Int8 > mData; + +public: + DialogInfo_Impl( OUString aName, const uno::Sequence< sal_Int8 >& Data ) + : maName(std::move( aName )), mData( Data ) {} + + // Methods XStarBasicDialogInfo + virtual OUString SAL_CALL getName() override + { return maName; } + virtual uno::Sequence< sal_Int8 > SAL_CALL getData() override + { return mData; } +}; + + +class LibraryInfo_Impl : public WeakImplHelper< script::XStarBasicLibraryInfo > +{ + OUString maName; + uno::Reference< container::XNameContainer > mxModuleContainer; + uno::Reference< container::XNameContainer > mxDialogContainer; + OUString maPassword; + OUString maExternaleSourceURL; + OUString maLinkTargetURL; + +public: + LibraryInfo_Impl + ( + OUString aName, + uno::Reference< container::XNameContainer > xModuleContainer, + uno::Reference< container::XNameContainer > xDialogContainer, + OUString aPassword, + OUString aExternaleSourceURL, + OUString aLinkTargetURL + ) + : maName(std::move( aName )) + , mxModuleContainer(std::move( xModuleContainer )) + , mxDialogContainer(std::move( xDialogContainer )) + , maPassword(std::move( aPassword )) + , maExternaleSourceURL(std::move( aExternaleSourceURL )) + , maLinkTargetURL(std::move( aLinkTargetURL )) + {} + + // Methods XStarBasicLibraryInfo + virtual OUString SAL_CALL getName() override + { return maName; } + virtual uno::Reference< container::XNameContainer > SAL_CALL getModuleContainer() override + { return mxModuleContainer; } + virtual uno::Reference< container::XNameContainer > SAL_CALL getDialogContainer() override + { return mxDialogContainer; } + virtual OUString SAL_CALL getPassword() override + { return maPassword; } + virtual OUString SAL_CALL getExternalSourceURL() override + { return maExternaleSourceURL; } + virtual OUString SAL_CALL getLinkTargetURL() override + { return maLinkTargetURL; } +}; + + +class ModuleContainer_Impl : public NameContainerHelper +{ + StarBASIC* mpLib; + +public: + explicit ModuleContainer_Impl( StarBASIC* pLib ) + :mpLib( pLib ) {} + + // Methods XElementAccess + virtual uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // Methods XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // Methods XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override; + + // Methods XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; +}; + +} + +// Methods XElementAccess +uno::Type ModuleContainer_Impl::getElementType() +{ + uno::Type aModuleType = cppu::UnoType<script::XStarBasicModuleInfo>::get(); + return aModuleType; +} + +sal_Bool ModuleContainer_Impl::hasElements() +{ + return mpLib && !mpLib->GetModules().empty(); +} + +// Methods XNameAccess +uno::Any ModuleContainer_Impl::getByName( const OUString& aName ) +{ + SbModule* pMod = mpLib ? mpLib->FindModule( aName ) : nullptr; + if( !pMod ) + throw container::NoSuchElementException(); + uno::Reference< script::XStarBasicModuleInfo > xMod = new ModuleInfo_Impl( aName, "StarBasic", pMod->GetSource32() ); + uno::Any aRetAny; + aRetAny <<= xMod; + return aRetAny; +} + +uno::Sequence< OUString > ModuleContainer_Impl::getElementNames() +{ + sal_uInt16 nMods = mpLib ? mpLib->GetModules().size() : 0; + uno::Sequence< OUString > aRetSeq( nMods ); + OUString* pRetSeq = aRetSeq.getArray(); + for( sal_uInt16 i = 0 ; i < nMods ; i++ ) + { + pRetSeq[i] = mpLib->GetModules()[i]->GetName(); + } + return aRetSeq; +} + +sal_Bool ModuleContainer_Impl::hasByName( const OUString& aName ) +{ + SbModule* pMod = mpLib ? mpLib->FindModule( aName ) : nullptr; + bool bRet = (pMod != nullptr); + return bRet; +} + + +// Methods XNameReplace +void ModuleContainer_Impl::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + removeByName( aName ); + insertByName( aName, aElement ); +} + + +// Methods XNameContainer +void ModuleContainer_Impl::insertByName( const OUString& aName, const uno::Any& aElement ) +{ + uno::Type aModuleType = cppu::UnoType<script::XStarBasicModuleInfo>::get(); + const uno::Type& aAnyType = aElement.getValueType(); + if( aModuleType != aAnyType ) + { + throw lang::IllegalArgumentException("types do not match", getXWeak(), 2); + } + uno::Reference< script::XStarBasicModuleInfo > xMod; + aElement >>= xMod; + mpLib->MakeModule( aName, xMod->getSource() ); +} + +void ModuleContainer_Impl::removeByName( const OUString& Name ) +{ + SbModule* pMod = mpLib ? mpLib->FindModule( Name ) : nullptr; + if( !pMod ) + { + throw container::NoSuchElementException(); + } + mpLib->Remove( pMod ); +} + + +static uno::Sequence< sal_Int8 > implGetDialogData( SbxObject* pDialog ) +{ + SvMemoryStream aMemStream; + pDialog->Store( aMemStream ); + sal_Int32 nLen = aMemStream.Tell(); + if (nLen < 0) { abort(); } + uno::Sequence< sal_Int8 > aData( nLen ); + sal_Int8* pDestData = aData.getArray(); + const sal_Int8* pSrcData = static_cast<const sal_Int8*>(aMemStream.GetData()); + memcpy( pDestData, pSrcData, nLen ); + return aData; +} + +static SbxObjectRef implCreateDialog( const uno::Sequence< sal_Int8 >& aData ) +{ + sal_Int8* pData = const_cast< uno::Sequence< sal_Int8 >& >(aData).getArray(); + SvMemoryStream aMemStream( pData, aData.getLength(), StreamMode::READ ); + SbxBaseRef pBase = SbxBase::Load( aMemStream ); + return dynamic_cast<SbxObject*>(pBase.get()); +} + +// HACK! Because this value is defined in basctl/inc/vcsbxdef.hxx +// which we can't include here, we have to use the value directly +#define SBXID_DIALOG 101 + +namespace { + +class DialogContainer_Impl : public NameContainerHelper +{ + StarBASIC* mpLib; + +public: + explicit DialogContainer_Impl( StarBASIC* pLib ) + :mpLib( pLib ) {} + + // Methods XElementAccess + virtual uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // Methods XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // Methods XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override; + + // Methods XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; +}; + +} + +// Methods XElementAccess +uno::Type DialogContainer_Impl::getElementType() +{ + uno::Type aModuleType = cppu::UnoType<script::XStarBasicDialogInfo>::get(); + return aModuleType; +} + +sal_Bool DialogContainer_Impl::hasElements() +{ + bool bRet = false; + + sal_Int32 nCount = mpLib->GetObjects()->Count(); + for( sal_Int32 nObj = 0; nObj < nCount ; nObj++ ) + { + SbxVariable* pVar = mpLib->GetObjects()->Get( nObj ); + SbxObject* pObj = dynamic_cast<SbxObject*>(pVar); + if ( pObj && (pObj->GetSbxId() == SBXID_DIALOG ) ) + { + bRet = true; + break; + } + } + return bRet; +} + +// Methods XNameAccess +uno::Any DialogContainer_Impl::getByName( const OUString& aName ) +{ + SbxVariable* pVar = mpLib->GetObjects()->Find( aName, SbxClassType::DontCare ); + SbxObject* pObj = dynamic_cast<SbxObject*>(pVar); + if( !( pObj && pObj->GetSbxId() == SBXID_DIALOG ) ) + { + throw container::NoSuchElementException(); + } + + uno::Reference< script::XStarBasicDialogInfo > xDialog = + new DialogInfo_Impl(aName, implGetDialogData(pObj)); + + uno::Any aRetAny; + aRetAny <<= xDialog; + return aRetAny; +} + +uno::Sequence< OUString > DialogContainer_Impl::getElementNames() +{ + sal_Int32 nCount = mpLib->GetObjects()->Count(); + uno::Sequence< OUString > aRetSeq( nCount ); + OUString* pRetSeq = aRetSeq.getArray(); + sal_Int32 nDialogCounter = 0; + + for( sal_Int32 nObj = 0; nObj < nCount ; nObj++ ) + { + SbxVariable* pVar = mpLib->GetObjects()->Get( nObj ); + SbxObject* pObj = dynamic_cast<SbxObject*> (pVar); + if ( pObj && ( pObj->GetSbxId() == SBXID_DIALOG ) ) + { + pRetSeq[ nDialogCounter ] = pVar->GetName(); + nDialogCounter++; + } + } + aRetSeq.realloc( nDialogCounter ); + return aRetSeq; +} + +sal_Bool DialogContainer_Impl::hasByName( const OUString& aName ) +{ + bool bRet = false; + SbxVariable* pVar = mpLib->GetObjects()->Find( aName, SbxClassType::DontCare ); + SbxObject* pObj = dynamic_cast<SbxObject*>(pVar); + if( pObj && ( pObj->GetSbxId() == SBXID_DIALOG ) ) + { + bRet = true; + } + return bRet; +} + + +// Methods XNameReplace +void DialogContainer_Impl::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + removeByName( aName ); + insertByName( aName, aElement ); +} + + +// Methods XNameContainer +void DialogContainer_Impl::insertByName( const OUString&, const uno::Any& aElement ) +{ + uno::Type aModuleType = cppu::UnoType<script::XStarBasicDialogInfo>::get(); + const uno::Type& aAnyType = aElement.getValueType(); + if( aModuleType != aAnyType ) + { + throw lang::IllegalArgumentException("types do not match", getXWeak(), 2); + } + uno::Reference< script::XStarBasicDialogInfo > xMod; + aElement >>= xMod; + SbxObjectRef xDialog = implCreateDialog( xMod->getData() ); + mpLib->Insert( xDialog.get() ); +} + +void DialogContainer_Impl::removeByName( const OUString& Name ) +{ + SbxVariable* pVar = mpLib->GetObjects()->Find( Name, SbxClassType::DontCare ); + SbxObject* pObj = dynamic_cast<SbxObject*>(pVar); + if( !( pObj && ( pObj->GetSbxId() == SBXID_DIALOG ) ) ) + { + throw container::NoSuchElementException(); + } + mpLib->Remove( pVar ); +} + + +class LibraryContainer_Impl : public NameContainerHelper +{ + BasicManager* mpMgr; + +public: + explicit LibraryContainer_Impl( BasicManager* pMgr ) + :mpMgr( pMgr ) {} + + // Methods XElementAccess + virtual uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // Methods XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // Methods XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override; + + // Methods XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; +}; + + +// Methods XElementAccess +uno::Type LibraryContainer_Impl::getElementType() +{ + uno::Type aType = cppu::UnoType<script::XStarBasicLibraryInfo>::get(); + return aType; +} + +sal_Bool LibraryContainer_Impl::hasElements() +{ + sal_Int32 nLibs = mpMgr->GetLibCount(); + bool bRet = (nLibs > 0); + return bRet; +} + +// Methods XNameAccess +uno::Any LibraryContainer_Impl::getByName( const OUString& aName ) +{ + uno::Any aRetAny; + if( !mpMgr->HasLib( aName ) ) + throw container::NoSuchElementException(); + StarBASIC* pLib = mpMgr->GetLib( aName ); + + uno::Reference< container::XNameContainer > xModuleContainer = + new ModuleContainer_Impl( pLib ); + + uno::Reference< container::XNameContainer > xDialogContainer = + new DialogContainer_Impl( pLib ); + + BasicLibInfo* pLibInfo = mpMgr->FindLibInfo( pLib ); + + OUString aPassword = pLibInfo->GetPassword(); + + // TODO Only provide extern info! + OUString aExternaleSourceURL; + OUString aLinkTargetURL; + if( pLibInfo->IsReference() ) + { + aLinkTargetURL = pLibInfo->GetStorageName(); + } + else if( pLibInfo->IsExtern() ) + { + aExternaleSourceURL = pLibInfo->GetStorageName(); + } + uno::Reference< script::XStarBasicLibraryInfo > xLibInfo = new LibraryInfo_Impl + ( + aName, + xModuleContainer, + xDialogContainer, + aPassword, + aExternaleSourceURL, + aLinkTargetURL + ); + + aRetAny <<= xLibInfo; + return aRetAny; +} + +uno::Sequence< OUString > LibraryContainer_Impl::getElementNames() +{ + sal_uInt16 nLibs = mpMgr->GetLibCount(); + uno::Sequence< OUString > aRetSeq( nLibs ); + OUString* pRetSeq = aRetSeq.getArray(); + for( sal_uInt16 i = 0 ; i < nLibs ; i++ ) + { + pRetSeq[i] = mpMgr->GetLibName( i ); + } + return aRetSeq; +} + +sal_Bool LibraryContainer_Impl::hasByName( const OUString& aName ) +{ + bool bRet = mpMgr->HasLib( aName ); + return bRet; +} + +// Methods XNameReplace +void LibraryContainer_Impl::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + removeByName( aName ); + insertByName( aName, aElement ); +} + +// Methods XNameContainer +void LibraryContainer_Impl::insertByName( const OUString&, const uno::Any& ) +{ + // TODO: Insert a complete Library?! +} + +void LibraryContainer_Impl::removeByName( const OUString& Name ) +{ + StarBASIC* pLib = mpMgr->GetLib( Name ); + if( !pLib ) + { + throw container::NoSuchElementException(); + } + sal_uInt16 nLibId = mpMgr->GetLibId( Name ); + mpMgr->RemoveLib( nLibId ); +} + + +typedef WeakImplHelper< script::XStarBasicAccess > StarBasicAccessHelper; + + +class StarBasicAccess_Impl : public StarBasicAccessHelper +{ + BasicManager* mpMgr; + uno::Reference< container::XNameContainer > mxLibContainer; + +public: + explicit StarBasicAccess_Impl( BasicManager* pMgr ) + :mpMgr( pMgr ) {} + +public: + // Methods + virtual uno::Reference< container::XNameContainer > SAL_CALL getLibraryContainer() override; + virtual void SAL_CALL createLibrary( const OUString& LibName, const OUString& Password, + const OUString& ExternalSourceURL, const OUString& LinkTargetURL ) override; + virtual void SAL_CALL addModule( const OUString& LibraryName, const OUString& ModuleName, + const OUString& Language, const OUString& Source ) override; + virtual void SAL_CALL addDialog( const OUString& LibraryName, const OUString& DialogName, + const uno::Sequence< sal_Int8 >& Data ) override; +}; + +uno::Reference< container::XNameContainer > SAL_CALL StarBasicAccess_Impl::getLibraryContainer() +{ + if( !mxLibContainer.is() ) + mxLibContainer = new LibraryContainer_Impl( mpMgr ); + return mxLibContainer; +} + +void SAL_CALL StarBasicAccess_Impl::createLibrary +( + const OUString& LibName, + const OUString& Password, + const OUString&, + const OUString& LinkTargetURL +) +{ + StarBASIC* pLib = mpMgr->CreateLib( LibName, Password, LinkTargetURL ); + DBG_ASSERT( pLib, "XML Import: Basic library could not be created"); +} + +void SAL_CALL StarBasicAccess_Impl::addModule +( + const OUString& LibraryName, + const OUString& ModuleName, + const OUString&, + const OUString& Source +) +{ + StarBASIC* pLib = mpMgr->GetLib( LibraryName ); + DBG_ASSERT( pLib, "XML Import: Lib for module unknown"); + if( pLib ) + { + pLib->MakeModule( ModuleName, Source ); + } +} + +void SAL_CALL StarBasicAccess_Impl::addDialog +( + const OUString&, + const OUString&, + const uno::Sequence< sal_Int8 >& +) +{} + +// Basic XML Import/Export +uno::Reference< script::XStarBasicAccess > getStarBasicAccess( BasicManager* pMgr ) +{ + uno::Reference< script::XStarBasicAccess > xRet = + new StarBasicAccess_Impl( pMgr ); + return xRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |