diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /basic/source/runtime | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basic/source/runtime')
-rw-r--r-- | basic/source/runtime/basrdll.cxx | 126 | ||||
-rw-r--r-- | basic/source/runtime/comenumwrapper.cxx | 67 | ||||
-rw-r--r-- | basic/source/runtime/comenumwrapper.hxx | 44 | ||||
-rw-r--r-- | basic/source/runtime/ddectrl.cxx | 198 | ||||
-rw-r--r-- | basic/source/runtime/ddectrl.hxx | 54 | ||||
-rw-r--r-- | basic/source/runtime/dllmgr-none.cxx | 114 | ||||
-rw-r--r-- | basic/source/runtime/dllmgr-x64.cxx | 794 | ||||
-rw-r--r-- | basic/source/runtime/dllmgr-x86.cxx | 734 | ||||
-rw-r--r-- | basic/source/runtime/dllmgr.hxx | 53 | ||||
-rw-r--r-- | basic/source/runtime/inputbox.cxx | 143 | ||||
-rw-r--r-- | basic/source/runtime/iosys.cxx | 849 | ||||
-rw-r--r-- | basic/source/runtime/methods.cxx | 4826 | ||||
-rw-r--r-- | basic/source/runtime/methods1.cxx | 3066 | ||||
-rw-r--r-- | basic/source/runtime/props.cxx | 448 | ||||
-rw-r--r-- | basic/source/runtime/runtime.cxx | 4641 | ||||
-rw-r--r-- | basic/source/runtime/stdobj.cxx | 898 | ||||
-rw-r--r-- | basic/source/runtime/stdobj1.cxx | 422 | ||||
-rw-r--r-- | basic/source/runtime/wnt-x86.asm | 47 |
18 files changed, 17524 insertions, 0 deletions
diff --git a/basic/source/runtime/basrdll.cxx b/basic/source/runtime/basrdll.cxx new file mode 100644 index 000000000..6da6ed9e2 --- /dev/null +++ b/basic/source/runtime/basrdll.cxx @@ -0,0 +1,126 @@ +/* -*- 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 <memory> +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> +#include <vcl/weld.hxx> + +#include <basic/sbstar.hxx> +#include <basic/basrdll.hxx> +#include <strings.hrc> +#include <sbxbase.hxx> +#include <config_features.h> + +namespace +{ +struct BasicDLLImpl : public SvRefBase +{ + bool bDebugMode; + bool bBreakEnabled; + + std::unique_ptr<SbxAppData> xSbxAppData; + + BasicDLLImpl() + : bDebugMode(false) + , bBreakEnabled(true) + , xSbxAppData(new SbxAppData) + { } + + static BasicDLLImpl* BASIC_DLL; + static osl::Mutex& getMutex() + { + static osl::Mutex aMutex; + return aMutex; + } +}; + +BasicDLLImpl* BasicDLLImpl::BASIC_DLL = nullptr; +} + +BasicDLL::BasicDLL() +{ + osl::MutexGuard aGuard(BasicDLLImpl::getMutex()); + if (!BasicDLLImpl::BASIC_DLL) + BasicDLLImpl::BASIC_DLL = new BasicDLLImpl; + m_xImpl = BasicDLLImpl::BASIC_DLL; +} + +BasicDLL::~BasicDLL() +{ + osl::MutexGuard aGuard(BasicDLLImpl::getMutex()); + const bool bLastRef = m_xImpl->GetRefCount() == 1; + if (bLastRef) { + BasicDLLImpl::BASIC_DLL->xSbxAppData->m_aGlobErr.clear(); + } + m_xImpl.clear(); + // only reset BASIC_DLL after the object had been destroyed + if (bLastRef) + BasicDLLImpl::BASIC_DLL = nullptr; +} + +void BasicDLL::EnableBreak( bool bEnable ) +{ + DBG_ASSERT( BasicDLLImpl::BASIC_DLL, "BasicDLL::EnableBreak: No instance yet!" ); + if (BasicDLLImpl::BASIC_DLL) + { + BasicDLLImpl::BASIC_DLL->bBreakEnabled = bEnable; + } +} + +void BasicDLL::SetDebugMode( bool bDebugMode ) +{ + DBG_ASSERT( BasicDLLImpl::BASIC_DLL, "BasicDLL::EnableBreak: No instance yet!" ); + if (BasicDLLImpl::BASIC_DLL) + { + BasicDLLImpl::BASIC_DLL->bDebugMode = bDebugMode; + } +} + + +void BasicDLL::BasicBreak() +{ + DBG_ASSERT( BasicDLLImpl::BASIC_DLL, "BasicDLL::EnableBreak: No instance yet!" ); +#if HAVE_FEATURE_SCRIPTING + if (!BasicDLLImpl::BASIC_DLL) + return; + + // bJustStopping: if there's someone pressing STOP like crazy umpteen times, + // but the Basic doesn't stop early enough, the box might appear more often... + static bool bJustStopping = false; + if (StarBASIC::IsRunning() && !bJustStopping + && (BasicDLLImpl::BASIC_DLL->bBreakEnabled || BasicDLLImpl::BASIC_DLL->bDebugMode)) + { + bJustStopping = true; + StarBASIC::Stop(); + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + BasResId(IDS_SBERR_TERMINATED))); + xInfoBox->run(); + bJustStopping = false; + } +#endif +} + +SbxAppData& GetSbxData_Impl() +{ + return *BasicDLLImpl::BASIC_DLL->xSbxAppData; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/comenumwrapper.cxx b/basic/source/runtime/comenumwrapper.cxx new file mode 100644 index 000000000..7e2432e43 --- /dev/null +++ b/basic/source/runtime/comenumwrapper.cxx @@ -0,0 +1,67 @@ +/* -*- 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 "comenumwrapper.hxx" + +using namespace ::com::sun::star; + +sal_Bool SAL_CALL ComEnumerationWrapper::hasMoreElements() +{ + bool bResult = false; + + try + { + if ( m_xInvocation.is() ) + { + sal_Int32 nLength = 0; + bResult = ( ( m_xInvocation->getValue( "length" ) >>= nLength ) && nLength > m_nCurInd ); + } + } + catch(const uno::Exception& ) + {} + + return bResult; +} + +uno::Any SAL_CALL ComEnumerationWrapper::nextElement() +{ + try + { + if ( m_xInvocation.is() ) + { + uno::Sequence< sal_Int16 > aNamedParamIndex; + uno::Sequence< uno::Any > aNamedParam; + uno::Sequence< uno::Any > aArgs( 1 ); + + aArgs[0] <<= m_nCurInd++; + + return m_xInvocation->invoke( "item", + aArgs, + aNamedParamIndex, + aNamedParam ); + } + } + catch(const uno::Exception& ) + {} + + throw container::NoSuchElementException(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/comenumwrapper.hxx b/basic/source/runtime/comenumwrapper.hxx new file mode 100644 index 000000000..5637a8b7c --- /dev/null +++ b/basic/source/runtime/comenumwrapper.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/script/XInvocation.hpp> + +#include <cppuhelper/implbase.hxx> + +class ComEnumerationWrapper : public ::cppu::WeakImplHelper< css::container::XEnumeration > +{ + css::uno::Reference< css::script::XInvocation > m_xInvocation; + sal_Int32 m_nCurInd; + +public: + explicit ComEnumerationWrapper( const css::uno::Reference< css::script::XInvocation >& xInvocation ) + : m_xInvocation( xInvocation ) + , m_nCurInd( 0 ) + { + } + + // container::XEnumeration + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual css::uno::Any SAL_CALL nextElement() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/ddectrl.cxx b/basic/source/runtime/ddectrl.cxx new file mode 100644 index 000000000..37d3c7d0c --- /dev/null +++ b/basic/source/runtime/ddectrl.cxx @@ -0,0 +1,198 @@ +/* -*- 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 <vcl/errcode.hxx> +#include <svl/svdde.hxx> +#include "ddectrl.hxx" +#include <basic/sberrors.hxx> + +#define DDE_FIRSTERR 0x4000 +#define DDE_LASTERR 0x4011 + +static const ErrCode nDdeErrMap[] = +{ + /* DMLERR_ADVACKTIMEOUT */ ErrCode(0x4000), ERRCODE_BASIC_DDE_TIMEOUT, + /* DMLERR_BUSY */ ErrCode(0x4001), ERRCODE_BASIC_DDE_BUSY, + /* DMLERR_DATAACKTIMEOUT */ ErrCode(0x4002), ERRCODE_BASIC_DDE_TIMEOUT, + /* DMLERR_DLL_NOT_INITIALIZED */ ErrCode(0x4003), ERRCODE_BASIC_DDE_ERROR, + /* DMLERR_DLL_USAGE */ ErrCode(0x4004), ERRCODE_BASIC_DDE_ERROR, + /* DMLERR_EXECACKTIMEOUT */ ErrCode(0x4005), ERRCODE_BASIC_DDE_TIMEOUT, + /* DMLERR_INVALIDPARAMETER */ ErrCode(0x4006), ERRCODE_BASIC_DDE_ERROR, + /* DMLERR_LOW_MEMORY */ ErrCode(0x4007), ERRCODE_BASIC_DDE_ERROR, + /* DMLERR_MEMORY_ERROR */ ErrCode(0x4008), ERRCODE_BASIC_DDE_ERROR, + /* DMLERR_NOTPROCESSED */ ErrCode(0x4009), ERRCODE_BASIC_DDE_NOTPROCESSED, + /* DMLERR_NO_CONV_ESTABLISHED */ ErrCode(0x400a), ERRCODE_BASIC_DDE_NO_CHANNEL, + /* DMLERR_POKEACKTIMEOUT */ ErrCode(0x400b), ERRCODE_BASIC_DDE_TIMEOUT, + /* DMLERR_POSTMSG_FAILED */ ErrCode(0x400c), ERRCODE_BASIC_DDE_QUEUE_OVERFLOW, + /* DMLERR_REENTRANCY */ ErrCode(0x400d), ERRCODE_BASIC_DDE_ERROR, + /* DMLERR_SERVER_DIED */ ErrCode(0x400e), ERRCODE_BASIC_DDE_PARTNER_QUIT, + /* DMLERR_SYS_ERROR */ ErrCode(0x400f), ERRCODE_BASIC_DDE_ERROR, + /* DMLERR_UNADVACKTIMEOUT */ ErrCode(0x4010), ERRCODE_BASIC_DDE_TIMEOUT, + /* DMLERR_UNFOUND_QUEUE_ID */ ErrCode(0x4011), ERRCODE_BASIC_DDE_NO_CHANNEL +}; + +ErrCode SbiDdeControl::GetLastErr( const DdeConnection* pConv ) +{ + if( !pConv ) + { + return ERRCODE_NONE; + } + long nErr = pConv->GetError(); + if( !nErr ) + { + return ERRCODE_NONE; + } + if( nErr < DDE_FIRSTERR || nErr > DDE_LASTERR ) + { + return ERRCODE_BASIC_DDE_ERROR; + } + return nDdeErrMap[ 2 * (nErr - DDE_FIRSTERR) + 1 ]; +} + +IMPL_LINK( SbiDdeControl, Data, const DdeData*, pData, void ) +{ + aData = OUString::createFromAscii( static_cast<const char*>(pData->getData()) ); +} + +SbiDdeControl::SbiDdeControl() +{ +} + +SbiDdeControl::~SbiDdeControl() +{ + TerminateAll(); +} + +size_t SbiDdeControl::GetFreeChannel() +{ + size_t nChannel = 0; + size_t nListSize = aConvList.size(); + + for (; nChannel < nListSize; ++nChannel) + { + if (!aConvList[nChannel]) + { + return nChannel+1; + } + } + + aConvList.push_back(nullptr); + return nChannel+1; +} + +ErrCode SbiDdeControl::Initiate( const OUString& rService, const OUString& rTopic, + size_t& rnHandle ) +{ + ErrCode nErr; + std::unique_ptr<DdeConnection> pConv(new DdeConnection( rService, rTopic )); + nErr = GetLastErr( pConv.get() ); + if( nErr ) + { + rnHandle = 0; + } + else + { + size_t nChannel = GetFreeChannel(); + aConvList[nChannel-1] = std::move(pConv); + rnHandle = nChannel; + } + return ERRCODE_NONE; +} + +ErrCode SbiDdeControl::Terminate( size_t nChannel ) +{ + if (!nChannel || nChannel > aConvList.size()) + { + return ERRCODE_BASIC_DDE_NO_CHANNEL; + } + DdeConnection* pConv = aConvList[nChannel-1].get(); + + if( !pConv ) + { + return ERRCODE_BASIC_DDE_NO_CHANNEL; + } + aConvList[nChannel-1].reset(); + + return ERRCODE_NONE; +} + +ErrCode SbiDdeControl::TerminateAll() +{ + aConvList.clear(); + return ERRCODE_NONE; +} + +ErrCode SbiDdeControl::Request( size_t nChannel, const OUString& rItem, OUString& rResult ) +{ + if (!nChannel || nChannel > aConvList.size()) + { + return ERRCODE_BASIC_DDE_NO_CHANNEL; + } + + DdeConnection* pConv = aConvList[nChannel-1].get(); + + if( !pConv ) + { + return ERRCODE_BASIC_DDE_NO_CHANNEL; + } + + DdeRequest aRequest( *pConv, rItem, 30000 ); + aRequest.SetDataHdl( LINK( this, SbiDdeControl, Data ) ); + aRequest.Execute(); + rResult = aData; + return GetLastErr( pConv ); +} + +ErrCode SbiDdeControl::Execute( size_t nChannel, const OUString& rCommand ) +{ + if (!nChannel || nChannel > aConvList.size()) + { + return ERRCODE_BASIC_DDE_NO_CHANNEL; + } + + DdeConnection* pConv = aConvList[nChannel-1].get(); + + if( !pConv ) + { + return ERRCODE_BASIC_DDE_NO_CHANNEL; + } + DdeExecute aRequest( *pConv, rCommand, 30000 ); + aRequest.Execute(); + return GetLastErr( pConv ); +} + +ErrCode SbiDdeControl::Poke( size_t nChannel, const OUString& rItem, const OUString& rData ) +{ + if (!nChannel || nChannel > aConvList.size()) + { + return ERRCODE_BASIC_DDE_NO_CHANNEL; + } + DdeConnection* pConv = aConvList[nChannel-1].get(); + + if( !pConv ) + { + return ERRCODE_BASIC_DDE_NO_CHANNEL; + } + DdePoke aRequest( *pConv, rItem, DdeData(rData), 30000 ); + aRequest.Execute(); + return GetLastErr( pConv ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/ddectrl.hxx b/basic/source/runtime/ddectrl.hxx new file mode 100644 index 000000000..3b4f3d9bb --- /dev/null +++ b/basic/source/runtime/ddectrl.hxx @@ -0,0 +1,54 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/link.hxx> +#include <vcl/errcode.hxx> + +#include <memory> +#include <vector> + +class DdeConnection; +class DdeData; + +class SbiDdeControl +{ +private: + DECL_LINK( Data, const DdeData*, void ); + static ErrCode GetLastErr( const DdeConnection* ); + size_t GetFreeChannel(); + std::vector<std::unique_ptr<DdeConnection>> aConvList; + OUString aData; + +public: + + SbiDdeControl(); + ~SbiDdeControl(); + + ErrCode Initiate( const OUString& rService, const OUString& rTopic, + size_t& rnHandle ); + ErrCode Terminate( size_t nChannel ); + ErrCode TerminateAll(); + ErrCode Request( size_t nChannel, const OUString& rItem, OUString& rResult ); + ErrCode Execute( size_t nChannel, const OUString& rCommand ); + ErrCode Poke( size_t nChannel, const OUString& rItem, const OUString& rData ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/dllmgr-none.cxx b/basic/source/runtime/dllmgr-none.cxx new file mode 100644 index 000000000..7ca7e5131 --- /dev/null +++ b/basic/source/runtime/dllmgr-none.cxx @@ -0,0 +1,114 @@ +/* -*- 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 <sal/config.h> + +#if defined(_WIN32) +#include <prewin.h> +#include <postwin.h> +#endif + +#include <basic/sberrors.hxx> +#include <basic/sbx.hxx> +#include <basic/sbxvar.hxx> +#include <rtl/ustring.hxx> +#include <osl/time.h> + +#include "dllmgr.hxx" + +struct SbiDllMgr::Impl {}; + +namespace { + +// Overcome the mess of Currency vs. custom types etc. +ErrCode returnInt64InOutArg(SbxArray *pArgs, SbxVariable &rRetVal, + sal_Int64 nValue) +{ + if (!rRetVal.PutLong(1) && !rRetVal.PutInteger(1)) + return ERRCODE_BASIC_BAD_ARGUMENT; + if (!pArgs || pArgs->Count32() != 2) + return ERRCODE_BASIC_BAD_ARGUMENT; + SbxVariable *pOut = pArgs->Get32(1); + if (!pOut) + return ERRCODE_BASIC_BAD_ARGUMENT; + if (pOut->IsCurrency()) + { + pOut->PutCurrency(nValue); + return ERRCODE_NONE; + } + if (pOut->IsObject()) + { + // FIXME: should we clone this and use pOut->PutObject ? + SbxObject* pObj = dynamic_cast<SbxObject*>( pOut->GetObject() ); + if (!pObj) + return ERRCODE_BASIC_BAD_ARGUMENT; + + // We expect two Longs but other mappings could be possible too. + SbxArray* pProps = pObj->GetProperties(); + if (pProps->Count32() != 2) + return ERRCODE_BASIC_BAD_ARGUMENT; + SbxVariable* pLow = pProps->Get32( 0 ); + SbxVariable* pHigh = pProps->Get32( 1 ); + if (!pLow || !pLow->IsLong() || + !pHigh || !pHigh->IsLong()) + return ERRCODE_BASIC_BAD_ARGUMENT; + pLow->PutLong(nValue & 0xffffffff); + pHigh->PutLong(nValue >> 32); + return ERRCODE_NONE; + } + return ERRCODE_BASIC_BAD_ARGUMENT; +} + +ErrCode builtin_kernel32(const OUString &aFuncName, SbxArray *pArgs, + SbxVariable &rRetVal) +{ + sal_Int64 nNanoSecsPerSec = 1000.0*1000*1000; + if (aFuncName == "QueryPerformanceFrequency") + return returnInt64InOutArg(pArgs, rRetVal, nNanoSecsPerSec); + + else if (aFuncName == "QueryPerformanceCounter") + { + TimeValue aNow; + osl_getSystemTime( &aNow ); + sal_Int64 nStamp = aNow.Nanosec + aNow.Seconds * nNanoSecsPerSec; + return returnInt64InOutArg(pArgs, rRetVal, nStamp); + } + return ERRCODE_BASIC_NOT_IMPLEMENTED; +} + +}; + +ErrCode SbiDllMgr::Call( + const OUString &aFuncName, const OUString &aDllName, + SbxArray *pArgs, SbxVariable &rRetVal, + SAL_UNUSED_PARAMETER bool /* bCDecl */) +{ + if (aDllName == "kernel32") + return builtin_kernel32(aFuncName, pArgs, rRetVal); + else + return ERRCODE_BASIC_NOT_IMPLEMENTED; +} + +void SbiDllMgr::FreeDll(SAL_UNUSED_PARAMETER OUString const &) {} + +SbiDllMgr::SbiDllMgr(): impl_(new Impl) {} + +SbiDllMgr::~SbiDllMgr() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/dllmgr-x64.cxx b/basic/source/runtime/dllmgr-x64.cxx new file mode 100644 index 000000000..0cc276192 --- /dev/null +++ b/basic/source/runtime/dllmgr-x64.cxx @@ -0,0 +1,794 @@ +/* -*- 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 <sal/config.h> + +#if defined(_WIN32) +#include <prewin.h> +#include <postwin.h> +#endif + +#include <algorithm> +#include <cstddef> +#include <map> +#include <vector> + +#include <basic/sbx.hxx> +#include <basic/sbxvar.hxx> +#include <runtime.hxx> +#include <osl/thread.h> +#include <osl/diagnose.h> +#include <rtl/ref.hxx> +#include <rtl/string.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <salhelper/simplereferenceobject.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#undef max + +#include "dllmgr.hxx" + +using namespace css; + +/* Open issues: + + Missing support for functions returning structs (see TODO in call()). + + Missing support for additional data types (64 bit integers, Any, ...; would + trigger assert(false) in various switches). + + It is assumed that the variables passed into SbiDllMgr::Call to represent + the arguments and return value have types that exactly match the Declare + statement; it would be better if this code had access to the function + signature from the Declare statement, so that it could convert the passed + variables accordingly. +*/ + +namespace { + +char * address(std::vector< char > & blob) { + return blob.empty() ? nullptr : blob.data(); +} + +ErrCode convert(OUString const & source, OString * target) { + return + source.convertToString( + target, osl_getThreadTextEncoding(), + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) + ? ERRCODE_NONE : ERRCODE_BASIC_BAD_ARGUMENT; + //TODO: more specific errcode? +} + +ErrCode convert(char const * source, sal_Int32 length, OUString * target) { + return + rtl_convertStringToUString( + &target->pData, source, length, osl_getThreadTextEncoding(), + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) + ? ERRCODE_NONE : ERRCODE_BASIC_BAD_ARGUMENT; + //TODO: more specific errcode? +} + +struct UnmarshalData { + UnmarshalData(SbxVariable * theVariable, void * theBuffer): + variable(theVariable), buffer(theBuffer) {} + + SbxVariable * variable; + void * buffer; +}; + +struct StringData: public UnmarshalData { + StringData(SbxVariable * theVariable, void * theBuffer, bool theSpecial): + UnmarshalData(theVariable, theBuffer), special(theSpecial) {} + + bool special; +}; + +class MarshalData { +public: + MarshalData() = default; + MarshalData(const MarshalData&) = delete; + const MarshalData& operator=(const MarshalData&) = delete; + + std::vector< char > * newBlob() { + blobs_.push_back(std::vector< char >()); + return &blobs_.back(); + } + + std::vector< UnmarshalData > unmarshal; + + std::vector< StringData > unmarshalStrings; + +private: + std::vector< std::vector< char > > blobs_; +}; + +std::size_t align(std::size_t address, std::size_t alignment) { + // alignment = 2^k for some k >= 0 + return (address + (alignment - 1)) & ~(alignment - 1); +} + +char * align( + std::vector< char > & blob, std::size_t alignment, std::size_t offset, + std::size_t add) +{ + std::vector< char >::size_type n = blob.size(); + n = align(n - offset, alignment) + offset; //TODO: overflow in align() + blob.resize(n + add); //TODO: overflow + return address(blob) + n; +} + +template< typename T > void add( + std::vector< char > & blob, T const & data, std::size_t alignment, + std::size_t offset) +{ + *reinterpret_cast< T * >(align(blob, alignment, offset, sizeof (T))) = data; +} + +std::size_t alignment(SbxVariable const * variable) { + assert(variable != nullptr); + if ((variable->GetType() & SbxARRAY) == 0) { + switch (variable->GetType()) { + case SbxINTEGER: + return 2; + case SbxLONG: + case SbxSINGLE: + case SbxSTRING: + return 4; + case SbxDOUBLE: + return 8; + case SbxOBJECT: + { + std::size_t n = 1; + SbxObject* pobj = dynamic_cast<SbxObject*>(variable->GetObject()); + assert(pobj); + SbxArray* props = pobj->GetProperties(); + for (sal_uInt32 i = 0; i < props->Count32(); ++i) { + n = std::max(n, alignment(props->Get32(i))); + } + return n; + } + case SbxBOOL: + case SbxBYTE: + return 1; + default: + assert(false); + return 1; + } + } else { + SbxDimArray * arr = dynamic_cast<SbxDimArray*>( variable->GetObject() ); + assert(arr); + sal_Int32 dims = arr->GetDims32(); + std::vector< sal_Int32 > low(dims); + for (sal_Int32 i = 0; i < dims; ++i) { + sal_Int32 up; + arr->GetDim32(i + 1, low[i], up); + } + return alignment(arr->Get32(low.data())); + } +} + +ErrCode marshal( + bool outer, SbxVariable * variable, bool special, + std::vector< char > & blob, std::size_t offset, MarshalData & data); + +ErrCode marshalString( + SbxVariable * variable, bool special, MarshalData & data, void ** buffer) +{ + assert(variable != nullptr && buffer != nullptr); + OString str; + ErrCode e = convert(variable->GetOUString(), &str); + if (e != ERRCODE_NONE) { + return e; + } + std::vector< char > * blob = data.newBlob(); + blob->insert(blob->begin(), str.getStr(), str.getStr() + str.getLength() + 1); + *buffer = address(*blob); + data.unmarshalStrings.push_back(StringData(variable, *buffer, special)); + return ERRCODE_NONE; +} + +ErrCode marshalStruct( + SbxVariable const * variable, std::vector< char > & blob, std::size_t offset, + MarshalData & data) +{ + assert(variable != nullptr); + SbxObject* pobj = dynamic_cast<SbxObject*>(variable->GetObject()); + assert(pobj); + SbxArray* props = pobj->GetProperties(); + for (sal_uInt32 i = 0; i < props->Count32(); ++i) { + ErrCode e = marshal(false, props->Get32(i), false, blob, offset, data); + if (e != ERRCODE_NONE) { + return e; + } + } + return ERRCODE_NONE; +} + +ErrCode marshalArray( + SbxVariable const * variable, std::vector< char > & blob, std::size_t offset, + MarshalData & data) +{ + assert(variable != nullptr); + SbxDimArray * arr = dynamic_cast<SbxDimArray*>( variable->GetObject() ); + assert(arr); + sal_Int32 dims = arr->GetDims32(); + std::vector< sal_Int32 > low(dims); + std::vector< sal_Int32 > up(dims); + for (sal_Int32 i = 0; i < dims; ++i) { + arr->GetDim32(i + 1, low[i], up[i]); + } + for (std::vector< sal_Int32 > idx = low;;) { + ErrCode e = marshal( + false, arr->Get32(idx.data()), false, blob, offset, data); + if (e != ERRCODE_NONE) { + return e; + } + sal_Int32 i = dims - 1; + while (idx[i] == up[i]) { + idx[i] = low[i]; + if (i == 0) { + return ERRCODE_NONE; + } + --i; + } + ++idx[i]; + } +} + +// 8-aligned structs are only 4-aligned on stack, so alignment of members in +// such structs must take that into account via "offset" +ErrCode marshal( + bool outer, SbxVariable * variable, bool special, + std::vector< char > & blob, std::size_t offset, MarshalData & data) +{ + assert(variable != nullptr); + + SbxDataType eVarType = variable->GetType(); + bool bByVal = !(variable->GetFlags() & SbxFlagBits::Reference); + if( !bByVal && !SbiRuntime::isVBAEnabled() && eVarType == SbxSTRING ) + bByVal = true; + + if (bByVal) { + if ((eVarType & SbxARRAY) == 0) { + switch (eVarType) { + case SbxINTEGER: + add(blob, variable->GetInteger(), outer ? 8 : 2, offset); + break; + case SbxLONG: + add(blob, variable->GetLong(), outer ? 8 : 4, offset); + break; + case SbxSINGLE: + add(blob, variable->GetSingle(), outer ? 8 : 4, offset); + break; + case SbxDOUBLE: + add(blob, variable->GetDouble(), 8, offset); + break; + case SbxSTRING: + { + void * p; + ErrCode e = marshalString(variable, special, data, &p); + if (e != ERRCODE_NONE) { + return e; + } + add(blob, p, 8, offset); + break; + } + case SbxOBJECT: + { + align(blob, outer ? 8 : alignment(variable), offset, 0); + ErrCode e = marshalStruct(variable, blob, offset, data); + if (e != ERRCODE_NONE) { + return e; + } + break; + } + case SbxBOOL: + add(blob, variable->GetBool(), outer ? 8 : 1, offset); + break; + case SbxBYTE: + add(blob, variable->GetByte(), outer ? 8 : 1, offset); + break; + default: + assert(false); + break; + } + } else { + ErrCode e = marshalArray(variable, blob, offset, data); + if (e != ERRCODE_NONE) { + return e; + } + } + } else { + if ((eVarType & SbxARRAY) == 0) { + switch (eVarType) { + case SbxINTEGER: + case SbxLONG: + case SbxSINGLE: + case SbxDOUBLE: + case SbxBOOL: + case SbxBYTE: + add(blob, variable->data(), 8, offset); + break; + case SbxSTRING: + { + void * p; + ErrCode e = marshalString(variable, special, data, &p); + if (e != ERRCODE_NONE) { + return e; + } + std::vector< char >* blob2 = data.newBlob(); + add(*blob2, p, 8, 0); + add(blob, address(*blob2), 8, offset); + break; + } + case SbxOBJECT: + { + std::vector< char > * blob2 = data.newBlob(); + ErrCode e = marshalStruct(variable, *blob2, 0, data); + if (e != ERRCODE_NONE) { + return e; + } + void * p = address(*blob2); + if (outer) { + data.unmarshal.push_back(UnmarshalData(variable, p)); + } + add(blob, p, 8, offset); + break; + } + default: + assert(false); + break; + } + } else { + std::vector< char > * blob2 = data.newBlob(); + ErrCode e = marshalArray(variable, *blob2, 0, data); + if (e != ERRCODE_NONE) { + return e; + } + void * p = address(*blob2); + if (outer) { + data.unmarshal.push_back(UnmarshalData(variable, p)); + } + add(blob, p, 8, offset); + } + } + return ERRCODE_NONE; +} + +template< typename T > T read(void const ** pointer) { + T const * p = static_cast< T const * >(*pointer); + *pointer = static_cast< void const * >(p + 1); + return *p; +} + +void const * unmarshal(SbxVariable * variable, void const * data) { + assert(variable != nullptr); + if ((variable->GetType() & SbxARRAY) == 0) { + switch (variable->GetType()) { + case SbxINTEGER: + variable->PutInteger(read< sal_Int16 >(&data)); + break; + case SbxLONG: + variable->PutLong(read< sal_Int32 >(&data)); + break; + case SbxSINGLE: + variable->PutSingle(read< float >(&data)); + break; + case SbxDOUBLE: + variable->PutDouble(read< double >(&data)); + break; + case SbxSTRING: + read< char * >(&data); // handled by unmarshalString + break; + case SbxOBJECT: + { + data = reinterpret_cast< void const * >( + align( + reinterpret_cast< sal_uIntPtr >(data), + alignment(variable))); + SbxObject* pobj = dynamic_cast<SbxObject*>(variable->GetObject()); + assert(pobj); + SbxArray* props = pobj->GetProperties(); + for (sal_uInt32 i = 0; i < props->Count32(); ++i) { + data = unmarshal(props->Get32(i), data); + } + break; + } + case SbxBOOL: + variable->PutBool(read< sal_Bool >(&data)); + break; + case SbxBYTE: + variable->PutByte(read< sal_uInt8 >(&data)); + break; + default: + assert(false); + break; + } + } else { + SbxDimArray * arr = dynamic_cast<SbxDimArray*>( variable->GetObject() ); + assert(arr); + sal_Int32 dims = arr->GetDims32(); + std::vector< sal_Int32 > low(dims); + std::vector< sal_Int32 > up(dims); + for (sal_Int32 i = 0; i < dims; ++i) { + arr->GetDim32(i + 1, low[i], up[i]); + } + for (std::vector< sal_Int32 > idx = low;;) { + data = unmarshal(arr->Get32(idx.data()), data); + sal_Int32 i = dims - 1; + while (idx[i] == up[i]) { + idx[i] = low[i]; + if (i == 0) { + goto done; + } + --i; + } + ++idx[i]; + } + done:; + } + return data; +} + +ErrCode unmarshalString(StringData const & data, SbxVariable const & result) { + OUString str; + if (data.buffer != nullptr) { + char const * p = static_cast< char const * >(data.buffer); + sal_Int32 len; + if (data.special) { + len = static_cast< sal_Int32 >(result.GetULong()); + if (len < 0) { // i.e., DWORD result >= 2^31 + return ERRCODE_BASIC_BAD_ARGUMENT; + //TODO: more specific errcode? + } + } else { + len = rtl_str_getLength(p); + } + ErrCode e = convert(p, len, &str); + if (e != ERRCODE_NONE) { + return e; + } + } + data.variable->PutString(str); + return ERRCODE_NONE; +} + +struct ProcData { + OString name; + FARPROC proc; +}; + +ErrCode call( + OUString const & dll, ProcData const & proc, SbxArray * arguments, + SbxVariable & result) +{ + if (arguments && arguments->Count32() > 20) + return ERRCODE_BASIC_NOT_IMPLEMENTED; + + std::vector< char > stack; + MarshalData data; + + // For DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer) + // from kernel32, upon return, filled lpBuffer length is result DWORD, which + // requires special handling in unmarshalString; other functions might + // require similar treatment, too: + bool special = + dll.equalsIgnoreAsciiCase("KERNEL32.DLL") && + (proc.name == OString("GetLogicalDriveStringsA")); + for (sal_uInt32 i = 1; i < (arguments == nullptr ? 0 : arguments->Count32()); ++i) { + ErrCode e = marshal( + true, arguments->Get32(i), special && i == 2, stack, stack.size(), + data); + if (e != ERRCODE_NONE) { + return e; + } + align(stack, 8, 0, 0); + } + + stack.resize(20*8); + + // We fake all calls as being to a varargs function, + // as this means any floating-point argument among the first four + // ones will end up in a XMM register where the callee expects it. + sal_Int32 (*proc_i)(double d, ...) = reinterpret_cast<sal_Int32 (*)(double, ...)>(proc.proc); + double (*proc_d)(double d, ...) = reinterpret_cast<double (*)(double, ...)>(proc.proc); + + sal_Int64 iRetVal = 0; + double dRetVal = 0.0; + + switch (result.GetType()) { + case SbxEMPTY: + case SbxINTEGER: + case SbxLONG: + case SbxSTRING: + case SbxOBJECT: + case SbxBOOL: + case SbxBYTE: + { + auto const st = stack.data(); + iRetVal = + proc_i(*reinterpret_cast<double *>(st + 0), + *reinterpret_cast<double *>(st + 1*8), + *reinterpret_cast<double *>(st + 2*8), + *reinterpret_cast<double *>(st + 3*8), + *reinterpret_cast<sal_uInt64 *>(st + 4*8), + *reinterpret_cast<sal_uInt64 *>(st + 5*8), + *reinterpret_cast<sal_uInt64 *>(st + 6*8), + *reinterpret_cast<sal_uInt64 *>(st + 7*8), + *reinterpret_cast<sal_uInt64 *>(st + 8*8), + *reinterpret_cast<sal_uInt64 *>(st + 9*8), + *reinterpret_cast<sal_uInt64 *>(st + 10*8), + *reinterpret_cast<sal_uInt64 *>(st + 11*8), + *reinterpret_cast<sal_uInt64 *>(st + 12*8), + *reinterpret_cast<sal_uInt64 *>(st + 13*8), + *reinterpret_cast<sal_uInt64 *>(st + 14*8), + *reinterpret_cast<sal_uInt64 *>(st + 15*8), + *reinterpret_cast<sal_uInt64 *>(st + 16*8), + *reinterpret_cast<sal_uInt64 *>(st + 17*8), + *reinterpret_cast<sal_uInt64 *>(st + 18*8), + *reinterpret_cast<sal_uInt64 *>(st + 19*8)); + break; + } + case SbxSINGLE: + case SbxDOUBLE: + { + auto const st = stack.data(); + dRetVal = + proc_d(*reinterpret_cast<double *>(st + 0), + *reinterpret_cast<double *>(st + 1*8), + *reinterpret_cast<double *>(st + 2*8), + *reinterpret_cast<double *>(st + 3*8), + *reinterpret_cast<sal_uInt64 *>(st + 4*8), + *reinterpret_cast<sal_uInt64 *>(st + 5*8), + *reinterpret_cast<sal_uInt64 *>(st + 6*8), + *reinterpret_cast<sal_uInt64 *>(st + 7*8), + *reinterpret_cast<sal_uInt64 *>(st + 8*8), + *reinterpret_cast<sal_uInt64 *>(st + 9*8), + *reinterpret_cast<sal_uInt64 *>(st + 10*8), + *reinterpret_cast<sal_uInt64 *>(st + 11*8), + *reinterpret_cast<sal_uInt64 *>(st + 12*8), + *reinterpret_cast<sal_uInt64 *>(st + 13*8), + *reinterpret_cast<sal_uInt64 *>(st + 14*8), + *reinterpret_cast<sal_uInt64 *>(st + 15*8), + *reinterpret_cast<sal_uInt64 *>(st + 16*8), + *reinterpret_cast<sal_uInt64 *>(st + 17*8), + *reinterpret_cast<sal_uInt64 *>(st + 18*8), + *reinterpret_cast<sal_uInt64 *>(st + 19*8)); + break; + } + default: + break; + } + + switch (result.GetType()) { + case SbxEMPTY: + break; + case SbxINTEGER: + result.PutInteger(static_cast< sal_Int16 >(iRetVal)); + break; + case SbxLONG: + result.PutLong(static_cast< sal_Int32 >(iRetVal)); + break; + case SbxSINGLE: + result.PutSingle(static_cast< float >(dRetVal)); + break; + case SbxDOUBLE: + result.PutDouble(dRetVal); + break; + case SbxSTRING: + { + char const * s1 = reinterpret_cast< char const * >(iRetVal); + OUString s2; + ErrCode e = convert(s1, rtl_str_getLength(s1), &s2); + if (e != ERRCODE_NONE) { + return e; + } + result.PutString(s2); + break; + } + case SbxOBJECT: + //TODO + break; + case SbxBOOL: + result.PutBool(bool(iRetVal)); + break; + case SbxBYTE: + result.PutByte(static_cast< sal_uInt8 >(iRetVal)); + break; + default: + assert(false); + break; + } + for (sal_uInt32 i = 1; i < (arguments == nullptr ? 0 : arguments->Count32()); ++i) { + arguments->Get32(i)->ResetFlag(SbxFlagBits::Reference); + //TODO: skipped for errors?!? + } + for (auto const& elem : data.unmarshal) + { + unmarshal(elem.variable, elem.buffer); + } + for (auto const& elem : data.unmarshalStrings) + { + ErrCode e = unmarshalString(elem, result); + if (e != ERRCODE_NONE) { + return e; + } + } + return ERRCODE_NONE; +} + +ErrCode getProcData(HMODULE handle, OUString const & name, ProcData * proc) +{ + assert(proc != nullptr); + if (name.getLength() != 0 && name[0] == '@') { //TODO: "@" vs. "#"??? + sal_Int32 n = name.copy(1).toInt32(); //TODO: handle bad input + if (n <= 0 || n > 0xFFFF) { + return ERRCODE_BASIC_BAD_ARGUMENT; //TODO: more specific errcode? + } + FARPROC p = GetProcAddress(handle, reinterpret_cast< LPCSTR >(n)); + if (p != nullptr) { + proc->name = "#" + OString::number(n); + proc->proc = p; + return ERRCODE_NONE; + } + } else { + OString name8; + ErrCode e = convert(name, &name8); + if (e != ERRCODE_NONE) { + return e; + } + FARPROC p = GetProcAddress(handle, name8.getStr()); + if (p != nullptr) { + proc->name = name8; + proc->proc = p; + return ERRCODE_NONE; + } + sal_Int32 i = name8.indexOf('#'); + if (i != -1) { + name8 = name8.copy(0, i); + p = GetProcAddress(handle, name8.getStr()); + if (p != nullptr) { + proc->name = name8; + proc->proc = p; + return ERRCODE_NONE; + } + } + OString real("_" + name8); + p = GetProcAddress(handle, real.getStr()); + if (p != nullptr) { + proc->name = real; + proc->proc = p; + return ERRCODE_NONE; + } + real = name8 + "A"; + p = GetProcAddress(handle, real.getStr()); + if (p != nullptr) { + proc->name = real; + proc->proc = p; + return ERRCODE_NONE; + } + } + return ERRCODE_BASIC_PROC_UNDEFINED; +} + +struct Dll: public salhelper::SimpleReferenceObject { +private: + typedef std::map< OUString, ProcData > Procs; + + virtual ~Dll() override; + +public: + Dll(): handle(nullptr) {} + + ErrCode getProc(OUString const & name, ProcData * proc); + + HMODULE handle; + Procs procs; +}; + +Dll::~Dll() { + if (handle != nullptr && !FreeLibrary(handle)) { + SAL_WARN("basic", "FreeLibrary(" << handle << ") failed with " << GetLastError()); + } +} + +ErrCode Dll::getProc(OUString const & name, ProcData * proc) { + Procs::iterator i(procs.find(name)); + if (i != procs.end()) { + *proc = i->second; + return ERRCODE_NONE; + } + ErrCode e = getProcData(handle, name, proc); + if (e == ERRCODE_NONE) { + procs.emplace(name, *proc); + } + return e; +} + +OUString fullDllName(OUString const & name) { + OUString full(name); + if (full.indexOf('.') == -1) { + full += ".DLL"; + } + return full; +} + +} + +struct SbiDllMgr::Impl{ +private: + typedef std::map< OUString, ::rtl::Reference< Dll > > Dlls; + +public: + Impl() = default; + Impl(const Impl&) = delete; + const Impl& operator=(const Impl&) = delete; + + Dll * getDll(OUString const & name); + + Dlls dlls; +}; + +Dll * SbiDllMgr::Impl::getDll(OUString const & name) { + Dlls::iterator i(dlls.find(name)); + if (i == dlls.end()) { + i = dlls.emplace(name, new Dll).first; + HMODULE h = LoadLibraryW(o3tl::toW(name.getStr())); + if (h == nullptr) { + dlls.erase(i); + return nullptr; + } + i->second->handle = h; + } + return i->second.get(); +} + +ErrCode SbiDllMgr::Call( + OUString const & function, OUString const & library, + SbxArray * arguments, SbxVariable & result, bool cdeclConvention) +{ + if (cdeclConvention) { + return ERRCODE_BASIC_NOT_IMPLEMENTED; + } + OUString dllName(fullDllName(library)); + Dll * dll = impl_->getDll(dllName); + if (dll == nullptr) { + return ERRCODE_BASIC_BAD_DLL_LOAD; + } + ProcData proc; + ErrCode e = dll->getProc(function, &proc); + if (e != ERRCODE_NONE) { + return e; + } + return call(dllName, proc, arguments, result); +} + +void SbiDllMgr::FreeDll(OUString const & library) { + impl_->dlls.erase(library); +} + +SbiDllMgr::SbiDllMgr(): impl_(new Impl) {} + +SbiDllMgr::~SbiDllMgr() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/dllmgr-x86.cxx b/basic/source/runtime/dllmgr-x86.cxx new file mode 100644 index 000000000..3220691bb --- /dev/null +++ b/basic/source/runtime/dllmgr-x86.cxx @@ -0,0 +1,734 @@ +/* -*- 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 <sal/config.h> + +#if defined(_WIN32) +#include <prewin.h> +#include <postwin.h> +#endif + +#include <algorithm> +#include <cstddef> +#include <map> +#include <vector> + +#include <basic/sbx.hxx> +#include <basic/sbxvar.hxx> +#include "runtime.hxx" +#include <osl/thread.h> +#include <rtl/ref.hxx> +#include <rtl/string.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <salhelper/simplereferenceobject.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#undef max + +#include "dllmgr.hxx" + +using namespace css; +using namespace css::uno; + +/* Open issues: + + Missing support for functions returning structs (see TODO in call()). + + Missing support for additional data types (64 bit integers, Any, ...; would + trigger assert(false) in various switches). + + It is assumed that the variables passed into SbiDllMgr::Call to represent + the arguments and return value have types that exactly match the Declare + statement; it would be better if this code had access to the function + signature from the Declare statement, so that it could convert the passed + variables accordingly. +*/ + +extern "C" { + +int __stdcall DllMgr_call32(FARPROC, void const * stack, std::size_t size); +double __stdcall DllMgr_callFp(FARPROC, void const * stack, std::size_t size); + +} + +namespace { + +char * address(std::vector< char > & blob) { + return blob.empty() ? 0 : &blob[0]; +} + +ErrCode convert(OUString const & source, OString * target) { + return + source.convertToString( + target, osl_getThreadTextEncoding(), + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) + ? ERRCODE_NONE : ERRCODE_BASIC_BAD_ARGUMENT; + //TODO: more specific errcode? +} + +ErrCode convert(char const * source, sal_Int32 length, OUString * target) { + return + rtl_convertStringToUString( + &target->pData, source, length, osl_getThreadTextEncoding(), + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) + ? ERRCODE_NONE : ERRCODE_BASIC_BAD_ARGUMENT; + //TODO: more specific errcode? +} + +struct UnmarshalData { + UnmarshalData(SbxVariable * theVariable, void * theBuffer): + variable(theVariable), buffer(theBuffer) {} + + SbxVariable * variable; + void * buffer; +}; + +struct StringData: public UnmarshalData { + StringData(SbxVariable * theVariable, void * theBuffer, bool theSpecial): + UnmarshalData(theVariable, theBuffer), special(theSpecial) {} + + bool special; +}; + +class MarshalData { +public: + MarshalData() = default; + MarshalData(const MarshalData&) = delete; + const MarshalData& operator=(const MarshalData&) = delete; + + std::vector< char > * newBlob() { + blobs_.push_back(std::vector< char >()); + return &blobs_.back(); + } + + std::vector< UnmarshalData > unmarshal; + + std::vector< StringData > unmarshalStrings; + +private: + std::vector< std::vector< char > > blobs_; +}; + +std::size_t align(std::size_t address, std::size_t alignment) { + // alignment = 2^k for some k >= 0 + return (address + (alignment - 1)) & ~(alignment - 1); +} + +char * align( + std::vector< char > & blob, std::size_t alignment, std::size_t offset, + std::size_t add) +{ + std::vector< char >::size_type n = blob.size(); + n = align(n - offset, alignment) + offset; //TODO: overflow in align() + blob.resize(n + add); //TODO: overflow + return address(blob) + n; +} + +template< typename T > void add( + std::vector< char > & blob, T const & data, std::size_t alignment, + std::size_t offset) +{ + *reinterpret_cast< T * >(align(blob, alignment, offset, sizeof (T))) = data; +} + +std::size_t alignment(SbxVariable * variable) { + assert(variable != 0); + if ((variable->GetType() & SbxARRAY) == 0) { + switch (variable->GetType()) { + case SbxINTEGER: + return 2; + case SbxLONG: + case SbxSINGLE: + case SbxSTRING: + return 4; + case SbxDOUBLE: + return 8; + case SbxOBJECT: + { + std::size_t n = 1; + SbxObject* pobj = dynamic_cast<SbxObject*>(variable->GetObject()); + assert(pobj); + SbxArray* props = pobj->GetProperties(); + for (sal_uInt32 i = 0; i < props->Count32(); ++i) { + n = std::max(n, alignment(props->Get32(i))); + } + return n; + } + case SbxBOOL: + case SbxBYTE: + return 1; + default: + assert(false); + return 1; + } + } else { + SbxDimArray * arr = dynamic_cast<SbxDimArray*>( variable->GetObject() ); + assert(arr); + sal_Int32 dims = arr->GetDims32(); + std::vector< sal_Int32 > low(dims); + for (sal_Int32 i = 0; i < dims; ++i) { + sal_Int32 up; + arr->GetDim32(i + 1, low[i], up); + } + return alignment(arr->Get32(&low[0])); + } +} + +ErrCode marshal( + bool outer, SbxVariable * variable, bool special, + std::vector< char > & blob, std::size_t offset, MarshalData & data); + +ErrCode marshalString( + SbxVariable * variable, bool special, MarshalData & data, void ** buffer) +{ + assert(variable != 0 && buffer != 0); + OString str; + ErrCode e = convert(variable->GetOUString(), &str); + if (e != ERRCODE_NONE) { + return e; + } + std::vector< char > * blob = data.newBlob(); + blob->insert( + blob->begin(), str.getStr(), str.getStr() + str.getLength() + 1); + *buffer = address(*blob); + data.unmarshalStrings.push_back(StringData(variable, *buffer, special)); + return ERRCODE_NONE; +} + +ErrCode marshalStruct( + SbxVariable * variable, std::vector< char > & blob, std::size_t offset, + MarshalData & data) +{ + assert(variable != 0); + SbxObject* pobj = dynamic_cast<SbxObject*>(variable->GetObject()); + assert(pobj); + SbxArray* props = pobj->GetProperties(); + for (sal_uInt32 i = 0; i < props->Count32(); ++i) { + ErrCode e = marshal(false, props->Get32(i), false, blob, offset, data); + if (e != ERRCODE_NONE) { + return e; + } + } + return ERRCODE_NONE; +} + +ErrCode marshalArray( + SbxVariable * variable, std::vector< char > & blob, std::size_t offset, + MarshalData & data) +{ + assert(variable != 0); + SbxDimArray * arr = dynamic_cast<SbxDimArray*>( variable->GetObject() ); + assert(arr); + sal_Int32 dims = arr->GetDims32(); + std::vector< sal_Int32 > low(dims); + std::vector< sal_Int32 > up(dims); + for (sal_Int32 i = 0; i < dims; ++i) { + arr->GetDim32(i + 1, low[i], up[i]); + } + for (std::vector< sal_Int32 > idx = low;;) { + ErrCode e = marshal( + false, arr->Get32(&idx[0]), false, blob, offset, data); + if (e != ERRCODE_NONE) { + return e; + } + sal_Int32 i = dims - 1; + while (idx[i] == up[i]) { + idx[i] = low[i]; + if (i == 0) { + return ERRCODE_NONE; + } + --i; + } + ++idx[i]; + } +} + +// 8-aligned structs are only 4-aligned on stack, so alignment of members in +// such structs must take that into account via "offset" +ErrCode marshal( + bool outer, SbxVariable * variable, bool special, + std::vector< char > & blob, std::size_t offset, MarshalData & data) +{ + assert(variable != 0); + + SbxDataType eVarType = variable->GetType(); + bool bByVal = !(variable->GetFlags() & SbxFlagBits::Reference); + if( !bByVal && !SbiRuntime::isVBAEnabled() && eVarType == SbxSTRING ) + bByVal = true; + + if (bByVal) { + if ((eVarType & SbxARRAY) == 0) { + switch (eVarType) { + case SbxINTEGER: + add(blob, variable->GetInteger(), outer ? 4 : 2, offset); + break; + case SbxLONG: + add(blob, variable->GetLong(), 4, offset); + break; + case SbxSINGLE: + add(blob, variable->GetSingle(), 4, offset); + break; + case SbxDOUBLE: + add(blob, variable->GetDouble(), outer ? 4 : 8, offset); + break; + case SbxSTRING: + { + void * p; + ErrCode e = marshalString(variable, special, data, &p); + if (e != ERRCODE_NONE) { + return e; + } + add(blob, p, 4, offset); + break; + } + case SbxOBJECT: + { + align(blob, outer ? 4 : alignment(variable), offset, 0); + ErrCode e = marshalStruct(variable, blob, offset, data); + if (e != ERRCODE_NONE) { + return e; + } + break; + } + case SbxBOOL: + add(blob, variable->GetBool(), outer ? 4 : 1, offset); + break; + case SbxBYTE: + add(blob, variable->GetByte(), outer ? 4 : 1, offset); + break; + default: + assert(false); + break; + } + } else { + ErrCode e = marshalArray(variable, blob, offset, data); + if (e != ERRCODE_NONE) { + return e; + } + } + } else { + if ((eVarType & SbxARRAY) == 0) { + switch (eVarType) { + case SbxINTEGER: + case SbxLONG: + case SbxSINGLE: + case SbxDOUBLE: + case SbxBOOL: + case SbxBYTE: + add(blob, variable->data(), 4, offset); + break; + case SbxSTRING: + { + void * p; + ErrCode e = marshalString(variable, special, data, &p); + if (e != ERRCODE_NONE) { + return e; + } + std::vector< char > * blob2 = data.newBlob(); + add(*blob2, p, 4, 0); + add(blob, address(*blob2), 4, offset); + break; + } + case SbxOBJECT: + { + std::vector< char > * blob2 = data.newBlob(); + ErrCode e = marshalStruct(variable, *blob2, 0, data); + if (e != ERRCODE_NONE) { + return e; + } + void * p = address(*blob2); + if (outer) { + data.unmarshal.push_back(UnmarshalData(variable, p)); + } + add(blob, p, 4, offset); + break; + } + default: + assert(false); + break; + } + } else { + std::vector< char > * blob2 = data.newBlob(); + ErrCode e = marshalArray(variable, *blob2, 0, data); + if (e != ERRCODE_NONE) { + return e; + } + void * p = address(*blob2); + if (outer) { + data.unmarshal.push_back(UnmarshalData(variable, p)); + } + add(blob, p, 4, offset); + } + } + return ERRCODE_NONE; +} + +template< typename T > T read(void const ** pointer) { + T const * p = static_cast< T const * >(*pointer); + *pointer = static_cast< void const * >(p + 1); + return *p; +} + +void const * unmarshal(SbxVariable * variable, void const * data) { + assert(variable != 0); + if ((variable->GetType() & SbxARRAY) == 0) { + switch (variable->GetType()) { + case SbxINTEGER: + variable->PutInteger(read< sal_Int16 >(&data)); + break; + case SbxLONG: + variable->PutLong(read< sal_Int32 >(&data)); + break; + case SbxSINGLE: + variable->PutSingle(read< float >(&data)); + break; + case SbxDOUBLE: + variable->PutDouble(read< double >(&data)); + break; + case SbxSTRING: + read< char * >(&data); // handled by unmarshalString + break; + case SbxOBJECT: + { + data = reinterpret_cast< void const * >( + align( + reinterpret_cast< sal_uIntPtr >(data), + alignment(variable))); + SbxObject* pobj = dynamic_cast<SbxObject*>(variable->GetObject()); + assert(pobj); + SbxArray* props = pobj->GetProperties(); + for (sal_uInt32 i = 0; i < props->Count32(); ++i) { + data = unmarshal(props->Get32(i), data); + } + break; + } + case SbxBOOL: + variable->PutBool(read< sal_Bool >(&data)); + break; + case SbxBYTE: + variable->PutByte(read< sal_uInt8 >(&data)); + break; + default: + assert(false); + break; + } + } else { + SbxDimArray * arr = dynamic_cast<SbxDimArray*>( variable->GetObject() ); + assert(arr); + sal_Int32 dims = arr->GetDims32(); + std::vector< sal_Int32 > low(dims); + std::vector< sal_Int32 > up(dims); + for (sal_Int32 i = 0; i < dims; ++i) { + arr->GetDim32(i + 1, low[i], up[i]); + } + for (std::vector< sal_Int32 > idx = low;;) { + data = unmarshal(arr->Get32(&idx[0]), data); + sal_Int32 i = dims - 1; + while (idx[i] == up[i]) { + idx[i] = low[i]; + if (i == 0) { + goto done; + } + --i; + } + ++idx[i]; + } + done:; + } + return data; +} + +ErrCode unmarshalString(StringData const & data, SbxVariable & result) { + OUString str; + if (data.buffer != 0) { + char const * p = static_cast< char const * >(data.buffer); + sal_Int32 len; + if (data.special) { + len = static_cast< sal_Int32 >(result.GetULong()); + if (len < 0) { // i.e., DWORD result >= 2^31 + return ERRCODE_BASIC_BAD_ARGUMENT; + //TODO: more specific errcode? + } + } else { + len = rtl_str_getLength(p); + } + ErrCode e = convert(p, len, &str); + if (e != ERRCODE_NONE) { + return e; + } + } + data.variable->PutString(str); + return ERRCODE_NONE; +} + +struct ProcData { + OString name; + FARPROC proc; +}; + +ErrCode call( + OUString const & dll, ProcData const & proc, SbxArray * arguments, + SbxVariable & result) +{ + std::vector< char > stack; + MarshalData data; + // For DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer) + // from kernel32, upon return, filled lpBuffer length is result DWORD, which + // requires special handling in unmarshalString; other functions might + // require similar treatment, too: + bool special = dll.equalsIgnoreAsciiCase("KERNEL32.DLL") && + (proc.name == OString("GetLogicalDriveStringsA")); + for (sal_uInt32 i = 1; i < (arguments == 0 ? 0 : arguments->Count32()); ++i) { + ErrCode e = marshal( + true, arguments->Get32(i), special && i == 2, stack, stack.size(), + data); + if (e != ERRCODE_NONE) { + return e; + } + align(stack, 4, 0, 0); + } + switch (result.GetType()) { + case SbxEMPTY: + DllMgr_call32(proc.proc, address(stack), stack.size()); + break; + case SbxINTEGER: + result.PutInteger( + static_cast< sal_Int16 >( + DllMgr_call32(proc.proc, address(stack), stack.size()))); + break; + case SbxLONG: + result.PutLong( + static_cast< sal_Int32 >( + DllMgr_call32(proc.proc, address(stack), stack.size()))); + break; + case SbxSINGLE: + result.PutSingle( + static_cast< float >( + DllMgr_callFp(proc.proc, address(stack), stack.size()))); + break; + case SbxDOUBLE: + result.PutDouble( + DllMgr_callFp(proc.proc, address(stack), stack.size())); + break; + case SbxSTRING: + { + char const * s1 = reinterpret_cast< char const * >( + DllMgr_call32(proc.proc, address(stack), stack.size())); + OUString s2; + ErrCode e = convert(s1, rtl_str_getLength(s1), &s2); + if (e != ERRCODE_NONE) { + return e; + } + result.PutString(s2); + break; + } + case SbxOBJECT: + //TODO + DllMgr_call32(proc.proc, address(stack), stack.size()); + break; + case SbxBOOL: + result.PutBool( + bool(DllMgr_call32(proc.proc, address(stack), stack.size()))); + break; + case SbxBYTE: + result.PutByte( + static_cast< sal_uInt8 >( + DllMgr_call32(proc.proc, address(stack), stack.size()))); + break; + default: + assert(false); + break; + } + for (sal_uInt32 i = 1; i < (arguments == 0 ? 0 : arguments->Count32()); ++i) { + arguments->Get32(i)->ResetFlag(SbxFlagBits::Reference); + //TODO: skipped for errors?!? + } + for (auto& rUnmarshalData : data.unmarshal) + { + unmarshal(rUnmarshalData.variable, rUnmarshalData.buffer); + } + for (const auto& rStringData : data.unmarshalStrings) + { + ErrCode e = unmarshalString(rStringData, result); + if (e != ERRCODE_NONE) { + return e; + } + } + return ERRCODE_NONE; +} + +ErrCode getProcData(HMODULE handle, OUString const & name, ProcData * proc) +{ + assert(proc != 0); + if ( !name.isEmpty() && name[0] == '@' ) { //TODO: "@" vs. "#"??? + sal_Int32 n = name.copy(1).toInt32(); //TODO: handle bad input + if (n <= 0 || n > 0xFFFF) { + return ERRCODE_BASIC_BAD_ARGUMENT; //TODO: more specific errcode? + } + FARPROC p = GetProcAddress(handle, reinterpret_cast< LPCSTR >(n)); + if (p != 0) { + proc->name = OString("#") + OString::number(n); + proc->proc = p; + return ERRCODE_NONE; + } + } else { + OString name8; + ErrCode e = convert(name, &name8); + if (e != ERRCODE_NONE) { + return e; + } + FARPROC p = GetProcAddress(handle, name8.getStr()); + if (p != 0) { + proc->name = name8; + proc->proc = p; + return ERRCODE_NONE; + } + sal_Int32 i = name8.indexOf('#'); + if (i != -1) { + name8 = name8.copy(0, i); + p = GetProcAddress(handle, name8.getStr()); + if (p != 0) { + proc->name = name8; + proc->proc = p; + return ERRCODE_NONE; + } + } + OString real(OString("_") + name8); + p = GetProcAddress(handle, real.getStr()); + if (p != 0) { + proc->name = real; + proc->proc = p; + return ERRCODE_NONE; + } + real = name8 + OString("A"); + p = GetProcAddress(handle, real.getStr()); + if (p != 0) { + proc->name = real; + proc->proc = p; + return ERRCODE_NONE; + } + } + return ERRCODE_BASIC_PROC_UNDEFINED; +} + +struct Dll: public salhelper::SimpleReferenceObject { +private: + typedef std::map< OUString, ProcData > Procs; + + virtual ~Dll(); + +public: + Dll(): handle(0) {} + + ErrCode getProc(OUString const & name, ProcData * proc); + + HMODULE handle; + Procs procs; +}; + +Dll::~Dll() { + if (handle != 0 && !FreeLibrary(handle)) { + SAL_WARN("basic", "FreeLibrary(" << handle << ") failed with " << GetLastError()); + } +} + +ErrCode Dll::getProc(OUString const & name, ProcData * proc) { + Procs::iterator i(procs.find(name)); + if (i != procs.end()) { + *proc = i->second; + return ERRCODE_NONE; + } + ErrCode e = getProcData(handle, name, proc); + if (e == ERRCODE_NONE) { + procs.emplace(name, *proc); + } + return e; +} + +OUString fullDllName(OUString const & name) { + OUString full(name); + if (full.indexOf('.') == -1) { + full += ".DLL"; + } + return full; +} + +} + +struct SbiDllMgr::Impl { +private: + typedef std::map< OUString, rtl::Reference< Dll > > Dlls; + +public: + Impl() = default; + Impl(const Impl&) = delete; + const Impl& operator=(const Impl&) = delete; + + Dll * getDll(OUString const & name); + + Dlls dlls; +}; + +Dll * SbiDllMgr::Impl::getDll(OUString const & name) { + Dlls::iterator i(dlls.find(name)); + if (i == dlls.end()) { + i = dlls.emplace(name, new Dll).first; + HMODULE h = LoadLibraryW(o3tl::toW(name.getStr())); + if (h == 0) { + dlls.erase(i); + return 0; + } + i->second->handle = h; + } + return i->second.get(); +} + +ErrCode SbiDllMgr::Call( + OUString const & function, OUString const & library, + SbxArray * arguments, SbxVariable & result, bool cdeclConvention) +{ + if (cdeclConvention) { + return ERRCODE_BASIC_NOT_IMPLEMENTED; + } + OUString dllName(fullDllName(library)); + Dll * dll = impl_->getDll(dllName); + if (dll == 0) { + return ERRCODE_BASIC_BAD_DLL_LOAD; + } + ProcData proc; + ErrCode e = dll->getProc(function, &proc); + if (e != ERRCODE_NONE) { + return e; + } + return call(dllName, proc, arguments, result); +} + +void SbiDllMgr::FreeDll(OUString const & library) { + impl_->dlls.erase(library); +} + +SbiDllMgr::SbiDllMgr(): impl_(new Impl) {} + +SbiDllMgr::~SbiDllMgr() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/dllmgr.hxx b/basic/source/runtime/dllmgr.hxx new file mode 100644 index 000000000..461375467 --- /dev/null +++ b/basic/source/runtime/dllmgr.hxx @@ -0,0 +1,53 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_BASIC_SOURCE_RUNTIME_DLLMGR_HXX +#define INCLUDED_BASIC_SOURCE_RUNTIME_DLLMGR_HXX + +#include <sal/config.h> +#include <vcl/errcode.hxx> +#include <memory> + +class SbxArray; +class SbxVariable; + +class SbiDllMgr { +public: + SbiDllMgr(const SbiDllMgr&) = delete; + const SbiDllMgr& operator=(const SbiDllMgr&) = delete; + + SbiDllMgr(); + + ~SbiDllMgr(); + + ErrCode Call( + OUString const & function, OUString const & library, + SbxArray * arguments, SbxVariable & result, bool cdeclConvention); + + void FreeDll(OUString const & library); + +private: + struct Impl; + + std::unique_ptr< Impl > impl_; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/inputbox.cxx b/basic/source/runtime/inputbox.cxx new file mode 100644 index 000000000..1d27b25ab --- /dev/null +++ b/basic/source/runtime/inputbox.cxx @@ -0,0 +1,143 @@ +/* -*- 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 <basic/sberrors.hxx> +#include <tools/lineend.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/window.hxx> +#include <rtlproto.hxx> +#include <memory> + +namespace { + +class SvRTLInputBox : public weld::GenericDialogController +{ + std::unique_ptr<weld::Entry> m_xEdit; + std::unique_ptr<weld::Button> m_xOk; + std::unique_ptr<weld::Button> m_xCancel; + std::unique_ptr<weld::Label> m_xPromptText; + OUString m_aText; + + void PositionDialog( long nXTwips, long nYTwips ); + void InitButtons(); + void SetPrompt(const OUString& rPrompt); + DECL_LINK( OkHdl, weld::Button&, void ); + DECL_LINK( CancelHdl, weld::Button&, void ); + +public: + SvRTLInputBox(weld::Window* pParent, const OUString& rPrompt, const OUString& rTitle, + const OUString& rDefault, long nXTwips, long nYTwips ); + OUString const & GetText() const { return m_aText; } +}; + +} + +SvRTLInputBox::SvRTLInputBox(weld::Window* pParent, const OUString& rPrompt, + const OUString& rTitle, const OUString& rDefault, + long nXTwips, long nYTwips) + : GenericDialogController(pParent, "svt/ui/inputbox.ui", "InputBox") + , m_xEdit(m_xBuilder->weld_entry("entry")) + , m_xOk(m_xBuilder->weld_button("ok")) + , m_xCancel(m_xBuilder->weld_button("cancel")) + , m_xPromptText(m_xBuilder->weld_label("prompt")) +{ + PositionDialog( nXTwips, nYTwips ); + InitButtons(); + SetPrompt(rPrompt); + m_xDialog->set_title(rTitle); + m_xEdit->set_text(rDefault); + m_xEdit->select_region(0, -1); +} + +void SvRTLInputBox::InitButtons() +{ + m_xOk->connect_clicked(LINK(this,SvRTLInputBox, OkHdl)); + m_xCancel->connect_clicked(LINK(this,SvRTLInputBox,CancelHdl)); +} + +void SvRTLInputBox::PositionDialog(long nXTwips, long nYTwips) +{ + if( nXTwips != -1 && nYTwips != -1 ) + { + Point aDlgPosApp( nXTwips, nYTwips ); + OutputDevice* pDefaultDevice = Application::GetDefaultDevice(); + pDefaultDevice->Push(PushFlags::MAPMODE); + pDefaultDevice->SetMapMode(MapMode( MapUnit::MapAppFont)); + aDlgPosApp = pDefaultDevice->LogicToPixel(aDlgPosApp, MapMode(MapUnit::MapTwip)); + pDefaultDevice->Pop(); + m_xDialog->window_move(aDlgPosApp.X(), aDlgPosApp.Y()); + } +} + +void SvRTLInputBox::SetPrompt(const OUString& rPrompt) +{ + if (rPrompt.isEmpty()) + return; + OUString aText_(convertLineEnd(rPrompt, LINEEND_CR)); + m_xPromptText->set_label( aText_ ); +} + +IMPL_LINK_NOARG( SvRTLInputBox, OkHdl, weld::Button&, void ) +{ + m_aText = m_xEdit->get_text(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG( SvRTLInputBox, CancelHdl, weld::Button&, void ) +{ + m_aText.clear(); + m_xDialog->response(RET_CANCEL); +} + +// Syntax: String InputBox( Prompt, [Title], [Default] [, nXpos, nYpos ] ) + +void SbRtl_InputBox(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32(); + if ( nArgCount < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + OUString aTitle; + OUString aDefault; + sal_Int32 nX = -1, nY = -1; // center + const OUString& rPrompt = rPar.Get32(1)->GetOUString(); + if ( nArgCount > 2 && !rPar.Get32(2)->IsErr() ) + aTitle = rPar.Get32(2)->GetOUString(); + if ( nArgCount > 3 && !rPar.Get32(3)->IsErr() ) + aDefault = rPar.Get32(3)->GetOUString(); + if ( nArgCount > 4 ) + { + if ( nArgCount != 6 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + nX = rPar.Get32(4)->GetLong(); + nY = rPar.Get32(5)->GetLong(); + } + vcl::Window* pParent = Application::GetDefDialogParent(); + SvRTLInputBox aDlg(pParent ? pParent->GetFrameWeld() : nullptr,rPrompt,aTitle,aDefault,nX,nY); + aDlg.run(); + rPar.Get32(0)->PutString(aDlg.GetText()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/iosys.cxx b/basic/source/runtime/iosys.cxx new file mode 100644 index 000000000..d525d1a13 --- /dev/null +++ b/basic/source/runtime/iosys.cxx @@ -0,0 +1,849 @@ +/* -*- 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 <string.h> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/window.hxx> +#include <osl/file.hxx> + +#include <runtime.hxx> + +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/ucb/UniversalContentBroker.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; +using namespace com::sun::star::io; +using namespace com::sun::star::bridge; + +#include <iosys.hxx> + +namespace { + +class SbiInputDialog : public weld::GenericDialogController +{ + std::unique_ptr<weld::Entry> m_xInput; + std::unique_ptr<weld::Button> m_xOk; + std::unique_ptr<weld::Button> m_xCancel; + std::unique_ptr<weld::Label> m_xPromptText; + OUString m_aText; + DECL_LINK(Ok, weld::Button&, void); + DECL_LINK(Cancel, weld::Button&, void); +public: + SbiInputDialog(weld::Window*, const OUString&); + const OUString& GetInput() const { return m_aText; } +}; + +} + +SbiInputDialog::SbiInputDialog(weld::Window* pParent, const OUString& rPrompt) + : GenericDialogController(pParent, "svt/ui/inputbox.ui", "InputBox") + , m_xInput(m_xBuilder->weld_entry("entry")) + , m_xOk(m_xBuilder->weld_button("ok")) + , m_xCancel(m_xBuilder->weld_button("cancel")) + , m_xPromptText(m_xBuilder->weld_label("prompt")) +{ + m_xDialog->set_title(rPrompt); + m_xPromptText->set_label(rPrompt); + m_xOk->connect_clicked( LINK( this, SbiInputDialog, Ok ) ); + m_xCancel->connect_clicked( LINK( this, SbiInputDialog, Cancel ) ); +} + +IMPL_LINK_NOARG( SbiInputDialog, Ok, weld::Button&, void ) +{ + m_aText = m_xInput->get_text(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG( SbiInputDialog, Cancel, weld::Button&, void ) +{ + m_xDialog->response(RET_CANCEL); +} + +SbiStream::SbiStream() + : nExpandOnWriteTo(0) + , nLine(0) + , nLen(0) + , nMode(SbiStreamFlags::NONE) + , nError(0) +{ +} + +SbiStream::~SbiStream() +{ +} + +// map an SvStream-error to StarBASIC-code + +void SbiStream::MapError() +{ + if( !pStrm ) + return; + + ErrCode nEC = pStrm->GetError(); + if (nEC == ERRCODE_NONE) + nError = ERRCODE_NONE; + else if (nEC == SVSTREAM_FILE_NOT_FOUND) + nError = ERRCODE_BASIC_FILE_NOT_FOUND; + else if (nEC ==SVSTREAM_PATH_NOT_FOUND) + nError = ERRCODE_BASIC_PATH_NOT_FOUND; + else if (nEC ==SVSTREAM_TOO_MANY_OPEN_FILES) + nError = ERRCODE_BASIC_TOO_MANY_FILES; + else if (nEC ==SVSTREAM_ACCESS_DENIED) + nError = ERRCODE_BASIC_ACCESS_DENIED; + else if (nEC ==SVSTREAM_INVALID_PARAMETER) + nError = ERRCODE_BASIC_BAD_ARGUMENT; + else if (nEC ==SVSTREAM_OUTOFMEMORY) + nError = ERRCODE_BASIC_NO_MEMORY; + else + nError = ERRCODE_BASIC_IO_ERROR; +} + +// Returns sal_True if UNO is available, otherwise the old file +// system implementation has to be used +// #89378 New semantic: Don't just ask for UNO but for UCB +bool hasUno() +{ + static bool bNeedInit = true; + static bool bRetVal = true; + + if( bNeedInit ) + { + bNeedInit = false; + Reference< XComponentContext > xContext = comphelper::getProcessComponentContext(); + if( !xContext.is() ) + { + // No service manager at all + bRetVal = false; + } + else + { + Reference< XUniversalContentBroker > xManager = UniversalContentBroker::create(xContext); + + if ( !( xManager->queryContentProvider( "file:///" ).is() ) ) + { + // No UCB + bRetVal = false; + } + } + } + return bRetVal; +} + +namespace { + +class OslStream : public SvStream +{ + osl::File maFile; + +public: + OslStream( const OUString& rName, StreamMode nStrmMode ); + virtual ~OslStream() override; + virtual std::size_t GetData(void* pData, std::size_t nSize) override; + virtual std::size_t PutData(const void* pData, std::size_t nSize) override; + virtual sal_uInt64 SeekPos( sal_uInt64 nPos ) override; + virtual void FlushData() override; + virtual void SetSize( sal_uInt64 nSize) override; +}; + +} + +OslStream::OslStream( const OUString& rName, StreamMode nStrmMode ) + : maFile( rName ) +{ + sal_uInt32 nFlags; + + if( (nStrmMode & (StreamMode::READ | StreamMode::WRITE)) == (StreamMode::READ | StreamMode::WRITE) ) + { + nFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write; + } + else if( nStrmMode & StreamMode::WRITE ) + { + nFlags = osl_File_OpenFlag_Write; + } + else //if( nStrmMode & StreamMode::READ ) + { + nFlags = osl_File_OpenFlag_Read; + } + + osl::FileBase::RC nRet = maFile.open( nFlags ); + if( nRet == osl::FileBase::E_NOENT && nFlags != osl_File_OpenFlag_Read ) + { + nFlags |= osl_File_OpenFlag_Create; + nRet = maFile.open( nFlags ); + } + + if( nRet != osl::FileBase::E_None ) + { + SetError( ERRCODE_IO_GENERAL ); + } +} + + +OslStream::~OslStream() +{ + maFile.close(); +} + +std::size_t OslStream::GetData(void* pData, std::size_t nSize) +{ + sal_uInt64 nBytesRead = nSize; + maFile.read( pData, nBytesRead, nBytesRead ); + return nBytesRead; +} + +std::size_t OslStream::PutData(const void* pData, std::size_t nSize) +{ + sal_uInt64 nBytesWritten; + maFile.write( pData, nSize, nBytesWritten ); + return nBytesWritten; +} + +sal_uInt64 OslStream::SeekPos( sal_uInt64 nPos ) +{ + ::osl::FileBase::RC rc = ::osl::FileBase::E_None; + // check if a truncated STREAM_SEEK_TO_END was passed + assert(nPos != SAL_MAX_UINT32); + if( nPos == STREAM_SEEK_TO_END ) + { + rc = maFile.setPos( osl_Pos_End, 0 ); + } + else + { + rc = maFile.setPos( osl_Pos_Absolut, nPos ); + } + OSL_VERIFY(rc == ::osl::FileBase::E_None); + sal_uInt64 nRealPos(0); + rc = maFile.getPos( nRealPos ); + OSL_VERIFY(rc == ::osl::FileBase::E_None); + return nRealPos; +} + +void OslStream::FlushData() +{ +} + +void OslStream::SetSize( sal_uInt64 nSize ) +{ + maFile.setSize( nSize ); +} + +namespace { + +class UCBStream : public SvStream +{ + Reference< XInputStream > xIS; + Reference< XStream > xS; + Reference< XSeekable > xSeek; +public: + explicit UCBStream( Reference< XInputStream > const & xIS ); + explicit UCBStream( Reference< XStream > const & xS ); + virtual ~UCBStream() override; + virtual std::size_t GetData( void* pData, std::size_t nSize ) override; + virtual std::size_t PutData( const void* pData, std::size_t nSize ) override; + virtual sal_uInt64 SeekPos( sal_uInt64 nPos ) override; + virtual void FlushData() override; + virtual void SetSize( sal_uInt64 nSize ) override; +}; + +} + +UCBStream::UCBStream( Reference< XInputStream > const & rStm ) + : xIS( rStm ) + , xSeek( rStm, UNO_QUERY ) +{ +} + +UCBStream::UCBStream( Reference< XStream > const & rStm ) + : xS( rStm ) + , xSeek( rStm, UNO_QUERY ) +{ +} + + +UCBStream::~UCBStream() +{ + try + { + if( xIS.is() ) + { + xIS->closeInput(); + } + else if( xS.is() ) + { + Reference< XInputStream > xIS_ = xS->getInputStream(); + if( xIS_.is() ) + { + xIS_->closeInput(); + } + } + } + catch(const Exception & ) + { + SetError( ERRCODE_IO_GENERAL ); + } +} + +std::size_t UCBStream::GetData(void* pData, std::size_t nSize) +{ + try + { + Reference< XInputStream > xISFromS; + if( xIS.is() ) + { + Sequence<sal_Int8> aData; + nSize = xIS->readBytes( aData, nSize ); + memcpy( pData, aData.getConstArray(), nSize ); + return nSize; + } + else if( xS.is() && (xISFromS = xS->getInputStream()).is() ) + { + Sequence<sal_Int8> aData; + nSize = xISFromS->readBytes( aData, nSize ); + memcpy(pData, aData.getConstArray(), nSize ); + return nSize; + } + else + { + SetError( ERRCODE_IO_GENERAL ); + } + } + catch(const Exception & ) + { + SetError( ERRCODE_IO_GENERAL ); + } + return 0; +} + +std::size_t UCBStream::PutData(const void* pData, std::size_t nSize) +{ + try + { + Reference< XOutputStream > xOSFromS; + if( xS.is() && (xOSFromS = xS->getOutputStream()).is() ) + { + Sequence<sal_Int8> aData( static_cast<const sal_Int8 *>(pData), nSize ); + xOSFromS->writeBytes( aData ); + return nSize; + } + else + { + SetError( ERRCODE_IO_GENERAL ); + } + } + catch(const Exception & ) + { + SetError( ERRCODE_IO_GENERAL ); + } + return 0; +} + +sal_uInt64 UCBStream::SeekPos( sal_uInt64 nPos ) +{ + try + { + if( xSeek.is() ) + { + sal_uInt64 nLen = static_cast<sal_uInt64>( xSeek->getLength() ); + if( nPos > nLen ) + { + nPos = nLen; + } + xSeek->seek( nPos ); + return nPos; + } + else + { + SetError( ERRCODE_IO_GENERAL ); + } + } + catch(const Exception & ) + { + SetError( ERRCODE_IO_GENERAL ); + } + return 0; +} + +void UCBStream::FlushData() +{ + try + { + Reference< XOutputStream > xOSFromS; + if( xS.is() && (xOSFromS = xS->getOutputStream()).is() ) + { + xOSFromS->flush(); + } + else + { + SetError( ERRCODE_IO_GENERAL ); + } + } + catch(const Exception & ) + { + SetError( ERRCODE_IO_GENERAL ); + } +} + +void UCBStream::SetSize( sal_uInt64 ) +{ + SAL_WARN("basic", "UCBStream::SetSize not allowed to call from basic" ); + SetError( ERRCODE_IO_GENERAL ); +} + + +ErrCode const & SbiStream::Open +( const OString& rName, StreamMode nStrmMode, SbiStreamFlags nFlags, short nL ) +{ + nMode = nFlags; + nLen = nL; + nLine = 0; + nExpandOnWriteTo = 0; + if( ( nStrmMode & ( StreamMode::READ|StreamMode::WRITE ) ) == StreamMode::READ ) + { + nStrmMode |= StreamMode::NOCREATE; + } + OUString aStr(OStringToOUString(rName, osl_getThreadTextEncoding())); + OUString aNameStr = getFullPath( aStr ); + + if( hasUno() ) + { + Reference< XSimpleFileAccess3 > xSFI( SimpleFileAccess::create( comphelper::getProcessComponentContext() ) ); + try + { + + // #??? For write access delete file if it already exists (not for appending) + if( (nStrmMode & StreamMode::WRITE) && !IsAppend() && !IsBinary() && !IsRandom() && + xSFI->exists( aNameStr ) && !xSFI->isFolder( aNameStr ) ) + { + xSFI->kill( aNameStr ); + } + + if( (nStrmMode & (StreamMode::READ | StreamMode::WRITE)) == (StreamMode::READ | StreamMode::WRITE) ) + { + Reference< XStream > xIS = xSFI->openFileReadWrite( aNameStr ); + pStrm.reset( new UCBStream( xIS ) ); + } + else if( nStrmMode & StreamMode::WRITE ) + { + Reference< XStream > xIS = xSFI->openFileReadWrite( aNameStr ); + pStrm.reset( new UCBStream( xIS ) ); + } + else //if( nStrmMode & StreamMode::READ ) + { + Reference< XInputStream > xIS = xSFI->openFileRead( aNameStr ); + pStrm.reset( new UCBStream( xIS ) ); + } + + } + catch(const Exception & ) + { + nError = ERRCODE_IO_GENERAL; + } + } + + if( !pStrm ) + { + pStrm.reset( new OslStream( aNameStr, nStrmMode ) ); + } + if( IsAppend() ) + { + pStrm->Seek( STREAM_SEEK_TO_END ); + } + MapError(); + if( nError ) + { + pStrm.reset(); + } + return nError; +} + +ErrCode const & SbiStream::Close() +{ + if( pStrm ) + { + MapError(); + pStrm.reset(); + } + return nError; +} + +ErrCode SbiStream::Read(OString& rBuf, sal_uInt16 n, bool bForceReadingPerByte) +{ + nExpandOnWriteTo = 0; + if( !bForceReadingPerByte && IsText() ) + { + pStrm->ReadLine(rBuf); + nLine++; + } + else + { + if( !n ) + { + n = nLen; + } + if( !n ) + { + return nError = ERRCODE_BASIC_BAD_RECORD_LENGTH; + } + OStringBuffer aBuffer(read_uInt8s_ToOString(*pStrm, n)); + //Pad it out with ' ' to the requested length on short read + sal_Int32 nRequested = sal::static_int_cast<sal_Int32>(n); + comphelper::string::padToLength(aBuffer, nRequested, ' '); + rBuf = aBuffer.makeStringAndClear(); + } + MapError(); + if( !nError && pStrm->eof() ) + { + nError = ERRCODE_BASIC_READ_PAST_EOF; + } + return nError; +} + +ErrCode const & SbiStream::Read( char& ch ) +{ + nExpandOnWriteTo = 0; + if (aLine.isEmpty()) + { + Read( aLine ); + aLine += OString('\n'); + } + ch = aLine[0]; + aLine = aLine.copy(1); + return nError; +} + +void SbiStream::ExpandFile() +{ + if ( !nExpandOnWriteTo ) + return; + + sal_uInt64 nCur = pStrm->Seek(STREAM_SEEK_TO_END); + if( nCur < nExpandOnWriteTo ) + { + sal_uInt64 nDiff = nExpandOnWriteTo - nCur; + while( nDiff-- ) + { + pStrm->WriteChar( 0 ); + } + } + else + { + pStrm->Seek( nExpandOnWriteTo ); + } + nExpandOnWriteTo = 0; +} + +namespace +{ + void WriteLines(SvStream &rStream, const OString& rStr) + { + OString aStr(convertLineEnd(rStr, rStream.GetLineDelimiter()) ); + write_uInt8s_FromOString(rStream, aStr); + endl( rStream ); + } +} + +ErrCode SbiStream::Write( const OString& rBuf ) +{ + ExpandFile(); + if( IsAppend() ) + { + pStrm->Seek( STREAM_SEEK_TO_END ); + } + if( IsText() ) + { + aLine += rBuf; + // Get it out, if the end is an LF, but strip CRLF before, + // because the SvStream adds a CRLF! + sal_Int32 nLineLen = aLine.getLength(); + if (nLineLen && aLine[--nLineLen] == 0x0A) + { + aLine = aLine.copy(0, nLineLen); + if (nLineLen && aLine[--nLineLen] == 0x0D) + { + aLine = aLine.copy(0, nLineLen); + } + WriteLines(*pStrm, aLine); + aLine.clear(); + } + } + else + { + if( !nLen ) + { + return nError = ERRCODE_BASIC_BAD_RECORD_LENGTH; + } + pStrm->WriteBytes(rBuf.getStr(), nLen); + MapError(); + } + return nError; +} + + +SbiIoSystem::SbiIoSystem() +{ + for(SbiStream* & i : pChan) + { + i = nullptr; + } + nChan = 0; + nError = ERRCODE_NONE; +} + +SbiIoSystem::~SbiIoSystem() COVERITY_NOEXCEPT_FALSE +{ + Shutdown(); +} + +ErrCode SbiIoSystem::GetError() +{ + ErrCode n = nError; + nError = ERRCODE_NONE; + return n; +} + +void SbiIoSystem::Open(short nCh, const OString& rName, StreamMode nMode, SbiStreamFlags nFlags, short nLen) +{ + nError = ERRCODE_NONE; + if( nCh >= CHANNELS || !nCh ) + { + nError = ERRCODE_BASIC_BAD_CHANNEL; + } + else if( pChan[ nCh ] ) + { + nError = ERRCODE_BASIC_FILE_ALREADY_OPEN; + } + else + { + pChan[ nCh ] = new SbiStream; + nError = pChan[ nCh ]->Open( rName, nMode, nFlags, nLen ); + if( nError ) + { + delete pChan[ nCh ]; + pChan[ nCh ] = nullptr; + } + } + nChan = 0; +} + + +void SbiIoSystem::Close() +{ + if( !nChan ) + { + nError = ERRCODE_BASIC_BAD_CHANNEL; + } + else if( !pChan[ nChan ] ) + { + nError = ERRCODE_BASIC_BAD_CHANNEL; + } + else + { + nError = pChan[ nChan ]->Close(); + delete pChan[ nChan ]; + pChan[ nChan ] = nullptr; + } + nChan = 0; +} + + +void SbiIoSystem::Shutdown() +{ + for( short i = 1; i < CHANNELS; i++ ) + { + if( pChan[ i ] ) + { + ErrCode n = pChan[ i ]->Close(); + delete pChan[ i ]; + pChan[ i ] = nullptr; + if( n && !nError ) + { + nError = n; + } + } + } + nChan = 0; + // anything left to PRINT? + if( !aOut.isEmpty() ) + { + vcl::Window* pParent = Application::GetDefDialogParent(); + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent ? pParent->GetFrameWeld() : nullptr, VclMessageType::Warning, + VclButtonsType::Ok, aOut)); + xBox->run(); + } + aOut.clear(); +} + + +void SbiIoSystem::Read(OString& rBuf) +{ + if( !nChan ) + { + ReadCon( rBuf ); + } + else if( !pChan[ nChan ] ) + { + nError = ERRCODE_BASIC_BAD_CHANNEL; + } + else + { + nError = pChan[ nChan ]->Read( rBuf ); + } +} + +char SbiIoSystem::Read() +{ + char ch = ' '; + if( !nChan ) + { + if( aIn.isEmpty() ) + { + ReadCon( aIn ); + aIn += OString('\n'); + } + ch = aIn[0]; + aIn = aIn.copy(1); + } + else if( !pChan[ nChan ] ) + { + nError = ERRCODE_BASIC_BAD_CHANNEL; + } + else + { + nError = pChan[ nChan ]->Read( ch ); + } + return ch; +} + +void SbiIoSystem::Write(const OUString& rBuf) +{ + if( !nChan ) + { + WriteCon( rBuf ); + } + else if( !pChan[ nChan ] ) + { + nError = ERRCODE_BASIC_BAD_CHANNEL; + } + else + { + nError = pChan[ nChan ]->Write( OUStringToOString(rBuf, osl_getThreadTextEncoding()) ); + } +} + +// nChannel == 0..CHANNELS-1 + +SbiStream* SbiIoSystem::GetStream( short nChannel ) const +{ + SbiStream* pRet = nullptr; + if( nChannel >= 0 && nChannel < CHANNELS ) + { + pRet = pChan[ nChannel ]; + } + return pRet; +} + +void SbiIoSystem::CloseAll() +{ + for( short i = 1; i < CHANNELS; i++ ) + { + if( pChan[ i ] ) + { + ErrCode n = pChan[ i ]->Close(); + delete pChan[ i ]; + pChan[ i ] = nullptr; + if( n && !nError ) + { + nError = n; + } + } + } +} + +void SbiIoSystem::ReadCon(OString& rIn) +{ + OUString aPromptStr(OStringToOUString(aPrompt, osl_getThreadTextEncoding())); + SbiInputDialog aDlg(nullptr, aPromptStr); + if (aDlg.run() == RET_OK) + { + rIn = OUStringToOString(aDlg.GetInput(), osl_getThreadTextEncoding()); + } + else + { + nError = ERRCODE_BASIC_USER_ABORT; + } + aPrompt.clear(); +} + +// output of a MessageBox, if there's a CR in the console-buffer + +void SbiIoSystem::WriteCon(const OUString& rText) +{ + aOut += rText; + sal_Int32 n1 = aOut.indexOf('\n'); + sal_Int32 n2 = aOut.indexOf('\r'); + if( n1 == -1 && n2 == -1 ) + return; + + if( n1 == -1 ) + { + n1 = n2; + } + else if( n2 == -1 ) + { + n2 = n1; + } + if( n1 > n2 ) + { + n1 = n2; + } + OUString s(aOut.copy(0, n1)); + aOut = aOut.copy(n1); + while ( !aOut.isEmpty() && (aOut[0] == '\n' || aOut[0] == '\r') ) + { + aOut = aOut.copy(1); + } + { + SolarMutexGuard aSolarGuard; + + vcl::Window* pParent = Application::GetDefDialogParent(); + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent ? pParent->GetFrameWeld() : nullptr, VclMessageType::Warning, + VclButtonsType::OkCancel, s)); + xBox->set_default_response(RET_OK); + if (!xBox->run()) + { + nError = ERRCODE_BASIC_USER_ABORT; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/methods.cxx b/basic/source/runtime/methods.cxx new file mode 100644 index 000000000..671cbe0a3 --- /dev/null +++ b/basic/source/runtime/methods.cxx @@ -0,0 +1,4826 @@ +/* -*- 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 <config_features.h> + +#include <tools/date.hxx> +#include <basic/sbxvar.hxx> +#include <basic/sbuno.hxx> +#include <osl/process.h> +#include <vcl/dibtools.hxx> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/sound.hxx> +#include <tools/wintypes.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/weld.hxx> +#include <basic/sbx.hxx> +#include <svl/zforlist.hxx> +#include <rtl/character.hxx> +#include <rtl/math.hxx> +#include <tools/urlobj.hxx> +#include <osl/time.h> +#include <unotools/charclass.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <tools/wldcrd.hxx> +#include <i18nlangtag/lang.h> +#include <rtl/string.hxx> +#include <sal/log.hxx> +#include <comphelper/DirectoryHelper.hxx> + +#include <runtime.hxx> +#include <sbunoobj.hxx> +#include <osl/file.hxx> +#include <errobject.hxx> + +#include <comphelper/string.hxx> +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/script/XErrorQuery.hpp> +#include <ooo/vba/VbTriState.hpp> +#include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp> +#include <memory> +#include <random> +#include <o3tl/char16_t2wchar_t.hxx> + +using namespace comphelper; +using namespace osl; +using namespace com::sun::star; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; + +#include <date.hxx> +#include <sbstdobj.hxx> +#include <rtlproto.hxx> +#include <image.hxx> +#include <iosys.hxx> +#include "ddectrl.hxx" +#include <sbintern.hxx> +#include <basic/vbahelper.hxx> + +#include <vector> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <sbobjmod.hxx> +#include <sbxmod.hxx> + +#ifdef _WIN32 +#include <prewin.h> +#include <direct.h> +#include <io.h> +#include <postwin.h> +#else +#include <unistd.h> +#endif + +#include <com/sun/star/i18n/XCharacterClassification.hpp> +#include <vcl/unohelp.hxx> + +#if HAVE_FEATURE_SCRIPTING + +static void FilterWhiteSpace( OUString& rStr ) +{ + if (rStr.isEmpty()) + { + return; + } + OUStringBuffer aRet; + + for (sal_Int32 i = 0; i < rStr.getLength(); ++i) + { + sal_Unicode cChar = rStr[i]; + if ((cChar != ' ') && (cChar != '\t') && + (cChar != '\n') && (cChar != '\r')) + { + aRet.append(cChar); + } + } + + rStr = aRet.makeStringAndClear(); +} + +static long GetDayDiff( const Date& rDate ); + +static const CharClass& GetCharClass() +{ + static CharClass aCharClass( Application::GetSettings().GetLanguageTag() ); + return aCharClass; +} + +static bool isFolder( FileStatus::Type aType ) +{ + return ( aType == FileStatus::Directory || aType == FileStatus::Volume ); +} + + +//*** UCB file access *** + +// Converts possibly relative paths to absolute paths +// according to the setting done by ChDir/ChDrive +OUString getFullPath( const OUString& aRelPath ) +{ + OUString aFileURL; + + // #80204 Try first if it already is a valid URL + INetURLObject aURLObj( aRelPath ); + aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if( aFileURL.isEmpty() ) + { + File::getFileURLFromSystemPath( aRelPath, aFileURL ); + } + + return aFileURL; +} + +// TODO: -> SbiGlobals +static uno::Reference< ucb::XSimpleFileAccess3 > const & getFileAccess() +{ + static uno::Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ); + return xSFI; +} + + +// Properties and methods lie down the return value at the Get (bPut = sal_False) in the +// element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True) + +// CreateObject( class ) + +void SbRtl_CreateObject(StarBASIC * pBasic, SbxArray & rPar, bool) +{ + OUString aClass( rPar.Get32(1)->GetOUString() ); + SbxObjectRef p = SbxBase::CreateObject( aClass ); + if( !p.is() ) + StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD ); + else + { + // Convenience: enter BASIC as parent + p->SetParent( pBasic ); + rPar.Get32(0)->PutObject( p.get() ); + } +} + +// Error( n ) + +void SbRtl_Error(StarBASIC * pBasic, SbxArray & rPar, bool) +{ + if( !pBasic ) + StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR ); + else + { + OUString aErrorMsg; + ErrCode nErr = ERRCODE_NONE; + sal_Int32 nCode = 0; + if( rPar.Count32() == 1 ) + { + nErr = StarBASIC::GetErrBasic(); + aErrorMsg = StarBASIC::GetErrorMsg(); + } + else + { + nCode = rPar.Get32(1)->GetLong(); + if( nCode > 65535 ) + { + StarBASIC::Error( ERRCODE_BASIC_CONVERSION ); + } + else + { + nErr = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nCode) ); + } + } + + bool bVBA = SbiRuntime::isVBAEnabled(); + OUString tmpErrMsg; + if( bVBA && !aErrorMsg.isEmpty()) + { + tmpErrMsg = aErrorMsg; + } + else + { + StarBASIC::MakeErrorText( nErr, aErrorMsg ); + tmpErrMsg = StarBASIC::GetErrorText(); + } + // If this rtlfunc 'Error' passed an errcode the same as the active Err Objects's + // current err then return the description for the error message if it is set + // ( complicated isn't it ? ) + if ( bVBA && rPar.Count32() > 1 ) + { + uno::Reference< ooo::vba::XErrObject > xErrObj( SbxErrObject::getUnoErrObject() ); + if ( xErrObj.is() && xErrObj->getNumber() == nCode && !xErrObj->getDescription().isEmpty() ) + { + tmpErrMsg = xErrObj->getDescription(); + } + } + rPar.Get32(0)->PutString( tmpErrMsg ); + } +} + +// Sinus + +void SbRtl_Sin(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + SbxVariableRef pArg = rPar.Get32(1); + rPar.Get32(0)->PutDouble( sin( pArg->GetDouble() ) ); + } +} + + +void SbRtl_Cos(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + SbxVariableRef pArg = rPar.Get32(1); + rPar.Get32(0)->PutDouble( cos( pArg->GetDouble() ) ); + } +} + + +void SbRtl_Atn(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + SbxVariableRef pArg = rPar.Get32(1); + rPar.Get32(0)->PutDouble( atan( pArg->GetDouble() ) ); + } +} + + +void SbRtl_Abs(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariableRef pArg = rPar.Get32(1); + rPar.Get32(0)->PutDouble( fabs( pArg->GetDouble() ) ); + } +} + + +void SbRtl_Asc(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariableRef pArg = rPar.Get32(1); + OUString aStr( pArg->GetOUString() ); + if ( aStr.isEmpty()) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + rPar.Get32(0)->PutEmpty(); + } + else + { + sal_Unicode aCh = aStr[0]; + rPar.Get32(0)->PutLong( aCh ); + } + } +} + +static void implChr( SbxArray& rPar, bool bChrW ) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariableRef pArg = rPar.Get32(1); + + OUString aStr; + if( !bChrW && SbiRuntime::isVBAEnabled() ) + { + char c = static_cast<char>(pArg->GetByte()); + aStr = OUString(&c, 1, osl_getThreadTextEncoding()); + } + else + { + // Map negative 16-bit values to large positive ones, so that code like Chr(&H8000) + // still works after the fix for tdf#62326 changed those four-digit hex notations to + // produce negative values: + sal_Int32 aCh = pArg->GetLong(); + if (aCh < -0x8000 || aCh > 0xFFFF) { + StarBASIC::Error(ERRCODE_BASIC_MATH_OVERFLOW); + aCh = 0; + } + aStr = OUString(static_cast<sal_Unicode>(aCh)); + } + rPar.Get32(0)->PutString( aStr ); + } +} + +void SbRtl_Chr(StarBASIC *, SbxArray & rPar, bool) +{ + implChr( rPar, false/*bChrW*/ ); +} + +void SbRtl_ChrW(StarBASIC *, SbxArray & rPar, bool) +{ + implChr( rPar, true/*bChrW*/ ); +} + +#if defined _WIN32 + +namespace { + +extern "C" void invalidParameterHandler( + wchar_t const * expression, wchar_t const * function, wchar_t const * file, unsigned int line, + uintptr_t) +{ + SAL_INFO( + "basic", + "invalid parameter during _wgetdcwd; \"" << (expression ? o3tl::toU(expression) : u"???") + << "\" (" << (function ? o3tl::toU(function) : u"???") << ") at " + << (file ? o3tl::toU(file) : u"???") << ":" << line); +} + +} + +#endif + +void SbRtl_CurDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite) +{ + (void)pBasic; + (void)bWrite; + + // #57064 Although this function doesn't work with DirEntry, it isn't touched + // by the adjustment to virtual URLs, as, using the DirEntry-functionality, + // there's no possibility to detect the current one in a way that a virtual URL + // could be delivered. + +#if defined(_WIN32) + int nCurDir = 0; // Current dir // JSM + if ( rPar.Count32() == 2 ) + { + OUString aDrive = rPar.Get32(1)->GetOUString(); + if ( aDrive.getLength() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + auto c = rtl::toAsciiUpperCase(aDrive[0]); + if ( !rtl::isAsciiUpperCase( c ) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + nCurDir = c - 'A' + 1; + } + wchar_t pBuffer[ _MAX_PATH ]; + // _wgetdcwd calls the C runtime's invalid parameter handler (which by default terminates the + // process) if nCurDir does not correspond to an existing drive, so temporarily set a "harmless" + // handler: + auto const handler = _set_thread_local_invalid_parameter_handler(&invalidParameterHandler); + auto const ok = _wgetdcwd( nCurDir, pBuffer, _MAX_PATH ) != nullptr; + _set_thread_local_invalid_parameter_handler(handler); + if ( ok ) + { + rPar.Get32(0)->PutString( o3tl::toU(pBuffer) ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE ); + } + +#else + + const int PATH_INCR = 250; + + int nSize = PATH_INCR; + std::unique_ptr<char[]> pMem; + while( true ) + { + pMem.reset(new char[nSize]); + if( !pMem ) + { + StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY ); + return; + } + if( getcwd( pMem.get(), nSize-1 ) != nullptr ) + { + rPar.Get32(0)->PutString( OUString::createFromAscii(pMem.get()) ); + return; + } + if( errno != ERANGE ) + { + StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR ); + return; + } + nSize += PATH_INCR; + }; + +#endif +} + +void SbRtl_ChDir(StarBASIC * pBasic, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if (rPar.Count32() == 2) + { + // VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.) + if( SbiRuntime::isVBAEnabled() ) + { + ::basic::vba::registerCurrentDirectory( getDocumentModel( pBasic ), rPar.Get32(1)->GetOUString() ); + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_ChDrive(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if (rPar.Count32() != 2) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + + +// Implementation of StepRENAME with UCB +void implStepRenameUCB( const OUString& aSource, const OUString& aDest ) +{ + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( !xSFI.is() ) + return; + + try + { + OUString aSourceFullPath = getFullPath( aSource ); + if( !xSFI->exists( aSourceFullPath ) ) + { + StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND ); + return; + } + + OUString aDestFullPath = getFullPath( aDest ); + if( xSFI->exists( aDestFullPath ) ) + { + StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS ); + } + else + { + xSFI->move( aSourceFullPath, aDestFullPath ); + } + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND ); + } +} + +// Implementation of StepRENAME with OSL +void implStepRenameOSL( const OUString& aSource, const OUString& aDest ) +{ + FileBase::RC nRet = File::move( getFullPath( aSource ), getFullPath( aDest ) ); + if( nRet != FileBase::E_None ) + { + StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND ); + } +} + +void SbRtl_FileCopy(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if (rPar.Count32() == 3) + { + OUString aSource = rPar.Get32(1)->GetOUString(); + OUString aDest = rPar.Get32(2)->GetOUString(); + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + try + { + xSFI->copy( getFullPath( aSource ), getFullPath( aDest ) ); + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND ); + } + } + } + else + { + FileBase::RC nRet = File::copy( getFullPath( aSource ), getFullPath( aDest ) ); + if( nRet != FileBase::E_None ) + { + StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND ); + } + } + } + else + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); +} + +void SbRtl_Kill(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if (rPar.Count32() == 2) + { + OUString aFileSpec = rPar.Get32(1)->GetOUString(); + + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + OUString aFullPath = getFullPath( aFileSpec ); + if( !xSFI->exists( aFullPath ) || xSFI->isFolder( aFullPath ) ) + { + StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND ); + return; + } + try + { + xSFI->kill( aFullPath ); + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + } + } + else + { + File::remove( getFullPath( aFileSpec ) ); + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_MkDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite) +{ + rPar.Get32(0)->PutEmpty(); + if (rPar.Count32() == 2) + { + OUString aPath = rPar.Get32(1)->GetOUString(); + if ( SbiRuntime::isVBAEnabled() ) + { + // In vba if the full path is not specified then + // folder is created relative to the curdir + INetURLObject aURLObj( getFullPath( aPath ) ); + if ( aURLObj.GetProtocol() != INetProtocol::File ) + { + SbxArrayRef pPar = new SbxArray(); + SbxVariableRef pResult = new SbxVariable(); + SbxVariableRef pParam = new SbxVariable(); + pPar->Insert32( pResult.get(), pPar->Count32() ); + pPar->Insert32( pParam.get(), pPar->Count32() ); + SbRtl_CurDir( pBasic, *pPar, bWrite ); + + OUString sCurPathURL; + File::getFileURLFromSystemPath( pPar->Get32(0)->GetOUString(), sCurPathURL ); + + aURLObj.SetURL( sCurPathURL ); + aURLObj.Append( aPath ); + File::getSystemPathFromFileURL(aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ),aPath ) ; + } + } + + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + try + { + xSFI->createFolder( getFullPath( aPath ) ); + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + } + } + else + { + Directory::create( getFullPath( aPath ) ); + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + + +static void implRemoveDirRecursive( const OUString& aDirPath ) +{ + DirectoryItem aItem; + FileBase::RC nRet = DirectoryItem::get( aDirPath, aItem ); + bool bExists = (nRet == FileBase::E_None); + + FileStatus aFileStatus( osl_FileStatus_Mask_Type ); + nRet = aItem.getFileStatus( aFileStatus ); + bool bFolder = nRet == FileBase::E_None + && isFolder( aFileStatus.getFileType() ); + + if( !bExists || !bFolder ) + { + StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND ); + return; + } + + Directory aDir( aDirPath ); + nRet = aDir.open(); + if( nRet != FileBase::E_None ) + { + StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND ); + return; + } + aDir.close(); + + comphelper::DirectoryHelper::deleteDirRecursively(aDirPath); +} + + +void SbRtl_RmDir(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if (rPar.Count32() == 2) + { + OUString aPath = rPar.Get32(1)->GetOUString(); + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + try + { + if( !xSFI->isFolder( aPath ) ) + { + StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND ); + return; + } + SbiInstance* pInst = GetSbData()->pInst; + bool bCompatibility = ( pInst && pInst->IsCompatibility() ); + if( bCompatibility ) + { + Sequence< OUString > aContent = xSFI->getFolderContents( aPath, true ); + if( aContent.hasElements() ) + { + StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR ); + return; + } + } + + xSFI->kill( getFullPath( aPath ) ); + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + } + } + else + { + implRemoveDirRecursive( getFullPath( aPath ) ); + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_SendKeys(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED); +} + +void SbRtl_Exp(StarBASIC *, SbxArray & rPar, bool) +{ + if( rPar.Count32() < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + double aDouble = rPar.Get32(1)->GetDouble(); + aDouble = exp( aDouble ); + checkArithmeticOverflow( aDouble ); + rPar.Get32(0)->PutDouble( aDouble ); + } +} + +void SbRtl_FileLen(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariableRef pArg = rPar.Get32(1); + OUString aStr( pArg->GetOUString() ); + sal_Int32 nLen = 0; + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + try + { + nLen = xSFI->getSize( getFullPath( aStr ) ); + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + } + } + else + { + DirectoryItem aItem; + (void)DirectoryItem::get( getFullPath( aStr ), aItem ); + FileStatus aFileStatus( osl_FileStatus_Mask_FileSize ); + (void)aItem.getFileStatus( aFileStatus ); + nLen = static_cast<sal_Int32>(aFileStatus.getFileSize()); + } + rPar.Get32(0)->PutLong( static_cast<long>(nLen) ); + } +} + + +void SbRtl_Hex(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariableRef pArg = rPar.Get32(1); + // converting value to unsigned and limit to 2 or 4 byte representation + sal_uInt32 nVal = pArg->IsInteger() ? + static_cast<sal_uInt16>(pArg->GetInteger()) : + static_cast<sal_uInt32>(pArg->GetLong()); + OUString aStr(OUString::number( nVal, 16 )); + aStr = aStr.toAsciiUpperCase(); + rPar.Get32(0)->PutString( aStr ); + } +} + +void SbRtl_FuncCaller(StarBASIC *, SbxArray & rPar, bool) +{ + if ( SbiRuntime::isVBAEnabled() && GetSbData()->pInst && GetSbData()->pInst->pRun ) + { + if ( GetSbData()->pInst->pRun->GetExternalCaller() ) + *rPar.Get32(0) = *GetSbData()->pInst->pRun->GetExternalCaller(); + else + { + SbxVariableRef pVar = new SbxVariable(SbxVARIANT); + *rPar.Get32(0) = *pVar; + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED ); + } + +} +// InStr( [start],string,string,[compare] ) + +void SbRtl_InStr(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32()-1; + if ( nArgCount < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + sal_Int32 nStartPos = 1; + sal_Int32 nFirstStringPos = 1; + + if ( nArgCount >= 3 ) + { + nStartPos = rPar.Get32(1)->GetLong(); + if( nStartPos <= 0 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + nStartPos = 1; + } + nFirstStringPos++; + } + + SbiInstance* pInst = GetSbData()->pInst; + bool bTextMode; + bool bCompatibility = ( pInst && pInst->IsCompatibility() ); + if( bCompatibility ) + { + SbiRuntime* pRT = pInst->pRun; + bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT ); + } + else + { + bTextMode = true; + } + if ( nArgCount == 4 ) + { + bTextMode = rPar.Get32(4)->GetInteger(); + } + sal_Int32 nPos; + const OUString& rToken = rPar.Get32(nFirstStringPos+1)->GetOUString(); + + // #97545 Always find empty string + if( rToken.isEmpty() ) + { + nPos = nStartPos; + } + else + { + if( !bTextMode ) + { + const OUString& rStr1 = rPar.Get32(nFirstStringPos)->GetOUString(); + nPos = rStr1.indexOf( rToken, nStartPos - 1 ) + 1; + } + else + { + OUString aStr1 = rPar.Get32(nFirstStringPos)->GetOUString(); + OUString aToken = rToken; + + aStr1 = aStr1.toAsciiUpperCase(); + aToken = aToken.toAsciiUpperCase(); + + nPos = aStr1.indexOf( aToken, nStartPos-1 ) + 1; + } + } + rPar.Get32(0)->PutLong( nPos ); + } +} + + +// InstrRev(string1, string2[, start[, compare]]) + +void SbRtl_InStrRev(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32()-1; + if ( nArgCount < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUString aStr1 = rPar.Get32(1)->GetOUString(); + OUString aToken = rPar.Get32(2)->GetOUString(); + + sal_Int32 nStartPos = -1; + if ( nArgCount >= 3 ) + { + nStartPos = rPar.Get32(3)->GetLong(); + if( nStartPos <= 0 && nStartPos != -1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + nStartPos = -1; + } + } + + SbiInstance* pInst = GetSbData()->pInst; + bool bTextMode; + bool bCompatibility = ( pInst && pInst->IsCompatibility() ); + if( bCompatibility ) + { + SbiRuntime* pRT = pInst->pRun; + bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT ); + } + else + { + bTextMode = true; + } + if ( nArgCount == 4 ) + { + bTextMode = rPar.Get32(4)->GetInteger(); + } + sal_Int32 nStrLen = aStr1.getLength(); + if( nStartPos == -1 ) + { + nStartPos = nStrLen; + } + + sal_Int32 nPos = 0; + if( nStartPos <= nStrLen ) + { + sal_Int32 nTokenLen = aToken.getLength(); + if( !nTokenLen ) + { + // Always find empty string + nPos = nStartPos; + } + else if( nStrLen > 0 ) + { + if( !bTextMode ) + { + nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1; + } + else + { + aStr1 = aStr1.toAsciiUpperCase(); + aToken = aToken.toAsciiUpperCase(); + + nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1; + } + } + } + rPar.Get32(0)->PutLong( nPos ); + } +} + + +/* + Int( 2.8 ) = 2.0 + Int( -2.8 ) = -3.0 + Fix( 2.8 ) = 2.0 + Fix( -2.8 ) = -2.0 <- !! +*/ + +void SbRtl_Int(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + SbxVariableRef pArg = rPar.Get32(1); + double aDouble= pArg->GetDouble(); + /* + floor( 2.8 ) = 2.0 + floor( -2.8 ) = -3.0 + */ + aDouble = floor( aDouble ); + rPar.Get32(0)->PutDouble( aDouble ); + } +} + + +void SbRtl_Fix(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + SbxVariableRef pArg = rPar.Get32(1); + double aDouble = pArg->GetDouble(); + if ( aDouble >= 0.0 ) + aDouble = floor( aDouble ); + else + aDouble = ceil( aDouble ); + rPar.Get32(0)->PutDouble( aDouble ); + } +} + + +void SbRtl_LCase(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + const CharClass& rCharClass = GetCharClass(); + OUString aStr( rPar.Get32(1)->GetOUString() ); + aStr = rCharClass.lowercase(aStr); + rPar.Get32(0)->PutString( aStr ); + } +} + +void SbRtl_Left(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUString aStr( rPar.Get32(1)->GetOUString() ); + sal_Int32 nResultLen = rPar.Get32(2)->GetLong(); + if( nResultLen < 0 ) + { + nResultLen = 0; + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else if(nResultLen > aStr.getLength()) + { + nResultLen = aStr.getLength(); + } + aStr = aStr.copy(0, nResultLen ); + rPar.Get32(0)->PutString( aStr ); + } +} + +void SbRtl_Log(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + double aArg = rPar.Get32(1)->GetDouble(); + if ( aArg > 0 ) + { + double d = log( aArg ); + checkArithmeticOverflow( d ); + rPar.Get32(0)->PutDouble( d ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + } +} + +void SbRtl_LTrim(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUString aStr(comphelper::string::stripStart(rPar.Get32(1)->GetOUString(), ' ')); + rPar.Get32(0)->PutString(aStr); + } +} + + +// Mid( String, nStart, nLength ) + +void SbRtl_Mid(StarBASIC *, SbxArray & rPar, bool bWrite) +{ + int nArgCount = rPar.Count32()-1; + if ( nArgCount < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + // #23178: replicate the functionality of Mid$ as a command + // by adding a replacement-string as a fourth parameter. + // In contrast to the original the third parameter (nLength) + // can't be left out here. That's considered in bWrite already. + if( nArgCount == 4 ) + { + bWrite = true; + } + OUString aArgStr = rPar.Get32(1)->GetOUString(); + sal_Int32 nStartPos = rPar.Get32(2)->GetLong(); + if ( nStartPos < 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + nStartPos--; + sal_Int32 nLen = -1; + bool bWriteNoLenParam = false; + if ( nArgCount == 3 || bWrite ) + { + sal_Int32 n = rPar.Get32(3)->GetLong(); + if( bWrite && n == -1 ) + { + bWriteNoLenParam = true; + } + nLen = n; + } + if ( bWrite ) + { + sal_Int32 nArgLen = aArgStr.getLength(); + if( nStartPos > nArgLen ) + { + SbiInstance* pInst = GetSbData()->pInst; + bool bCompatibility = ( pInst && pInst->IsCompatibility() ); + if( bCompatibility ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + nStartPos = nArgLen; + } + + OUString aReplaceStr = rPar.Get32(4)->GetOUString(); + sal_Int32 nReplaceStrLen = aReplaceStr.getLength(); + sal_Int32 nReplaceLen; + if( bWriteNoLenParam ) + { + nReplaceLen = nArgLen - nStartPos; + } + else + { + nReplaceLen = nLen; + if( nReplaceLen < 0 || nReplaceLen > nArgLen - nStartPos ) + { + nReplaceLen = nArgLen - nStartPos; + } + } + + OUStringBuffer aResultStr = aArgStr; + sal_Int32 nErase = nReplaceLen; + aResultStr.remove( nStartPos, nErase ); + aResultStr.insert( + nStartPos, aReplaceStr.getStr(), std::min(nReplaceLen, nReplaceStrLen)); + + rPar.Get32(1)->PutString( aResultStr.makeStringAndClear() ); + } + else + { + OUString aResultStr; + if (nStartPos > aArgStr.getLength()) + { + // do nothing + } + else if(nArgCount == 2) + { + aResultStr = aArgStr.copy( nStartPos); + } + else + { + if (nLen < 0) + nLen = 0; + if(nStartPos + nLen > aArgStr.getLength()) + { + nLen = aArgStr.getLength() - nStartPos; + } + if (nLen > 0) + aResultStr = aArgStr.copy( nStartPos, nLen ); + } + rPar.Get32(0)->PutString( aResultStr ); + } + } + } +} + +void SbRtl_Oct(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + char aBuffer[16]; + SbxVariableRef pArg = rPar.Get32(1); + if ( pArg->IsInteger() ) + { + snprintf( aBuffer, sizeof(aBuffer), "%o", pArg->GetInteger() ); + } + else + { + snprintf( aBuffer, sizeof(aBuffer), "%lo", static_cast<long unsigned int>(pArg->GetLong()) ); + } + rPar.Get32(0)->PutString( OUString::createFromAscii( aBuffer ) ); + } +} + +// Replace(expression, find, replace[, start[, count[, compare]]]) + +void SbRtl_Replace(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32()-1; + if ( nArgCount < 3 || nArgCount > 6 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + sal_Int32 lStartPos = 1; + if (nArgCount >= 4) + { + if (rPar.Get32(4)->GetType() != SbxEMPTY) + { + lStartPos = rPar.Get32(4)->GetLong(); + } + if (lStartPos < 1) + { + StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT); + return; + } + } + + sal_Int32 lCount = -1; + if (nArgCount >= 5) + { + if (rPar.Get32(5)->GetType() != SbxEMPTY) + { + lCount = rPar.Get32(5)->GetLong(); + } + if (lCount < -1) + { + StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT); + return; + } + } + + bool bCaseInsensitive; + if (nArgCount == 6) + { + bCaseInsensitive = rPar.Get32(6)->GetInteger(); + } + else + { + SbiInstance* pInst = GetSbData()->pInst; + if (pInst && pInst->IsCompatibility()) + { + SbiRuntime* pRT = pInst->pRun; + bCaseInsensitive = pRT && pRT->IsImageFlag(SbiImageFlags::COMPARETEXT); + } + else + { + bCaseInsensitive = true; + } + } + + const OUString aExpStr = rPar.Get32(1)->GetOUString(); + OUString aFindStr = rPar.Get32(2)->GetOUString(); + const OUString aReplaceStr = rPar.Get32(3)->GetOUString(); + const sal_Int32 nExpStrLen = aExpStr.getLength(); + const sal_Int32 nFindStrLen = aFindStr.getLength(); + + OUString aSrcStr(aExpStr); + if (bCaseInsensitive) + { + // tdf#132389 - case-insensitive operation for non-ASCII characters + const css::lang::Locale& rLocale = Application::GetSettings().GetUILanguageTag().getLocale(); + css::uno::Reference < i18n::XCharacterClassification > xCharClass = vcl::unohelper::CreateCharacterClassification(); + aSrcStr = xCharClass->toUpper(aSrcStr, 0, aSrcStr.getLength(), rLocale); + aFindStr = xCharClass->toUpper(aFindStr, 0, aSrcStr.getLength(), rLocale); + } + + // Note: the result starts from lStartPos, removing everything to the left. See i#94895. + sal_Int32 nPrevPos = std::min(lStartPos - 1, nExpStrLen); + OUStringBuffer sResult(nExpStrLen - nPrevPos); + sal_Int32 nCounts = 0; + while (lCount == -1 || lCount > nCounts) + { + sal_Int32 nPos = aSrcStr.indexOf(aFindStr, nPrevPos); + if (nPos >= 0) + { + sResult.append(aExpStr.getStr() + nPrevPos, nPos - nPrevPos); + sResult.append(aReplaceStr); + nPrevPos = nPos + nFindStrLen; + nCounts++; + } + else + { + break; + } + } + sResult.append(aExpStr.getStr() + nPrevPos, nExpStrLen - nPrevPos); + rPar.Get32(0)->PutString(sResult.makeStringAndClear()); +} + +void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + const OUString& rStr = rPar.Get32(1)->GetOUString(); + int nResultLen = rPar.Get32(2)->GetLong(); + if( nResultLen < 0 ) + { + nResultLen = 0; + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + int nStrLen = rStr.getLength(); + if ( nResultLen > nStrLen ) + { + nResultLen = nStrLen; + } + OUString aResultStr = rStr.copy( nStrLen - nResultLen ); + rPar.Get32(0)->PutString( aResultStr ); + } +} + +void SbRtl_RTL(StarBASIC * pBasic, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutObject( pBasic->getRTL().get() ); +} + +void SbRtl_RTrim(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUString aStr(comphelper::string::stripEnd(rPar.Get32(1)->GetOUString(), ' ')); + rPar.Get32(0)->PutString(aStr); + } +} + +void SbRtl_Sgn(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + double aDouble = rPar.Get32(1)->GetDouble(); + sal_Int16 nResult = 0; + if ( aDouble > 0 ) + { + nResult = 1; + } + else if ( aDouble < 0 ) + { + nResult = -1; + } + rPar.Get32(0)->PutInteger( nResult ); + } +} + +void SbRtl_Space(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUStringBuffer aBuf; + string::padToLength(aBuf, rPar.Get32(1)->GetLong(), ' '); + rPar.Get32(0)->PutString(aBuf.makeStringAndClear()); + } +} + +void SbRtl_Spc(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUStringBuffer aBuf; + string::padToLength(aBuf, rPar.Get32(1)->GetLong(), ' '); + rPar.Get32(0)->PutString(aBuf.makeStringAndClear()); + } +} + +void SbRtl_Sqr(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + double aDouble = rPar.Get32(1)->GetDouble(); + if ( aDouble >= 0 ) + { + rPar.Get32(0)->PutDouble( sqrt( aDouble )); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + } +} + +void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUString aStr; + OUString aStrNew(""); + SbxVariableRef pArg = rPar.Get32(1); + pArg->Format( aStr ); + + // Numbers start with a space + if( pArg->IsNumericRTL() ) + { + // replace commas by points so that it's symmetric to Val! + aStr = aStr.replaceFirst( ",", "." ); + + SbiInstance* pInst = GetSbData()->pInst; + bool bCompatibility = ( pInst && pInst->IsCompatibility() ); + if( bCompatibility ) + { + sal_Int32 nLen = aStr.getLength(); + + const sal_Unicode* pBuf = aStr.getStr(); + + bool bNeg = ( pBuf[0] == '-' ); + sal_Int32 iZeroSearch = 0; + if( bNeg ) + { + aStrNew += "-"; + iZeroSearch++; + } + else + { + if( pBuf[0] != ' ' ) + { + aStrNew += " "; + } + } + sal_Int32 iNext = iZeroSearch + 1; + if( pBuf[iZeroSearch] == '0' && nLen > iNext && pBuf[iNext] == '.' ) + { + iZeroSearch += 1; + } + aStrNew += aStr.copy(iZeroSearch); + } + else + { + aStrNew = " " + aStr; + } + } + else + { + aStrNew = aStr; + } + rPar.Get32(0)->PutString( aStrNew ); + } +} + +void SbRtl_StrComp(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + rPar.Get32(0)->PutEmpty(); + return; + } + const OUString& rStr1 = rPar.Get32(1)->GetOUString(); + const OUString& rStr2 = rPar.Get32(2)->GetOUString(); + + SbiInstance* pInst = GetSbData()->pInst; + bool bTextCompare; + bool bCompatibility = ( pInst && pInst->IsCompatibility() ); + if( bCompatibility ) + { + SbiRuntime* pRT = pInst->pRun; + bTextCompare = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT ); + } + else + { + bTextCompare = true; + } + if ( rPar.Count32() == 4 ) + bTextCompare = rPar.Get32(3)->GetInteger(); + + if( !bCompatibility ) + { + bTextCompare = !bTextCompare; + } + sal_Int32 nRetValue = 0; + if( bTextCompare ) + { + ::utl::TransliterationWrapper* pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get(); + if( !pTransliterationWrapper ) + { + uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext(); + GetSbData()->pTransliterationWrapper.reset( + new ::utl::TransliterationWrapper( xContext, + TransliterationFlags::IGNORE_CASE | + TransliterationFlags::IGNORE_KANA | + TransliterationFlags::IGNORE_WIDTH ) ); + pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get(); + } + + LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType(); + pTransliterationWrapper->loadModuleIfNeeded( eLangType ); + nRetValue = pTransliterationWrapper->compareString( rStr1, rStr2 ); + } + else + { + sal_Int32 aResult; + aResult = rStr1.compareTo( rStr2 ); + if ( aResult < 0 ) + { + nRetValue = -1; + } + else if ( aResult > 0) + { + nRetValue = 1; + } + } + rPar.Get32(0)->PutInteger( sal::static_int_cast< sal_Int16 >( nRetValue ) ); +} + +void SbRtl_String(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Unicode aFiller; + sal_Int32 lCount = rPar.Get32(1)->GetLong(); + if( lCount < 0 || lCount > 0xffff ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + if( rPar.Get32(2)->GetType() == SbxINTEGER ) + { + aFiller = static_cast<sal_Unicode>(rPar.Get32(2)->GetInteger()); + } + else + { + const OUString& rStr = rPar.Get32(2)->GetOUString(); + aFiller = rStr[0]; + } + OUStringBuffer aBuf(lCount); + string::padToLength(aBuf, lCount, aFiller); + rPar.Get32(0)->PutString(aBuf.makeStringAndClear()); + } +} + +void SbRtl_Tab(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + else + { + OUStringBuffer aStr; + comphelper::string::padToLength(aStr, rPar.Get32(1)->GetLong(), '\t'); + rPar.Get32(0)->PutString(aStr.makeStringAndClear()); + } +} + +void SbRtl_Tan(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariableRef pArg = rPar.Get32(1); + rPar.Get32(0)->PutDouble( tan( pArg->GetDouble() ) ); + } +} + +void SbRtl_UCase(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + const CharClass& rCharClass = GetCharClass(); + OUString aStr( rPar.Get32(1)->GetOUString() ); + aStr = rCharClass.uppercase( aStr ); + rPar.Get32(0)->PutString( aStr ); + } +} + + +void SbRtl_Val(StarBASIC * pBasic, SbxArray & rPar, bool bWrite) +{ + (void)pBasic; + (void)bWrite; + + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + double nResult = 0.0; + char* pEndPtr; + + OUString aStr( rPar.Get32(1)->GetOUString() ); + + FilterWhiteSpace( aStr ); + if ( aStr.getLength() > 1 && aStr[0] == '&' ) + { + int nRadix = 10; + char aChar = static_cast<char>(aStr[1]); + if ( aChar == 'h' || aChar == 'H' ) + { + nRadix = 16; + } + else if ( aChar == 'o' || aChar == 'O' ) + { + nRadix = 8; + } + if ( nRadix != 10 ) + { + OString aByteStr(OUStringToOString(aStr, osl_getThreadTextEncoding())); + sal_Int16 nlResult = static_cast<sal_Int16>(strtol( aByteStr.getStr()+2, &pEndPtr, nRadix)); + nResult = static_cast<double>(nlResult); + } + } + else + { + rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok; + sal_Int32 nParseEnd = 0; + nResult = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd ); + if ( eStatus != rtl_math_ConversionStatus_Ok ) + StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW ); + /* TODO: we should check whether all characters were parsed here, + * but earlier code silently ignored trailing nonsense such as "1x" + * resulting in 1 with the side effect that any alpha-only-string + * like "x" resulted in 0. Not changing that now (2013-03-22) as + * user macros may rely on it. */ +#if 0 + else if ( nParseEnd != aStr.getLength() ) + StarBASIC::Error( ERRCODE_BASIC_CONVERSION ); +#endif + } + + rPar.Get32(0)->PutDouble( nResult ); + } +} + + +// Helper functions for date conversion +sal_Int16 implGetDateDay( double aDate ) +{ + aDate -= 2.0; // standardize: 1.1.1900 => 0.0 + aDate = floor( aDate ); + Date aRefDate( 1, 1, 1900 ); + aRefDate.AddDays( aDate ); + + sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetDay() ); + return nRet; +} + +sal_Int16 implGetDateMonth( double aDate ) +{ + Date aRefDate( 1,1,1900 ); + sal_Int32 nDays = static_cast<sal_Int32>(aDate); + nDays -= 2; // standardize: 1.1.1900 => 0.0 + aRefDate.AddDays( nDays ); + sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetMonth() ); + return nRet; +} + +css::util::Date SbxDateToUNODate( const SbxValue* const pVal ) +{ + double aDate = pVal->GetDate(); + + css::util::Date aUnoDate; + aUnoDate.Day = implGetDateDay ( aDate ); + aUnoDate.Month = implGetDateMonth( aDate ); + aUnoDate.Year = implGetDateYear ( aDate ); + + return aUnoDate; +} + +void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate) +{ + double dDate; + if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, SbDateCorrection::None, dDate ) ) + { + pVal->PutDate( dDate ); + } +} + +// Function to convert date to UNO date (com.sun.star.util.Date) +void SbRtl_CDateToUnoDate(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + unoToSbxValue(rPar.Get32(0), Any(SbxDateToUNODate(rPar.Get32(1)))); +} + +// Function to convert date from UNO date (com.sun.star.util.Date) +void SbRtl_CDateFromUnoDate(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 || rPar.Get32(1)->GetType() != SbxOBJECT ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + Any aAny (sbxToUnoValue(rPar.Get32(1), cppu::UnoType<css::util::Date>::get())); + css::util::Date aUnoDate; + if(aAny >>= aUnoDate) + SbxDateFromUNODate(rPar.Get32(0), aUnoDate); + else + SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); +} + +css::util::Time SbxDateToUNOTime( const SbxValue* const pVal ) +{ + double aDate = pVal->GetDate(); + + css::util::Time aUnoTime; + aUnoTime.Hours = implGetHour ( aDate ); + aUnoTime.Minutes = implGetMinute ( aDate ); + aUnoTime.Seconds = implGetSecond ( aDate ); + aUnoTime.NanoSeconds = 0; + + return aUnoTime; +} + +void SbxDateFromUNOTime( SbxValue *pVal, const css::util::Time& aUnoTime) +{ + pVal->PutDate( implTimeSerial(aUnoTime.Hours, aUnoTime.Minutes, aUnoTime.Seconds) ); +} + +// Function to convert date to UNO time (com.sun.star.util.Time) +void SbRtl_CDateToUnoTime(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + unoToSbxValue(rPar.Get32(0), Any(SbxDateToUNOTime(rPar.Get32(1)))); +} + +// Function to convert date from UNO time (com.sun.star.util.Time) +void SbRtl_CDateFromUnoTime(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 || rPar.Get32(1)->GetType() != SbxOBJECT ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + Any aAny (sbxToUnoValue(rPar.Get32(1), cppu::UnoType<css::util::Time>::get())); + css::util::Time aUnoTime; + if(aAny >>= aUnoTime) + SbxDateFromUNOTime(rPar.Get32(0), aUnoTime); + else + SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); +} + +css::util::DateTime SbxDateToUNODateTime( const SbxValue* const pVal ) +{ + double aDate = pVal->GetDate(); + + css::util::DateTime aUnoDT; + aUnoDT.Day = implGetDateDay ( aDate ); + aUnoDT.Month = implGetDateMonth( aDate ); + aUnoDT.Year = implGetDateYear ( aDate ); + aUnoDT.Hours = implGetHour ( aDate ); + aUnoDT.Minutes = implGetMinute ( aDate ); + aUnoDT.Seconds = implGetSecond ( aDate ); + aUnoDT.NanoSeconds = 0; + + return aUnoDT; +} + +void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT) +{ + double dDate(0.0); + if( implDateTimeSerial( aUnoDT.Year, aUnoDT.Month, aUnoDT.Day, + aUnoDT.Hours, aUnoDT.Minutes, aUnoDT.Seconds, + dDate ) ) + { + pVal->PutDate( dDate ); + } +} + +// Function to convert date to UNO date (com.sun.star.util.Date) +void SbRtl_CDateToUnoDateTime(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + unoToSbxValue(rPar.Get32(0), Any(SbxDateToUNODateTime(rPar.Get32(1)))); +} + +// Function to convert date from UNO date (com.sun.star.util.Date) +void SbRtl_CDateFromUnoDateTime(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 || rPar.Get32(1)->GetType() != SbxOBJECT ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + Any aAny (sbxToUnoValue(rPar.Get32(1), cppu::UnoType<css::util::DateTime>::get())); + css::util::DateTime aUnoDT; + if(aAny >>= aUnoDT) + SbxDateFromUNODateTime(rPar.Get32(0), aUnoDT); + else + SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); +} + +// Function to convert date to ISO 8601 date format YYYYMMDD +void SbRtl_CDateToIso(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() == 2 ) + { + double aDate = rPar.Get32(1)->GetDate(); + + // Date may actually even be -YYYYYMMDD + char Buffer[11]; + sal_Int16 nYear = implGetDateYear( aDate ); + snprintf( Buffer, sizeof( Buffer ), (nYear < 0 ? "%05d%02d%02d" : "%04d%02d%02d"), + static_cast<int>(nYear), + static_cast<int>(implGetDateMonth( aDate )), + static_cast<int>(implGetDateDay( aDate )) ); + OUString aRetStr = OUString::createFromAscii( Buffer ); + rPar.Get32(0)->PutString( aRetStr ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +// Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD +// And even YYMMDD for compatibility, sigh... +void SbRtl_CDateFromIso(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() == 2 ) + { + do + { + OUString aStr = rPar.Get32(1)->GetOUString(); + if (aStr.isEmpty()) + break; + + // Valid formats are + // YYYYMMDD -YYYMMDD YYYYYMMDD -YYYYYMMDD YYMMDD + // YYYY-MM-DD -YYYY-MM-DD YYYYY-MM-DD -YYYYY-MM-DD + + sal_Int32 nSign = 1; + if (aStr[0] == '-') + { + nSign = -1; + aStr = aStr.copy(1); + } + const sal_Int32 nLen = aStr.getLength(); + + // Signed YYMMDD two digit year is invalid. + if (nLen == 6 && nSign == -1) + break; + + // Now valid + // YYYYMMDD YYYYYMMDD YYMMDD + // YYYY-MM-DD YYYYY-MM-DD + if (nLen != 6 && (nLen < 8 || 11 < nLen)) + break; + + bool bUseTwoDigitYear = false; + OUString aYearStr, aMonthStr, aDayStr; + if (nLen == 6 || nLen == 8 || nLen == 9) + { + // ((Y)YY)YYMMDD + if (!comphelper::string::isdigitAsciiString(aStr)) + break; + + const sal_Int32 nMonthPos = (nLen == 8 ? 4 : (nLen == 6 ? 2 : 5)); + if (nMonthPos == 2) + bUseTwoDigitYear = true; + aYearStr = aStr.copy( 0, nMonthPos ); + aMonthStr = aStr.copy( nMonthPos, 2 ); + aDayStr = aStr.copy( nMonthPos + 2, 2 ); + } + else + { + // (Y)YYYY-MM-DD + const sal_Int32 nMonthSep = (nLen == 11 ? 5 : 4); + if (aStr.indexOf('-') != nMonthSep) + break; + if (aStr.indexOf('-', nMonthSep + 1) != nMonthSep + 3) + break; + + aYearStr = aStr.copy( 0, nMonthSep ); + aMonthStr = aStr.copy( nMonthSep + 1, 2 ); + aDayStr = aStr.copy( nMonthSep + 4, 2 ); + if ( !comphelper::string::isdigitAsciiString(aYearStr) || + !comphelper::string::isdigitAsciiString(aMonthStr) || + !comphelper::string::isdigitAsciiString(aDayStr)) + break; + } + + double dDate; + if (!implDateSerial( static_cast<sal_Int16>(nSign * aYearStr.toInt32()), + static_cast<sal_Int16>(aMonthStr.toInt32()), static_cast<sal_Int16>(aDayStr.toInt32()), + bUseTwoDigitYear, SbDateCorrection::None, dDate )) + break; + + rPar.Get32(0)->PutDate( dDate ); + + return; + } + while (false); + + SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_DateSerial(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + sal_Int16 nYear = rPar.Get32(1)->GetInteger(); + sal_Int16 nMonth = rPar.Get32(2)->GetInteger(); + sal_Int16 nDay = rPar.Get32(3)->GetInteger(); + + double dDate; + if( implDateSerial( nYear, nMonth, nDay, true, SbDateCorrection::RollOver, dDate ) ) + { + rPar.Get32(0)->PutDate( dDate ); + } +} + +void SbRtl_TimeSerial(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + sal_Int16 nHour = rPar.Get32(1)->GetInteger(); + if ( nHour == 24 ) + { + nHour = 0; // because of UNO DateTimes, which go till 24 o'clock + } + sal_Int16 nMinute = rPar.Get32(2)->GetInteger(); + sal_Int16 nSecond = rPar.Get32(3)->GetInteger(); + if ((nHour < 0 || nHour > 23) || + (nMinute < 0 || nMinute > 59 ) || + (nSecond < 0 || nSecond > 59 )) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + rPar.Get32(0)->PutDate( implTimeSerial(nHour, nMinute, nSecond) ); // JSM +} + +void SbRtl_DateValue(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + // #39629 check GetSbData()->pInst, can be called from the URL line + std::shared_ptr<SvNumberFormatter> pFormatter; + if( GetSbData()->pInst ) + { + pFormatter = GetSbData()->pInst->GetNumberFormatter(); + } + else + { + sal_uInt32 n; // Dummy + pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n ); + } + + LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType(); + sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType); + double fResult; + OUString aStr( rPar.Get32(1)->GetOUString() ); + bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult ); + SvNumFormatType nType = pFormatter->GetType( nIndex ); + + // DateValue("February 12, 1969") raises error if the system locale is not en_US + // It seems that both locale number formatter and English number + // formatter are supported in Visual Basic. + if( !bSuccess && ( eLangType != LANGUAGE_ENGLISH_US ) ) + { + // Try using LANGUAGE_ENGLISH_US to get the date value. + nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US); + bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult ); + nType = pFormatter->GetType( nIndex ); + } + + if(bSuccess && (nType==SvNumFormatType::DATE || nType==SvNumFormatType::DATETIME)) + { + if ( nType == SvNumFormatType::DATETIME ) + { + // cut time + if ( fResult > 0.0 ) + { + fResult = floor( fResult ); + } + else + { + fResult = ceil( fResult ); + } + } + rPar.Get32(0)->PutDate( fResult ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_CONVERSION ); + } + } +} + +void SbRtl_TimeValue(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + std::shared_ptr<SvNumberFormatter> pFormatter; + if( GetSbData()->pInst ) + pFormatter = GetSbData()->pInst->GetNumberFormatter(); + else + { + sal_uInt32 n; + pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n ); + } + + sal_uInt32 nIndex = 0; + double fResult; + bool bSuccess = pFormatter->IsNumberFormat( rPar.Get32(1)->GetOUString(), + nIndex, fResult ); + SvNumFormatType nType = pFormatter->GetType(nIndex); + if(bSuccess && (nType==SvNumFormatType::TIME||nType==SvNumFormatType::DATETIME)) + { + if ( nType == SvNumFormatType::DATETIME ) + { + // cut days + fResult = fmod( fResult, 1 ); + } + rPar.Get32(0)->PutDate( fResult ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_CONVERSION ); + } + } +} + +void SbRtl_Day(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariableRef pArg = rPar.Get32(1); + double aDate = pArg->GetDate(); + + sal_Int16 nDay = implGetDateDay( aDate ); + rPar.Get32(0)->PutInteger( nDay ); + } +} + +void SbRtl_Year(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int16 nYear = implGetDateYear( rPar.Get32(1)->GetDate() ); + rPar.Get32(0)->PutInteger( nYear ); + } +} + +sal_Int16 implGetHour( double dDate ) +{ + double nFrac = dDate - floor( dDate ); + nFrac *= 86400.0; + sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5); + sal_Int16 nHour = static_cast<sal_Int16>(nSeconds / 3600); + return nHour; +} + +void SbRtl_Hour(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + double nArg = rPar.Get32(1)->GetDate(); + sal_Int16 nHour = implGetHour( nArg ); + rPar.Get32(0)->PutInteger( nHour ); + } +} + +void SbRtl_Minute(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + double nArg = rPar.Get32(1)->GetDate(); + sal_Int16 nMin = implGetMinute( nArg ); + rPar.Get32(0)->PutInteger( nMin ); + } +} + +void SbRtl_Month(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int16 nMonth = implGetDateMonth( rPar.Get32(1)->GetDate() ); + rPar.Get32(0)->PutInteger( nMonth ); + } +} + +sal_Int16 implGetSecond( double dDate ) +{ + double nFrac = dDate - floor( dDate ); + nFrac *= 86400.0; + sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5); + sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds / 3600); + nSeconds -= nTemp * 3600; + nTemp = static_cast<sal_Int16>(nSeconds / 60); + nSeconds -= nTemp * 60; + + sal_Int16 nRet = static_cast<sal_Int16>(nSeconds); + return nRet; +} + +void SbRtl_Second(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + double nArg = rPar.Get32(1)->GetDate(); + sal_Int16 nSecond = implGetSecond( nArg ); + rPar.Get32(0)->PutInteger( nSecond ); + } +} + +double Now_Impl() +{ + DateTime aDateTime( DateTime::SYSTEM ); + double aSerial = static_cast<double>(GetDayDiff( aDateTime )); + long nSeconds = aDateTime.GetHour(); + nSeconds *= 3600; + nSeconds += aDateTime.GetMin() * 60; + nSeconds += aDateTime.GetSec(); + double nDays = static_cast<double>(nSeconds) / (24.0*3600.0); + aSerial += nDays; + return aSerial; +} + +// Date Now() + +void SbRtl_Now(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutDate( Now_Impl() ); +} + +// Date Time() + +void SbRtl_Time(StarBASIC *, SbxArray & rPar, bool bWrite) +{ + if ( !bWrite ) + { + tools::Time aTime( tools::Time::SYSTEM ); + SbxVariable* pMeth = rPar.Get32(0); + OUString aRes; + if( pMeth->IsFixed() ) + { + // Time$: hh:mm:ss + char buf[ 20 ]; + snprintf( buf, sizeof(buf), "%02d:%02d:%02d", + aTime.GetHour(), aTime.GetMin(), aTime.GetSec() ); + aRes = OUString::createFromAscii( buf ); + } + else + { + // Time: system dependent + long nSeconds=aTime.GetHour(); + nSeconds *= 3600; + nSeconds += aTime.GetMin() * 60; + nSeconds += aTime.GetSec(); + double nDays = static_cast<double>(nSeconds) * ( 1.0 / (24.0*3600.0) ); + Color* pCol; + + std::shared_ptr<SvNumberFormatter> pFormatter; + sal_uInt32 nIndex; + if( GetSbData()->pInst ) + { + pFormatter = GetSbData()->pInst->GetNumberFormatter(); + nIndex = GetSbData()->pInst->GetStdTimeIdx(); + } + else + { + sal_uInt32 n; // Dummy + pFormatter = SbiInstance::PrepareNumberFormatter( n, nIndex, n ); + } + + pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol ); + } + pMeth->PutString( aRes ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED ); + } +} + +void SbRtl_Timer(StarBASIC *, SbxArray & rPar, bool) +{ + tools::Time aTime( tools::Time::SYSTEM ); + long nSeconds = aTime.GetHour(); + nSeconds *= 3600; + nSeconds += aTime.GetMin() * 60; + nSeconds += aTime.GetSec(); + rPar.Get32(0)->PutDate( static_cast<double>(nSeconds) ); +} + + +void SbRtl_Date(StarBASIC *, SbxArray & rPar, bool bWrite) +{ + if ( !bWrite ) + { + Date aToday( Date::SYSTEM ); + double nDays = static_cast<double>(GetDayDiff( aToday )); + SbxVariable* pMeth = rPar.Get32(0); + if( pMeth->IsString() ) + { + OUString aRes; + Color* pCol; + + std::shared_ptr<SvNumberFormatter> pFormatter; + sal_uInt32 nIndex; + if( GetSbData()->pInst ) + { + pFormatter = GetSbData()->pInst->GetNumberFormatter(); + nIndex = GetSbData()->pInst->GetStdDateIdx(); + } + else + { + sal_uInt32 n; + pFormatter = SbiInstance::PrepareNumberFormatter( nIndex, n, n ); + } + + pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol ); + pMeth->PutString( aRes ); + } + else + { + pMeth->PutDate( nDays ); + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED ); + } +} + +void SbRtl_IsArray(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + rPar.Get32(0)->PutBool((rPar.Get32(1)->GetType() & SbxARRAY) != 0); + } +} + +void SbRtl_IsObject(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariable* pVar = rPar.Get32(1); + bool bObject = pVar->IsObject(); + SbxBase* pObj = (bObject ? pVar->GetObject() : nullptr); + + if( auto pUnoClass = dynamic_cast<SbUnoClass*>( pObj) ) + { + bObject = pUnoClass->getUnoClass().is(); + } + rPar.Get32(0)->PutBool( bObject ); + } +} + +void SbRtl_IsDate(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + // #46134 only string is converted, all other types result in sal_False + SbxVariableRef xArg = rPar.Get32(1); + SbxDataType eType = xArg->GetType(); + bool bDate = false; + + if( eType == SbxDATE ) + { + bDate = true; + } + else if( eType == SbxSTRING ) + { + ErrCode nPrevError = SbxBase::GetError(); + SbxBase::ResetError(); + + // force conversion of the parameter to SbxDATE + xArg->SbxValue::GetDate(); + + bDate = !SbxBase::IsError(); + + SbxBase::ResetError(); + SbxBase::SetError( nPrevError ); + } + rPar.Get32(0)->PutBool( bDate ); + } +} + +void SbRtl_IsEmpty(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariable* pVar = nullptr; + if( SbiRuntime::isVBAEnabled() ) + { + pVar = getDefaultProp( rPar.Get32(1) ); + } + if ( pVar ) + { + pVar->Broadcast( SfxHintId::BasicDataWanted ); + rPar.Get32(0)->PutBool( pVar->IsEmpty() ); + } + else + { + rPar.Get32(0)->PutBool( rPar.Get32(1)->IsEmpty() ); + } + } +} + +void SbRtl_IsError(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxVariable* pVar =rPar.Get32(1); + SbUnoObject* pObj = dynamic_cast<SbUnoObject*>( pVar ); + if ( !pObj ) + { + if ( SbxBase* pBaseObj = (pVar->IsObject() ? pVar->GetObject() : nullptr) ) + { + pObj = dynamic_cast<SbUnoObject*>( pBaseObj ); + } + } + uno::Reference< script::XErrorQuery > xError; + if ( pObj ) + { + xError.set( pObj->getUnoAny(), uno::UNO_QUERY ); + } + if ( xError.is() ) + { + rPar.Get32(0)->PutBool( xError->hasError() ); + } + else + { + rPar.Get32(0)->PutBool( rPar.Get32(1)->IsErr() ); + } + } +} + +void SbRtl_IsNull(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + // #51475 because of Uno-objects return true + // even if the pObj value is NULL + SbxVariableRef pArg = rPar.Get32(1); + bool bNull = rPar.Get32(1)->IsNull(); + if( !bNull && pArg->GetType() == SbxOBJECT ) + { + SbxBase* pObj = pArg->GetObject(); + if( !pObj ) + { + bNull = true; + } + } + rPar.Get32(0)->PutBool( bNull ); + } +} + +void SbRtl_IsNumeric(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + rPar.Get32(0)->PutBool( rPar.Get32(1)->IsNumericRTL() ); + } +} + + +void SbRtl_IsMissing(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + // #57915 Missing is reported by an error + rPar.Get32(0)->PutBool( rPar.Get32(1)->IsErr() ); + } +} + +// Function looks for wildcards, removes them and always returns the pure path +static OUString implSetupWildcard(const OUString& rFileParam, SbiRTLData& rRTLData) +{ + static const char cDelim1 = '/'; + static const char cDelim2 = '\\'; + static const char cWild1 = '*'; + static const char cWild2 = '?'; + + rRTLData.pWildCard.reset(); + rRTLData.sFullNameToBeChecked.clear(); + + OUString aFileParam = rFileParam; + sal_Int32 nLastWild = aFileParam.lastIndexOf( cWild1 ); + if( nLastWild < 0 ) + { + nLastWild = aFileParam.lastIndexOf( cWild2 ); + } + bool bHasWildcards = ( nLastWild >= 0 ); + + + sal_Int32 nLastDelim = aFileParam.lastIndexOf( cDelim1 ); + if( nLastDelim < 0 ) + { + nLastDelim = aFileParam.lastIndexOf( cDelim2 ); + } + if( bHasWildcards ) + { + // Wildcards in path? + if( nLastDelim >= 0 && nLastDelim > nLastWild ) + { + return aFileParam; + } + } + else + { + OUString aPathStr = getFullPath( aFileParam ); + if( nLastDelim != aFileParam.getLength() - 1 ) + { + rRTLData.sFullNameToBeChecked = aPathStr; + } + return aPathStr; + } + + OUString aPureFileName; + if( nLastDelim < 0 ) + { + aPureFileName = aFileParam; + aFileParam.clear(); + } + else + { + aPureFileName = aFileParam.copy( nLastDelim + 1 ); + aFileParam = aFileParam.copy( 0, nLastDelim ); + } + + // Try again to get a valid URL/UNC-path with only the path + OUString aPathStr = getFullPath( aFileParam ); + + // Is there a pure file name left? Otherwise the path is + // invalid anyway because it was not accepted by OSL before + if (aPureFileName != "*") + { + rRTLData.pWildCard = std::make_unique<WildCard>(aPureFileName); + } + return aPathStr; +} + +static bool implCheckWildcard(const OUString& rName, SbiRTLData const& rRTLData) +{ + bool bMatch = true; + + if (rRTLData.pWildCard) + { + bMatch = rRTLData.pWildCard->Matches(rName); + } + return bMatch; +} + + +static bool isRootDir( const OUString& aDirURLStr ) +{ + INetURLObject aDirURLObj( aDirURLStr ); + bool bRoot = false; + + // Check if it's a root directory + sal_Int32 nCount = aDirURLObj.getSegmentCount(); + + // No segment means Unix root directory "file:///" + if( nCount == 0 ) + { + bRoot = true; + } + // Exactly one segment needs further checking, because it + // can be Unix "file:///foo/" -> no root + // or Windows "file:///c:/" -> root + else if( nCount == 1 ) + { + OUString aSeg1 = aDirURLObj.getName( 0, true, + INetURLObject::DecodeMechanism::WithCharset ); + if( aSeg1[1] == ':' ) + { + bRoot = true; + } + } + // More than one segments can never be root + // so bRoot remains false + + return bRoot; +} + +void SbRtl_Dir(StarBASIC *, SbxArray & rPar, bool) +{ + OUString aPath; + + const sal_uInt32 nParCount = rPar.Count32(); + if( nParCount > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbiRTLData& rRTLData = GetSbData()->pInst->GetRTLData(); + + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + if ( nParCount >= 2 ) + { + OUString aFileParam = rPar.Get32(1)->GetOUString(); + + OUString aFileURLStr = implSetupWildcard(aFileParam, rRTLData); + if (!rRTLData.sFullNameToBeChecked.isEmpty()) + { + bool bExists = false; + try { bExists = xSFI->exists( aFileURLStr ); } + catch(const Exception & ) {} + + OUString aNameOnlyStr; + if( bExists ) + { + INetURLObject aFileURL( aFileURLStr ); + aNameOnlyStr = aFileURL.getName( INetURLObject::LAST_SEGMENT, + true, INetURLObject::DecodeMechanism::WithCharset ); + } + rPar.Get32(0)->PutString( aNameOnlyStr ); + return; + } + + try + { + OUString aDirURLStr; + bool bFolder = xSFI->isFolder( aFileURLStr ); + + if( bFolder ) + { + aDirURLStr = aFileURLStr; + } + else + { + rPar.Get32(0)->PutString( "" ); + } + + SbAttributes nFlags = SbAttributes::NONE; + if ( nParCount > 2 ) + { + rRTLData.nDirFlags = nFlags + = static_cast<SbAttributes>(rPar.Get32(2)->GetInteger()); + } + else + { + rRTLData.nDirFlags = SbAttributes::NONE; + } + // Read directory + bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY); + rRTLData.aDirSeq = xSFI->getFolderContents(aDirURLStr, bIncludeFolders); + rRTLData.nCurDirPos = 0; + + // #78651 Add "." and ".." directories for VB compatibility + if( bIncludeFolders ) + { + bool bRoot = isRootDir( aDirURLStr ); + + // If it's no root directory we flag the need for + // the "." and ".." directories by the value -2 + // for the actual position. Later for -2 will be + // returned "." and for -1 ".." + if( !bRoot ) + { + rRTLData.nCurDirPos = -2; + } + } + } + catch(const Exception & ) + { + } + } + + + if (rRTLData.aDirSeq.hasElements()) + { + bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY); + + SbiInstance* pInst = GetSbData()->pInst; + bool bCompatibility = ( pInst && pInst->IsCompatibility() ); + for( ;; ) + { + if (rRTLData.nCurDirPos < 0) + { + if (rRTLData.nCurDirPos == -2) + { + aPath = "."; + } + else if (rRTLData.nCurDirPos == -1) + { + aPath = ".."; + } + rRTLData.nCurDirPos++; + } + else if (rRTLData.nCurDirPos >= rRTLData.aDirSeq.getLength()) + { + rRTLData.aDirSeq.realloc(0); + aPath.clear(); + break; + } + else + { + OUString aFile + = rRTLData.aDirSeq.getConstArray()[rRTLData.nCurDirPos++]; + + if( bCompatibility ) + { + if( !bFolderFlag ) + { + bool bFolder = xSFI->isFolder( aFile ); + if( bFolder ) + { + continue; + } + } + } + else + { + // Only directories + if( bFolderFlag ) + { + bool bFolder = xSFI->isFolder( aFile ); + if( !bFolder ) + { + continue; + } + } + } + + INetURLObject aURL( aFile ); + aPath = aURL.getName( INetURLObject::LAST_SEGMENT, true, + INetURLObject::DecodeMechanism::WithCharset ); + } + + bool bMatch = implCheckWildcard(aPath, rRTLData); + if( !bMatch ) + { + continue; + } + break; + } + } + rPar.Get32(0)->PutString( aPath ); + } + } + else + { + // TODO: OSL + if ( nParCount >= 2 ) + { + OUString aFileParam = rPar.Get32(1)->GetOUString(); + + OUString aDirURL = implSetupWildcard(aFileParam, rRTLData); + + SbAttributes nFlags = SbAttributes::NONE; + if ( nParCount > 2 ) + { + rRTLData.nDirFlags = nFlags + = static_cast<SbAttributes>(rPar.Get32(2)->GetInteger()); + } + else + { + rRTLData.nDirFlags = SbAttributes::NONE; + } + + // Read directory + bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY); + rRTLData.pDir = std::make_unique<Directory>(aDirURL); + FileBase::RC nRet = rRTLData.pDir->open(); + if( nRet != FileBase::E_None ) + { + rRTLData.pDir.reset(); + rPar.Get32(0)->PutString( OUString() ); + return; + } + + // #86950 Add "." and ".." directories for VB compatibility + rRTLData.nCurDirPos = 0; + if( bIncludeFolders ) + { + bool bRoot = isRootDir( aDirURL ); + + // If it's no root directory we flag the need for + // the "." and ".." directories by the value -2 + // for the actual position. Later for -2 will be + // returned "." and for -1 ".." + if( !bRoot ) + { + rRTLData.nCurDirPos = -2; + } + } + + } + + if (rRTLData.pDir) + { + bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY); + for( ;; ) + { + if (rRTLData.nCurDirPos < 0) + { + if (rRTLData.nCurDirPos == -2) + { + aPath = "."; + } + else if (rRTLData.nCurDirPos == -1) + { + aPath = ".."; + } + rRTLData.nCurDirPos++; + } + else + { + DirectoryItem aItem; + FileBase::RC nRet = rRTLData.pDir->getNextItem(aItem); + if( nRet != FileBase::E_None ) + { + rRTLData.pDir.reset(); + aPath.clear(); + break; + } + + // Handle flags + FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName ); + nRet = aItem.getFileStatus( aFileStatus ); + if( nRet != FileBase::E_None ) + { + SAL_WARN("basic", "getFileStatus failed"); + continue; + } + + // Only directories? + if( bFolderFlag ) + { + FileStatus::Type aType = aFileStatus.getFileType(); + bool bFolder = isFolder( aType ); + if( !bFolder ) + { + continue; + } + } + + aPath = aFileStatus.getFileName(); + } + + bool bMatch = implCheckWildcard(aPath, rRTLData); + if( !bMatch ) + { + continue; + } + break; + } + } + rPar.Get32(0)->PutString( aPath ); + } + } +} + + +void SbRtl_GetAttr(StarBASIC * pBasic, SbxArray & rPar, bool bWrite) +{ + (void)pBasic; + (void)bWrite; + + if ( rPar.Count32() == 2 ) + { + sal_Int16 nFlags = 0; + + // In Windows, we want to use Windows API to get the file attributes + // for VBA interoperability. + #if defined(_WIN32) + if( SbiRuntime::isVBAEnabled() ) + { + OUString aPathURL = getFullPath( rPar.Get32(1)->GetOUString() ); + OUString aPath; + FileBase::getSystemPathFromFileURL( aPathURL, aPath ); + DWORD nRealFlags = GetFileAttributesW (o3tl::toW(aPath.getStr())); + if (nRealFlags != 0xffffffff) + { + if (nRealFlags == FILE_ATTRIBUTE_NORMAL) + { + nRealFlags = 0; + } + nFlags = static_cast<sal_Int16>(nRealFlags); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND ); + } + rPar.Get32(0)->PutInteger( nFlags ); + + return; + } + #endif + + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + try + { + OUString aPath = getFullPath( rPar.Get32(1)->GetOUString() ); + bool bExists = false; + try { bExists = xSFI->exists( aPath ); } + catch(const Exception & ) {} + if( !bExists ) + { + StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND ); + return; + } + + bool bReadOnly = xSFI->isReadOnly( aPath ); + bool bHidden = xSFI->isHidden( aPath ); + bool bDirectory = xSFI->isFolder( aPath ); + if( bReadOnly ) + { + nFlags |= sal_uInt16(SbAttributes::READONLY); + } + if( bHidden ) + { + nFlags |= sal_uInt16(SbAttributes::HIDDEN); + } + if( bDirectory ) + { + nFlags |= sal_uInt16(SbAttributes::DIRECTORY); + } + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + } + } + else + { + DirectoryItem aItem; + (void)DirectoryItem::get( getFullPath( rPar.Get32(1)->GetOUString() ), aItem ); + FileStatus aFileStatus( osl_FileStatus_Mask_Attributes | osl_FileStatus_Mask_Type ); + (void)aItem.getFileStatus( aFileStatus ); + sal_uInt64 nAttributes = aFileStatus.getAttributes(); + bool bReadOnly = (nAttributes & osl_File_Attribute_ReadOnly) != 0; + + FileStatus::Type aType = aFileStatus.getFileType(); + bool bDirectory = isFolder( aType ); + if( bReadOnly ) + { + nFlags |= sal_uInt16(SbAttributes::READONLY); + } + if( bDirectory ) + { + nFlags |= sal_uInt16(SbAttributes::DIRECTORY); + } + } + rPar.Get32(0)->PutInteger( nFlags ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + + +void SbRtl_FileDateTime(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUString aPath = rPar.Get32(1)->GetOUString(); + tools::Time aTime( tools::Time::EMPTY ); + Date aDate( Date::EMPTY ); + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + try + { + util::DateTime aUnoDT = xSFI->getDateTimeModified( aPath ); + aTime = tools::Time( aUnoDT ); + aDate = Date( aUnoDT ); + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + } + } + else + { + bool bSuccess = false; + do + { + DirectoryItem aItem; + if (DirectoryItem::get( getFullPath( aPath ), aItem ) != FileBase::E_None) + break; + + FileStatus aFileStatus( osl_FileStatus_Mask_ModifyTime ); + if (aItem.getFileStatus( aFileStatus ) != FileBase::E_None) + break; + + TimeValue aTimeVal = aFileStatus.getModifyTime(); + oslDateTime aDT; + if (!osl_getDateTimeFromTimeValue( &aTimeVal, &aDT )) + // Strictly spoken this is not an i/o error but some other failure. + break; + + aTime = tools::Time( aDT.Hours, aDT.Minutes, aDT.Seconds, aDT.NanoSeconds ); + aDate = Date( aDT.Day, aDT.Month, aDT.Year ); + bSuccess = true; + } + while(false); + + if (!bSuccess) + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + + // An empty date shall not result in a formatted null-date (1899-12-30 + // or 1900-01-01) or even worse -0001-12-03 or some such due to how + // GetDayDiff() treats things. There should be an error set in this + // case anyway because of a missing file or other error above, but... so + // do not even bother to use the number formatter. + OUString aRes; + if (aDate.IsEmpty()) + { + aRes = "0000-00-00 00:00:00"; + } + else + { + double fSerial = static_cast<double>(GetDayDiff( aDate )); + long nSeconds = aTime.GetHour(); + nSeconds *= 3600; + nSeconds += aTime.GetMin() * 60; + nSeconds += aTime.GetSec(); + double nDays = static_cast<double>(nSeconds) / (24.0*3600.0); + fSerial += nDays; + + Color* pCol; + + std::shared_ptr<SvNumberFormatter> pFormatter; + sal_uInt32 nIndex; + if( GetSbData()->pInst ) + { + pFormatter = GetSbData()->pInst->GetNumberFormatter(); + nIndex = GetSbData()->pInst->GetStdDateTimeIdx(); + } + else + { + sal_uInt32 n; + pFormatter = SbiInstance::PrepareNumberFormatter( n, n, nIndex ); + } + + pFormatter->GetOutputString( fSerial, nIndex, aRes, &pCol ); + } + rPar.Get32(0)->PutString( aRes ); + } +} + + +void SbRtl_EOF(StarBASIC *, SbxArray & rPar, bool) +{ + // No changes for UCB + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int16 nChannel = rPar.Get32(1)->GetInteger(); + SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem(); + SbiStream* pSbStrm = pIO->GetStream( nChannel ); + if ( !pSbStrm ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL ); + return; + } + bool beof; + SvStream* pSvStrm = pSbStrm->GetStrm(); + if ( pSbStrm->IsText() ) + { + char cBla; + (*pSvStrm).ReadChar( cBla ); // can we read another character? + beof = pSvStrm->eof(); + if ( !beof ) + { + pSvStrm->SeekRel( -1 ); + } + } + else + { + beof = pSvStrm->eof(); // for binary data! + } + rPar.Get32(0)->PutBool( beof ); + } +} + +void SbRtl_FileAttr(StarBASIC *, SbxArray & rPar, bool) +{ + // No changes for UCB + // #57064 Although this function doesn't operate with DirEntry, it is + // not touched by the adjustment to virtual URLs, as it only works on + // already opened files and the name doesn't matter there. + + if ( rPar.Count32() != 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int16 nChannel = rPar.Get32(1)->GetInteger(); + SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem(); + SbiStream* pSbStrm = pIO->GetStream( nChannel ); + if ( !pSbStrm ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL ); + return; + } + sal_Int16 nRet; + if ( rPar.Get32(2)->GetInteger() == 1 ) + { + nRet = static_cast<sal_Int16>(pSbStrm->GetMode()); + } + else + { + nRet = 0; // System file handle not supported + } + rPar.Get32(0)->PutInteger( nRet ); + } +} +void SbRtl_Loc(StarBASIC *, SbxArray & rPar, bool) +{ + // No changes for UCB + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int16 nChannel = rPar.Get32(1)->GetInteger(); + SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem(); + SbiStream* pSbStrm = pIO->GetStream( nChannel ); + if ( !pSbStrm ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL ); + return; + } + SvStream* pSvStrm = pSbStrm->GetStrm(); + std::size_t nPos; + if( pSbStrm->IsRandom()) + { + short nBlockLen = pSbStrm->GetBlockLen(); + nPos = nBlockLen ? (pSvStrm->Tell() / nBlockLen) : 0; + nPos++; // block positions starting at 1 + } + else if ( pSbStrm->IsText() ) + { + nPos = pSbStrm->GetLine(); + } + else if( pSbStrm->IsBinary() ) + { + nPos = pSvStrm->Tell(); + } + else if ( pSbStrm->IsSeq() ) + { + nPos = ( pSvStrm->Tell()+1 ) / 128; + } + else + { + nPos = pSvStrm->Tell(); + } + rPar.Get32(0)->PutLong( static_cast<sal_Int32>(nPos) ); + } +} + +void SbRtl_Lof(StarBASIC *, SbxArray & rPar, bool) +{ + // No changes for UCB + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int16 nChannel = rPar.Get32(1)->GetInteger(); + SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem(); + SbiStream* pSbStrm = pIO->GetStream( nChannel ); + if ( !pSbStrm ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL ); + return; + } + SvStream* pSvStrm = pSbStrm->GetStrm(); + sal_uInt64 const nLen = pSvStrm->TellEnd(); + rPar.Get32(0)->PutLong( static_cast<sal_Int32>(nLen) ); + } +} + + +void SbRtl_Seek(StarBASIC *, SbxArray & rPar, bool) +{ + // No changes for UCB + int nArgs = static_cast<int>(rPar.Count32()); + if ( nArgs < 2 || nArgs > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + sal_Int16 nChannel = rPar.Get32(1)->GetInteger(); + SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem(); + SbiStream* pSbStrm = pIO->GetStream( nChannel ); + if ( !pSbStrm ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL ); + return; + } + SvStream* pStrm = pSbStrm->GetStrm(); + + if ( nArgs == 2 ) // Seek-Function + { + sal_uInt64 nPos = pStrm->Tell(); + if( pSbStrm->IsRandom() ) + { + nPos = nPos / pSbStrm->GetBlockLen(); + } + nPos++; // Basic counts from 1 + rPar.Get32(0)->PutLong( static_cast<sal_Int32>(nPos) ); + } + else // Seek-Statement + { + sal_Int32 nPos = rPar.Get32(2)->GetLong(); + if ( nPos < 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + nPos--; // Basic counts from 1, SvStreams count from 0 + pSbStrm->SetExpandOnWriteTo( 0 ); + if ( pSbStrm->IsRandom() ) + { + nPos *= pSbStrm->GetBlockLen(); + } + pStrm->Seek( static_cast<sal_uInt64>(nPos) ); + pSbStrm->SetExpandOnWriteTo( nPos ); + } +} + +void SbRtl_Format(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32(); + if ( nArgCount < 2 || nArgCount > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUString aResult; + if( nArgCount == 2 ) + { + rPar.Get32(1)->Format( aResult ); + } + else + { + OUString aFmt( rPar.Get32(2)->GetOUString() ); + rPar.Get32(1)->Format( aResult, &aFmt ); + } + rPar.Get32(0)->PutString( aResult ); + } +} + +// https://msdn.microsoft.com/en-us/vba/language-reference-vba/articles/formatnumber-function +void SbRtl_FormatNumber(StarBASIC*, SbxArray& rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32(); + if (nArgCount < 2 || nArgCount > 6) + { + StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT); + return; + } + + // The UI locale never changes -> we can use static value here + static const LocaleDataWrapper localeData(Application::GetSettings().GetUILanguageTag()); + sal_Int16 nNumDigitsAfterDecimal = -1; + if (nArgCount > 2 && !rPar.Get32(2)->IsEmpty()) + { + nNumDigitsAfterDecimal = rPar.Get32(2)->GetInteger(); + if (nNumDigitsAfterDecimal < -1) + { + StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT); + return; + } + else if (nNumDigitsAfterDecimal > 255) + nNumDigitsAfterDecimal %= 256; + } + if (nNumDigitsAfterDecimal == -1) + nNumDigitsAfterDecimal = LocaleDataWrapper::getNumDigits(); + + bool bIncludeLeadingDigit = LocaleDataWrapper::isNumLeadingZero(); + if (nArgCount > 3 && !rPar.Get32(3)->IsEmpty()) + { + switch (rPar.Get32(3)->GetInteger()) + { + case ooo::vba::VbTriState::vbFalse: + bIncludeLeadingDigit = false; + break; + case ooo::vba::VbTriState::vbTrue: + bIncludeLeadingDigit = true; + break; + case ooo::vba::VbTriState::vbUseDefault: + // do nothing; + break; + default: + StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT); + return; + } + } + + bool bUseParensForNegativeNumbers = false; + if (nArgCount > 4 && !rPar.Get32(4)->IsEmpty()) + { + switch (rPar.Get32(4)->GetInteger()) + { + case ooo::vba::VbTriState::vbFalse: + case ooo::vba::VbTriState::vbUseDefault: + // do nothing + break; + case ooo::vba::VbTriState::vbTrue: + bUseParensForNegativeNumbers = true; + break; + default: + StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT); + return; + } + } + + bool bGroupDigits = false; + if (nArgCount > 5 && !rPar.Get32(5)->IsEmpty()) + { + switch (rPar.Get32(5)->GetInteger()) + { + case ooo::vba::VbTriState::vbFalse: + case ooo::vba::VbTriState::vbUseDefault: + // do nothing + break; + case ooo::vba::VbTriState::vbTrue: + bGroupDigits = true; + break; + default: + StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT); + return; + } + } + + double fVal = rPar.Get32(1)->GetDouble(); + const bool bNegative = fVal < 0; + if (bNegative) + fVal = fabs(fVal); // Always work with non-negatives, to easily handle leading zero + + static const sal_Unicode decSep = localeData.getNumDecimalSep().toChar(); + OUString aResult = rtl::math::doubleToUString( + fVal, rtl_math_StringFormat_F, nNumDigitsAfterDecimal, decSep, + bGroupDigits ? localeData.getDigitGrouping().getConstArray() : nullptr, + localeData.getNumThousandSep().toChar()); + + if (!bIncludeLeadingDigit && aResult.getLength() > 1 && aResult.startsWith("0")) + aResult = aResult.copy(1); + + if (nNumDigitsAfterDecimal > 0) + { + sal_Int32 nActualDigits; + const sal_Int32 nSepPos = aResult.indexOf(decSep); + if (nSepPos == -1) + nActualDigits = 0; + else + nActualDigits = aResult.getLength() - nSepPos - 1; + + // VBA allows up to 255 digits; rtl::math::doubleToUString outputs up to 15 digits + // for ~small numbers, so pad them as appropriate. + if (nActualDigits < nNumDigitsAfterDecimal) + { + OUStringBuffer sBuf; + comphelper::string::padToLength(sBuf, nNumDigitsAfterDecimal - nActualDigits, '0'); + aResult += sBuf; + } + } + + if (bNegative) + { + if (bUseParensForNegativeNumbers) + aResult = "(" + aResult + ")"; + else + aResult = "-" + aResult; + } + + rPar.Get32(0)->PutString(aResult); +} + +namespace { + +// note: BASIC does not use comphelper::random, because +// Randomize(int) must be supported and should not affect non-BASIC random use +struct RandomNumberGenerator +{ + std::mt19937 global_rng; + + RandomNumberGenerator() + { + try + { + std::random_device rd; + // initialises the state of the global random number generator + // should only be called once. + // (note, a few std::variate_generator<> (like normal) have their + // own state which would need a reset as well to guarantee identical + // sequence of numbers, e.g. via myrand.distribution().reset()) + global_rng.seed(rd() ^ time(nullptr)); + } + catch (std::runtime_error& e) + { + SAL_WARN("basic", "Using std::random_device failed: " << e.what()); + global_rng.seed(time(nullptr)); + } + } +}; + +class theRandomNumberGenerator : public rtl::Static<RandomNumberGenerator, theRandomNumberGenerator> {}; + +} + +void SbRtl_Randomize(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() > 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + if( rPar.Count32() == 2 ) + { + int nSeed = static_cast<int>(rPar.Get32(1)->GetInteger()); + theRandomNumberGenerator::get().global_rng.seed(nSeed); + } + // without parameter, no need to do anything - RNG is seeded at first use +} + +void SbRtl_Rnd(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() > 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + std::uniform_real_distribution<double> dist(0.0, 1.0); + double const tmp(dist(theRandomNumberGenerator::get().global_rng)); + rPar.Get32(0)->PutDouble(tmp); + } +} + + +// Syntax: Shell("Path",[ Window-Style,[ "Params", [ bSync = sal_False ]]]) +// WindowStyles (VBA compatible): +// 2 == Minimized +// 3 == Maximized +// 10 == Full-Screen (text mode applications OS/2, WIN95, WNT) +// HACK: The WindowStyle will be passed to +// Application::StartApp in Creator. Format: "xxxx2" + + +void SbRtl_Shell(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32(); + if ( nArgCount < 2 || nArgCount > 5 ) + { + rPar.Get32(0)->PutLong(0); + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + oslProcessOption nOptions = osl_Process_SEARCHPATH | osl_Process_DETACHED; + + OUString aCmdLine = rPar.Get32(1)->GetOUString(); + // attach additional parameters - everything must be parsed anyway + if( nArgCount >= 4 ) + { + OUString tmp = rPar.Get32(3)->GetOUString().trim(); + if (!tmp.isEmpty()) + { + aCmdLine += " " + tmp; + } + } + else if( aCmdLine.isEmpty() ) + { + // avoid special treatment (empty list) + aCmdLine += " "; + } + sal_Int32 nLen = aCmdLine.getLength(); + + // #55735 if there are parameters, they have to be separated + // #72471 also separate the single parameters + std::vector<OUString> aTokenVector; + OUString aToken; + sal_Int32 i = 0; + sal_Unicode c; + while( i < nLen ) + { + for ( ;; ++i ) + { + c = aCmdLine[ i ]; + if ( c != ' ' && c != '\t' ) + { + break; + } + } + + if( c == '\"' || c == '\'' ) + { + sal_Int32 iFoundPos = aCmdLine.indexOf( c, i + 1 ); + + if( iFoundPos < 0 ) + { + aToken = aCmdLine.copy( i); + i = nLen; + } + else + { + aToken = aCmdLine.copy( i + 1, (iFoundPos - i - 1) ); + i = iFoundPos + 1; + } + } + else + { + sal_Int32 iFoundSpacePos = aCmdLine.indexOf( ' ', i ); + sal_Int32 iFoundTabPos = aCmdLine.indexOf( '\t', i ); + sal_Int32 iFoundPos = iFoundSpacePos >= 0 ? iFoundTabPos >= 0 ? std::min( iFoundSpacePos, iFoundTabPos ) : iFoundSpacePos : -1; + + if( iFoundPos < 0 ) + { + aToken = aCmdLine.copy( i ); + i = nLen; + } + else + { + aToken = aCmdLine.copy( i, (iFoundPos - i) ); + i = iFoundPos; + } + } + + // insert into the list + aTokenVector.push_back( aToken ); + } + // #55735 / #72471 end + + sal_Int16 nWinStyle = 0; + if( nArgCount >= 3 ) + { + nWinStyle = rPar.Get32(2)->GetInteger(); + switch( nWinStyle ) + { + case 2: + nOptions |= osl_Process_MINIMIZED; + break; + case 3: + nOptions |= osl_Process_MAXIMIZED; + break; + case 10: + nOptions |= osl_Process_FULLSCREEN; + break; + } + + bool bSync = false; + if( nArgCount >= 5 ) + { + bSync = rPar.Get32(4)->GetBool(); + } + if( bSync ) + { + nOptions |= osl_Process_WAIT; + } + } + + // #72471 work parameter(s) up + std::vector<OUString>::const_iterator iter = aTokenVector.begin(); + OUString aOUStrProgURL = getFullPath( *iter ); + + ++iter; + + sal_uInt16 nParamCount = sal::static_int_cast< sal_uInt16 >(aTokenVector.size() - 1 ); + std::unique_ptr<rtl_uString*[]> pParamList; + if( nParamCount ) + { + pParamList.reset( new rtl_uString*[nParamCount]); + for(int iVector = 0; iter != aTokenVector.end(); ++iVector, ++iter) + { + const OUString& rParamStr = *iter; + pParamList[iVector] = nullptr; + rtl_uString_assign(&(pParamList[iVector]), rParamStr.pData); + } + } + + oslProcess pApp; + bool bSucc = osl_executeProcess( + aOUStrProgURL.pData, + pParamList.get(), + nParamCount, + nOptions, + nullptr, + nullptr, + nullptr, 0, + &pApp ) == osl_Process_E_None; + + // 53521 only free process handle on success + if (bSucc) + { + osl_freeProcessHandle( pApp ); + } + + for(int j = 0; j < nParamCount; ++j) + { + rtl_uString_release(pParamList[j]); + } + + if( !bSucc ) + { + StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND ); + } + else + { + rPar.Get32(0)->PutLong( 0 ); + } + } +} + +void SbRtl_VarType(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxDataType eType = rPar.Get32(1)->GetType(); + rPar.Get32(0)->PutInteger( static_cast<sal_Int16>(eType) ); + } +} + +// Exported function +OUString getBasicTypeName( SbxDataType eType ) +{ + static const char* pTypeNames[] = + { + "Empty", // SbxEMPTY + "Null", // SbxNULL + "Integer", // SbxINTEGER + "Long", // SbxLONG + "Single", // SbxSINGLE + "Double", // SbxDOUBLE + "Currency", // SbxCURRENCY + "Date", // SbxDATE + "String", // SbxSTRING + "Object", // SbxOBJECT + "Error", // SbxERROR + "Boolean", // SbxBOOL + "Variant", // SbxVARIANT + "DataObject", // SbxDATAOBJECT + "Unknown Type", + "Unknown Type", + "Char", // SbxCHAR + "Byte", // SbxBYTE + "UShort", // SbxUSHORT + "ULong", // SbxULONG + "Long64", // SbxLONG64 + "ULong64", // SbxULONG64 + "Int", // SbxINT + "UInt", // SbxUINT + "Void", // SbxVOID + "HResult", // SbxHRESULT + "Pointer", // SbxPOINTER + "DimArray", // SbxDIMARRAY + "CArray", // SbxCARRAY + "Userdef", // SbxUSERDEF + "Lpstr", // SbxLPSTR + "Lpwstr", // SbxLPWSTR + "Unknown Type", // SbxCoreSTRING + "WString", // SbxWSTRING + "WChar", // SbxWCHAR + "Int64", // SbxSALINT64 + "UInt64", // SbxSALUINT64 + "Decimal", // SbxDECIMAL + }; + + size_t nPos = static_cast<size_t>(eType) & 0x0FFF; + const size_t nTypeNameCount = SAL_N_ELEMENTS( pTypeNames ); + if ( nPos >= nTypeNameCount ) + { + nPos = nTypeNameCount - 1; + } + return OUString::createFromAscii(pTypeNames[nPos]); +} + +static OUString getObjectTypeName( SbxVariable* pVar ) +{ + OUString sRet( "Object" ); + if ( pVar ) + { + SbxBase* pBaseObj = pVar->GetObject(); + if( !pBaseObj ) + { + sRet = "Nothing"; + } + else + { + SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pVar ); + if ( !pUnoObj ) + { + pUnoObj = dynamic_cast<SbUnoObject*>( pBaseObj ); + } + if ( pUnoObj ) + { + Any aObj = pUnoObj->getUnoAny(); + // For upstreaming unless we start to build oovbaapi by default + // we need to get detect the vba-ness of the object in some + // other way + // note: Automation objects do not support XServiceInfo + uno::Reference< XServiceInfo > xServInfo( aObj, uno::UNO_QUERY ); + if ( xServInfo.is() ) + { + // is this a VBA object ? + Sequence< OUString > sServices = xServInfo->getSupportedServiceNames(); + if ( sServices.hasElements() ) + { + sRet = sServices[ 0 ]; + } + } + else + { + uno::Reference< bridge::oleautomation::XAutomationObject > xAutoMation( aObj, uno::UNO_QUERY ); + if ( xAutoMation.is() ) + { + uno::Reference< script::XInvocation > xInv( aObj, uno::UNO_QUERY ); + if ( xInv.is() ) + { + try + { + xInv->getValue( "$GetTypeName" ) >>= sRet; + } + catch(const Exception& ) + { + } + } + } + } + sal_Int32 nDot = sRet.lastIndexOf( '.' ); + if ( nDot != -1 && nDot < sRet.getLength() ) + { + sRet = sRet.copy( nDot + 1 ); + } + } + } + } + return sRet; +} + +void SbRtl_TypeName(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxDataType eType = rPar.Get32(1)->GetType(); + bool bIsArray = ( ( eType & SbxARRAY ) != 0 ); + + OUString aRetStr; + if ( SbiRuntime::isVBAEnabled() && eType == SbxOBJECT ) + { + aRetStr = getObjectTypeName( rPar.Get32(1) ); + } + else + { + aRetStr = getBasicTypeName( eType ); + } + if( bIsArray ) + { + aRetStr += "()"; + } + rPar.Get32(0)->PutString( aRetStr ); + } +} + +void SbRtl_Len(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + const OUString& rStr = rPar.Get32(1)->GetOUString(); + rPar.Get32(0)->PutLong( rStr.getLength() ); + } +} + +void SbRtl_DDEInitiate(StarBASIC *, SbxArray & rPar, bool) +{ + int nArgs = static_cast<int>(rPar.Count32()); + if ( nArgs != 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + const OUString& rApp = rPar.Get32(1)->GetOUString(); + const OUString& rTopic = rPar.Get32(2)->GetOUString(); + + SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl(); + size_t nChannel; + ErrCode nDdeErr = pDDE->Initiate( rApp, rTopic, nChannel ); + if( nDdeErr ) + { + StarBASIC::Error( nDdeErr ); + } + else + { + rPar.Get32(0)->PutInteger( static_cast<sal_Int16>(nChannel) ); + } +} + +void SbRtl_DDETerminate(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + int nArgs = static_cast<int>(rPar.Count32()); + if ( nArgs != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + size_t nChannel = rPar.Get32(1)->GetInteger(); + SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl(); + ErrCode nDdeErr = pDDE->Terminate( nChannel ); + if( nDdeErr ) + { + StarBASIC::Error( nDdeErr ); + } +} + +void SbRtl_DDETerminateAll(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + int nArgs = static_cast<int>(rPar.Count32()); + if ( nArgs != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl(); + ErrCode nDdeErr = pDDE->TerminateAll(); + if( nDdeErr ) + { + StarBASIC::Error( nDdeErr ); + } +} + +void SbRtl_DDERequest(StarBASIC *, SbxArray & rPar, bool) +{ + int nArgs = static_cast<int>(rPar.Count32()); + if ( nArgs != 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + size_t nChannel = rPar.Get32(1)->GetInteger(); + const OUString& rItem = rPar.Get32(2)->GetOUString(); + SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl(); + OUString aResult; + ErrCode nDdeErr = pDDE->Request( nChannel, rItem, aResult ); + if( nDdeErr ) + { + StarBASIC::Error( nDdeErr ); + } + else + { + rPar.Get32(0)->PutString( aResult ); + } +} + +void SbRtl_DDEExecute(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + int nArgs = static_cast<int>(rPar.Count32()); + if ( nArgs != 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + size_t nChannel = rPar.Get32(1)->GetInteger(); + const OUString& rCommand = rPar.Get32(2)->GetOUString(); + SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl(); + ErrCode nDdeErr = pDDE->Execute( nChannel, rCommand ); + if( nDdeErr ) + { + StarBASIC::Error( nDdeErr ); + } +} + +void SbRtl_DDEPoke(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + int nArgs = static_cast<int>(rPar.Count32()); + if ( nArgs != 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + size_t nChannel = rPar.Get32(1)->GetInteger(); + const OUString& rItem = rPar.Get32(2)->GetOUString(); + const OUString& rData = rPar.Get32(3)->GetOUString(); + SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl(); + ErrCode nDdeErr = pDDE->Poke( nChannel, rItem, rData ); + if( nDdeErr ) + { + StarBASIC::Error( nDdeErr ); + } +} + + +void SbRtl_FreeFile(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem(); + short nChannel = 1; + while( nChannel < CHANNELS ) + { + SbiStream* pStrm = pIO->GetStream( nChannel ); + if( !pStrm ) + { + rPar.Get32(0)->PutInteger( nChannel ); + return; + } + nChannel++; + } + StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES ); +} + +void SbRtl_LBound(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nParCount = rPar.Count32(); + if ( nParCount != 3 && nParCount != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + SbxBase* pParObj = rPar.Get32(1)->GetObject(); + SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj ); + if( pArr ) + { + sal_Int32 nLower, nUpper; + short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get32(2)->GetInteger()) : 1; + if( !pArr->GetDim32( nDim, nLower, nUpper ) ) + StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE ); + else + rPar.Get32(0)->PutLong( nLower ); + } + else + StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS ); +} + +void SbRtl_UBound(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nParCount = rPar.Count32(); + if ( nParCount != 3 && nParCount != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + SbxBase* pParObj = rPar.Get32(1)->GetObject(); + SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj ); + if( pArr ) + { + sal_Int32 nLower, nUpper; + short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get32(2)->GetInteger()) : 1; + if( !pArr->GetDim32( nDim, nLower, nUpper ) ) + StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE ); + else + rPar.Get32(0)->PutLong( nUpper ); + } + else + StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS ); +} + +void SbRtl_RGB(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + sal_Int32 nRed = rPar.Get32(1)->GetInteger() & 0xFF; + sal_Int32 nGreen = rPar.Get32(2)->GetInteger() & 0xFF; + sal_Int32 nBlue = rPar.Get32(3)->GetInteger() & 0xFF; + sal_Int32 nRGB; + + SbiInstance* pInst = GetSbData()->pInst; + bool bCompatibility = ( pInst && pInst->IsCompatibility() ); + if( bCompatibility ) + { + nRGB = (nBlue << 16) | (nGreen << 8) | nRed; + } + else + { + nRGB = (nRed << 16) | (nGreen << 8) | nBlue; + } + rPar.Get32(0)->PutLong( nRGB ); +} + +void SbRtl_QBColor(StarBASIC *, SbxArray & rPar, bool) +{ + static const sal_Int32 pRGB[] = + { + 0x000000, + 0x800000, + 0x008000, + 0x808000, + 0x000080, + 0x800080, + 0x008080, + 0xC0C0C0, + 0x808080, + 0xFF0000, + 0x00FF00, + 0xFFFF00, + 0x0000FF, + 0xFF00FF, + 0x00FFFF, + 0xFFFFFF, + }; + + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + sal_Int16 nCol = rPar.Get32(1)->GetInteger(); + if( nCol < 0 || nCol > 15 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + sal_Int32 nRGB = pRGB[ nCol ]; + rPar.Get32(0)->PutLong( nRGB ); +} + +// StrConv(string, conversion, LCID) +void SbRtl_StrConv(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32()-1; + if( nArgCount < 2 || nArgCount > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + OUString aOldStr = rPar.Get32(1)->GetOUString(); + sal_Int32 nConversion = rPar.Get32(2)->GetLong(); + + LanguageType nLanguage = LANGUAGE_SYSTEM; + + sal_Int32 nOldLen = aOldStr.getLength(); + if( nOldLen == 0 ) + { + // null string,return + rPar.Get32(0)->PutString(aOldStr); + return; + } + + TransliterationFlags nType = TransliterationFlags::NONE; + if ( (nConversion & 0x03) == 3 ) // vbProperCase + { + const CharClass& rCharClass = GetCharClass(); + aOldStr = rCharClass.titlecase( aOldStr.toAsciiLowerCase(), 0, nOldLen ); + } + else if ( (nConversion & 0x01) == 1 ) // vbUpperCase + { + nType |= TransliterationFlags::LOWERCASE_UPPERCASE; + } + else if ( (nConversion & 0x02) == 2 ) // vbLowerCase + { + nType |= TransliterationFlags::UPPERCASE_LOWERCASE; + } + if ( (nConversion & 0x04) == 4 ) // vbWide + { + nType |= TransliterationFlags::HALFWIDTH_FULLWIDTH; + } + else if ( (nConversion & 0x08) == 8 ) // vbNarrow + { + nType |= TransliterationFlags::FULLWIDTH_HALFWIDTH; + } + if ( (nConversion & 0x10) == 16) // vbKatakana + { + nType |= TransliterationFlags::HIRAGANA_KATAKANA; + } + else if ( (nConversion & 0x20) == 32 ) // vbHiragana + { + nType |= TransliterationFlags::KATAKANA_HIRAGANA; + } + OUString aNewStr( aOldStr ); + if( nType != TransliterationFlags::NONE ) + { + uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext(); + ::utl::TransliterationWrapper aTransliterationWrapper( xContext, nType ); + uno::Sequence<sal_Int32> aOffsets; + aTransliterationWrapper.loadModuleIfNeeded( nLanguage ); + aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets ); + } + + if ( (nConversion & 0x40) == 64 ) // vbUnicode + { + // convert the string to byte string, preserving unicode (2 bytes per character) + sal_Int32 nSize = aNewStr.getLength()*2; + const sal_Unicode* pSrc = aNewStr.getStr(); + std::unique_ptr<char[]> pChar(new char[nSize+1]); + for( sal_Int32 i=0; i < nSize; i++ ) + { + pChar[i] = static_cast< char >( (i%2) ? ((*pSrc) >> 8) & 0xff : (*pSrc) & 0xff ); + if( i%2 ) + { + pSrc++; + } + } + pChar[nSize] = '\0'; + OString aOStr(pChar.get()); + + // there is no concept about default codepage in unix. so it is incorrectly in unix + OUString aOUStr = OStringToOUString(aOStr, osl_getThreadTextEncoding()); + rPar.Get32(0)->PutString( aOUStr ); + return; + } + else if ( (nConversion & 0x80) == 128 ) // vbFromUnicode + { + // there is no concept about default codepage in unix. so it is incorrectly in unix + OString aOStr = OUStringToOString(aNewStr,osl_getThreadTextEncoding()); + const char* pChar = aOStr.getStr(); + sal_Int32 nArraySize = aOStr.getLength(); + SbxDimArray* pArray = new SbxDimArray(SbxBYTE); + bool bIncIndex = (IsBaseIndexOne() && SbiRuntime::isVBAEnabled() ); + if(nArraySize) + { + if( bIncIndex ) + { + pArray->AddDim32( 1, nArraySize ); + } + else + { + pArray->AddDim32( 0, nArraySize-1 ); + } + } + else + { + pArray->unoAddDim32( 0, -1 ); + } + + for( sal_Int32 i=0; i< nArraySize; i++) + { + SbxVariable* pNew = new SbxVariable( SbxBYTE ); + pNew->PutByte(*pChar); + pChar++; + pNew->SetFlag( SbxFlagBits::Write ); + sal_Int32 aIdx[1]; + aIdx[0] = i; + if( bIncIndex ) + { + ++aIdx[0]; + } + pArray->Put32(pNew, aIdx); + } + + SbxVariableRef refVar = rPar.Get32(0); + SbxFlagBits nFlags = refVar->GetFlags(); + refVar->ResetFlag( SbxFlagBits::Fixed ); + refVar->PutObject( pArray ); + refVar->SetFlags( nFlags ); + refVar->SetParameters( nullptr ); + return; + } + rPar.Get32(0)->PutString(aNewStr); +} + + +void SbRtl_Beep(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + Sound::Beep(); +} + +void SbRtl_Load(StarBASIC *, SbxArray & rPar, bool) +{ + if( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + + SbxBase* pObj = rPar.Get32(1)->GetObject(); + if ( !pObj ) + return; + + if (SbUserFormModule* pModule = dynamic_cast<SbUserFormModule*>(pObj)) + { + pModule->Load(); + } + else if (SbxObject* pSbxObj = dynamic_cast<SbxObject*>(pObj)) + { + SbxVariable* pVar = pSbxObj->Find("Load", SbxClassType::Method); + if( pVar ) + { + pVar->GetInteger(); + } + } +} + +void SbRtl_Unload(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + + SbxBase* pObj = rPar.Get32(1)->GetObject(); + if ( !pObj ) + return; + + if (SbUserFormModule* pFormModule = dynamic_cast<SbUserFormModule*>(pObj)) + { + pFormModule->Unload(); + } + else if (SbxObject *pSbxObj = dynamic_cast<SbxObject*>(pObj)) + { + SbxVariable* pVar = pSbxObj->Find("Unload", SbxClassType::Method); + if( pVar ) + { + pVar->GetInteger(); + } + } +} + +void SbRtl_LoadPicture(StarBASIC *, SbxArray & rPar, bool) +{ + if( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + OUString aFileURL = getFullPath( rPar.Get32(1)->GetOUString() ); + std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aFileURL, StreamMode::READ )); + if( pStream ) + { + Bitmap aBmp; + ReadDIB(aBmp, *pStream, true); + Graphic aGraphic(aBmp); + + SbxObjectRef xRef = new SbStdPicture; + static_cast<SbStdPicture*>(xRef.get())->SetGraphic( aGraphic ); + rPar.Get32(0)->PutObject( xRef.get() ); + } +} + +void SbRtl_SavePicture(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if( rPar.Count32() != 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + SbxBase* pObj = rPar.Get32(1)->GetObject(); + if (SbStdPicture *pPicture = dynamic_cast<SbStdPicture*>(pObj)) + { + SvFileStream aOStream( rPar.Get32(2)->GetOUString(), StreamMode::WRITE | StreamMode::TRUNC ); + const Graphic& aGraphic = pPicture->GetGraphic(); + WriteGraphic( aOStream, aGraphic ); + } +} + +void SbRtl_MsgBox(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32(); + if( nArgCount < 2 || nArgCount > 6 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + WinBits nType = 0; // MB_OK + if( nArgCount >= 3 ) + nType = static_cast<WinBits>(rPar.Get32(2)->GetInteger()); + WinBits nStyle = nType; + nStyle &= 15; // delete bits 4-16 + if (nStyle > 5) + nStyle = 0; + + enum BasicResponse + { + Ok = 1, + Cancel = 2, + Abort = 3, + Retry = 4, + Ignore = 5, + Yes = 6, + No = 7 + }; + + OUString aMsg = rPar.Get32(1)->GetOUString(); + OUString aTitle; + if( nArgCount >= 4 ) + { + aTitle = rPar.Get32(3)->GetOUString(); + } + else + { + aTitle = Application::GetDisplayName(); + } + + WinBits nDialogType = nType & (16+32+64); + + SolarMutexGuard aSolarGuard; + vcl::Window* pParentWin = Application::GetDefDialogParent(); + weld::Widget* pParent = pParentWin ? pParentWin->GetFrameWeld() : nullptr; + + VclMessageType eType = VclMessageType::Other; + + switch (nDialogType) + { + case 16: + eType = VclMessageType::Error; + break; + case 32: + eType = VclMessageType::Question; + break; + case 48: + eType = VclMessageType::Warning; + break; + case 64: + eType = VclMessageType::Info; + break; + } + + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, + eType, VclButtonsType::NONE, aMsg)); + + switch (nStyle) + { + case 0: // MB_OK + default: + xBox->add_button(GetStandardText(StandardButtonType::OK), BasicResponse::Ok); + break; + case 1: // MB_OKCANCEL + xBox->add_button(GetStandardText(StandardButtonType::OK), BasicResponse::Ok); + xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel); + + if (nType & 256 || nType & 512) + xBox->set_default_response(BasicResponse::Cancel); + else + xBox->set_default_response(BasicResponse::Ok); + + break; + case 2: // MB_ABORTRETRYIGNORE + xBox->add_button(GetStandardText(StandardButtonType::Abort), BasicResponse::Abort); + xBox->add_button(GetStandardText(StandardButtonType::Retry), BasicResponse::Retry); + xBox->add_button(GetStandardText(StandardButtonType::Ignore), BasicResponse::Ignore); + + if (nType & 256) + xBox->set_default_response(BasicResponse::Retry); + else if (nType & 512) + xBox->set_default_response(BasicResponse::Ignore); + else + xBox->set_default_response(BasicResponse::Cancel); + + break; + case 3: // MB_YESNOCANCEL + xBox->add_button(GetStandardText(StandardButtonType::Yes), BasicResponse::Yes); + xBox->add_button(GetStandardText(StandardButtonType::No), BasicResponse::No); + xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel); + + if (nType & 256 || nType & 512) + xBox->set_default_response(BasicResponse::Cancel); + else + xBox->set_default_response(BasicResponse::Yes); + + break; + case 4: // MB_YESNO + xBox->add_button(GetStandardText(StandardButtonType::Yes), BasicResponse::Yes); + xBox->add_button(GetStandardText(StandardButtonType::No), BasicResponse::No); + + if (nType & 256 || nType & 512) + xBox->set_default_response(BasicResponse::No); + else + xBox->set_default_response(BasicResponse::Yes); + + break; + case 5: // MB_RETRYCANCEL + xBox->add_button(GetStandardText(StandardButtonType::Retry), BasicResponse::Retry); + xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel); + + if (nType & 256 || nType & 512) + xBox->set_default_response(BasicResponse::Cancel); + else + xBox->set_default_response(BasicResponse::Retry); + + break; + } + + xBox->set_title(aTitle); + sal_Int16 nRet = xBox->run(); + rPar.Get32(0)->PutInteger(nRet); +} + +void SbRtl_SetAttr(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if ( rPar.Count32() == 3 ) + { + OUString aStr = rPar.Get32(1)->GetOUString(); + SbAttributes nFlags = static_cast<SbAttributes>( rPar.Get32(2)->GetInteger() ); + + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + try + { + bool bReadOnly = bool(nFlags & SbAttributes::READONLY); + xSFI->setReadOnly( aStr, bReadOnly ); + bool bHidden = bool(nFlags & SbAttributes::HIDDEN); + xSFI->setHidden( aStr, bHidden ); + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + } + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_Reset(StarBASIC *, SbxArray &, bool) +{ + SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem(); + if (pIO) + { + pIO->CloseAll(); + } +} + +void SbRtl_DumpAllObjects(StarBASIC * pBasic, SbxArray & rPar, bool) +{ + const sal_uInt32 nArgCount = rPar.Count32(); + if( nArgCount < 2 || nArgCount > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else if( !pBasic ) + { + StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR ); + } + else + { + SbxObject* p = pBasic; + while( p->GetParent() ) + { + p = p->GetParent(); + } + SvFileStream aStrm( rPar.Get32(1)->GetOUString(), + StreamMode::WRITE | StreamMode::TRUNC ); + p->Dump( aStrm, rPar.Get32(2)->GetBool() ); + aStrm.Close(); + if( aStrm.GetError() != ERRCODE_NONE ) + { + StarBASIC::Error( ERRCODE_BASIC_IO_ERROR ); + } + } +} + + +void SbRtl_FileExists(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() == 2 ) + { + OUString aStr = rPar.Get32(1)->GetOUString(); + bool bExists = false; + + if( hasUno() ) + { + const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess(); + if( xSFI.is() ) + { + try + { + bExists = xSFI->exists( aStr ); + } + catch(const Exception & ) + { + StarBASIC::Error( ERRCODE_IO_GENERAL ); + } + } + } + else + { + DirectoryItem aItem; + FileBase::RC nRet = DirectoryItem::get( getFullPath( aStr ), aItem ); + bExists = (nRet == FileBase::E_None); + } + rPar.Get32(0)->PutBool( bExists ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_Partition(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 5 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + sal_Int32 nNumber = rPar.Get32(1)->GetLong(); + sal_Int32 nStart = rPar.Get32(2)->GetLong(); + sal_Int32 nStop = rPar.Get32(3)->GetLong(); + sal_Int32 nInterval = rPar.Get32(4)->GetLong(); + + if( nStart < 0 || nStop <= nStart || nInterval < 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // the Partition function inserts leading spaces before lowervalue and uppervalue + // so that they both have the same number of characters as the string + // representation of the value (Stop + 1). This ensures that if you use the output + // of the Partition function with several values of Number, the resulting text + // will be handled properly during any subsequent sort operation. + + // calculate the maximum number of characters before lowervalue and uppervalue + OUString aBeforeStart = OUString::number( nStart - 1 ); + OUString aAfterStop = OUString::number( nStop + 1 ); + sal_Int32 nLen1 = aBeforeStart.getLength(); + sal_Int32 nLen2 = aAfterStop.getLength(); + sal_Int32 nLen = nLen1 >= nLen2 ? nLen1:nLen2; + + OUStringBuffer aRetStr( nLen * 2 + 1); + OUString aLowerValue; + OUString aUpperValue; + if( nNumber < nStart ) + { + aUpperValue = aBeforeStart; + } + else if( nNumber > nStop ) + { + aLowerValue = aAfterStop; + } + else + { + sal_Int32 nLowerValue = nNumber; + sal_Int32 nUpperValue = nLowerValue; + if( nInterval > 1 ) + { + nLowerValue = ((( nNumber - nStart ) / nInterval ) * nInterval ) + nStart; + nUpperValue = nLowerValue + nInterval - 1; + } + aLowerValue = OUString::number( nLowerValue ); + aUpperValue = OUString::number( nUpperValue ); + } + + nLen1 = aLowerValue.getLength(); + nLen2 = aUpperValue.getLength(); + + if( nLen > nLen1 ) + { + // appending the leading spaces for the lowervalue + for ( sal_Int32 i= nLen - nLen1; i > 0; --i ) + { + aRetStr.append(" "); + } + } + aRetStr.append( aLowerValue ).append(":"); + if( nLen > nLen2 ) + { + // appending the leading spaces for the uppervalue + for ( sal_Int32 i= nLen - nLen2; i > 0; --i ) + { + aRetStr.append(" "); + } + } + aRetStr.append( aUpperValue ); + rPar.Get32(0)->PutString( aRetStr.makeStringAndClear()); +} + +#endif + +static long GetDayDiff( const Date& rDate ) +{ + Date aRefDate( 1,1,1900 ); + long nDiffDays; + if ( aRefDate > rDate ) + { + nDiffDays = aRefDate - rDate; + nDiffDays *= -1; + } + else + { + nDiffDays = rDate - aRefDate; + } + nDiffDays += 2; // adjustment VisualBasic: 1.Jan.1900 == 2 + return nDiffDays; +} + +sal_Int16 implGetDateYear( double aDate ) +{ + Date aRefDate( 1,1,1900 ); + long nDays = static_cast<long>(aDate); + nDays -= 2; // standardize: 1.1.1900 => 0.0 + aRefDate.AddDays( nDays ); + sal_Int16 nRet = aRefDate.GetYear(); + return nRet; +} + +bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, + bool bUseTwoDigitYear, SbDateCorrection eCorr, double& rdRet ) +{ + // XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and + // 30..99 can not be input as they are 2-digit for 2000..2029 and + // 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always + // true). For VBA years > 9999 are invalid. + // For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99 + // can not be input as they are 2-digit for 1900..1999, years<0 are + // accepted. If bUseTwoDigitYear==false then all years are accepted, but + // year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01). +#if HAVE_FEATURE_SCRIPTING + if ( (nYear < 0 || 9999 < nYear) && SbiRuntime::isVBAEnabled() ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return false; + } + else if ( nYear < 30 && SbiRuntime::isVBAEnabled() ) + { + nYear += 2000; + } + else +#endif + { + if ( 0 <= nYear && nYear < 100 && (bUseTwoDigitYear +#if HAVE_FEATURE_SCRIPTING + || SbiRuntime::isVBAEnabled() +#endif + ) ) + { + nYear += 1900; + } + } + + sal_Int32 nAddMonths = 0; + sal_Int32 nAddDays = 0; + // Always sanitize values to set date and to use for validity detection. + if (nMonth < 1 || 12 < nMonth) + { + sal_Int16 nM = ((nMonth < 1) ? (12 + (nMonth % 12)) : (nMonth % 12)); + nAddMonths = nMonth - nM; + nMonth = nM; + } + // Day 0 would already be normalized during Date::Normalize(), include + // it in negative days, also to detect non-validity. The actual day of + // month is 1+(nDay-1) + if (nDay < 1) + { + nAddDays = nDay - 1; + nDay = 1; + } + else if (nDay > 31) + { + nAddDays = nDay - 31; + nDay = 31; + } + + Date aCurDate( nDay, nMonth, nYear ); + + /* TODO: we could enable the same rollover mechanism for StarBASIC to be + * compatible with VBA (just with our wider supported date range), then + * documentation would need to be adapted. As is, the DateSerial() runtime + * function works as dumb as documented... (except that the resulting date + * is checked for validity now and not just day<=31 and month<=12). + * If change wanted then simply remove overriding RollOver here and adapt + * documentation.*/ +#if HAVE_FEATURE_SCRIPTING + if (eCorr == SbDateCorrection::RollOver && !SbiRuntime::isVBAEnabled()) + eCorr = SbDateCorrection::None; +#endif + + if (nYear == 0 || (eCorr == SbDateCorrection::None && (nAddMonths || nAddDays || !aCurDate.IsValidDate()))) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); +#endif + return false; + } + + if (eCorr != SbDateCorrection::None) + { + aCurDate.Normalize(); + if (nAddMonths) + aCurDate.AddMonths( nAddMonths); + if (nAddDays) + aCurDate.AddDays( nAddDays); + if (eCorr == SbDateCorrection::TruncateToMonth && aCurDate.GetMonth() != nMonth) + { + if (aCurDate.GetYear() == SAL_MAX_INT16 && nMonth == 12) + { + // Roll over and back not possible, hard max. + aCurDate.SetMonth(12); + aCurDate.SetDay(31); + } + else + { + aCurDate.SetMonth(nMonth); + aCurDate.SetDay(1); + aCurDate.AddMonths(1); + aCurDate.AddDays(-1); + } + } + } + + long nDiffDays = GetDayDiff( aCurDate ); + rdRet = static_cast<double>(nDiffDays); + return true; +} + +double implTimeSerial( sal_Int16 nHours, sal_Int16 nMinutes, sal_Int16 nSeconds ) +{ + return + static_cast<double>( nHours * ::tools::Time::secondPerHour + + nMinutes * ::tools::Time::secondPerMinute + + nSeconds) + / + static_cast<double>( ::tools::Time::secondPerDay ); +} + +bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, + sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond, + double& rdRet ) +{ + double dDate; + if(!implDateSerial(nYear, nMonth, nDay, false/*bUseTwoDigitYear*/, SbDateCorrection::None, dDate)) + return false; + rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond); + return true; +} + +sal_Int16 implGetMinute( double dDate ) +{ + double nFrac = dDate - floor( dDate ); + nFrac *= 86400.0; + sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5); + sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds % 3600); + sal_Int16 nMin = nTemp / 60; + return nMin; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/methods1.cxx b/basic/source/runtime/methods1.cxx new file mode 100644 index 000000000..75f273042 --- /dev/null +++ b/basic/source/runtime/methods1.cxx @@ -0,0 +1,3066 @@ +/* -*- 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 <config_features.h> + +#include <sal/config.h> +#include <config_version.h> + +#include <cstddef> + +#include <stdlib.h> +#include <vcl/svapp.hxx> +#include <vcl/mapmod.hxx> +#include <vcl/outdev.hxx> +#include <vcl/timer.hxx> +#include <vcl/settings.hxx> +#include <basic/sbxvar.hxx> +#include <basic/sbx.hxx> +#include <svl/zforlist.hxx> +#include <tools/urlobj.hxx> +#include <tools/fract.hxx> +#include <o3tl/temporary.hxx> +#include <osl/file.hxx> +#include <sbobjmod.hxx> +#include <basic/sbuno.hxx> + +#include <date.hxx> +#include <sbintern.hxx> +#include <runtime.hxx> +#include <rtlproto.hxx> +#include "dllmgr.hxx" +#include <iosys.hxx> +#include <sbunoobj.hxx> +#include <propacc.hxx> +#include <sal/log.hxx> +#include <eventatt.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/i18n/LocaleCalendar2.hpp> +#include <com/sun/star/sheet/XFunctionAccess.hpp> +#include <memory> + +using namespace comphelper; +using namespace com::sun::star::i18n; +using namespace com::sun::star::lang; +using namespace com::sun::star::sheet; +using namespace com::sun::star::uno; + +static Reference< XCalendar4 > const & getLocaleCalendar() +{ + static Reference< XCalendar4 > xCalendar = LocaleCalendar2::create(getProcessComponentContext()); + static css::lang::Locale aLastLocale; + static bool bNeedsInit = true; + + css::lang::Locale aLocale = Application::GetSettings().GetLanguageTag().getLocale(); + bool bNeedsReload = false; + if( bNeedsInit ) + { + bNeedsInit = false; + bNeedsReload = true; + } + else if( aLocale.Language != aLastLocale.Language || + aLocale.Country != aLastLocale.Country || + aLocale.Variant != aLastLocale.Variant ) + { + bNeedsReload = true; + } + if( bNeedsReload ) + { + aLastLocale = aLocale; + xCalendar->loadDefaultCalendar( aLocale ); + } + return xCalendar; +} + +#if HAVE_FEATURE_SCRIPTING + +void SbRtl_CallByName(StarBASIC *, SbxArray & rPar, bool) +{ + const sal_Int16 vbGet = 2; + const sal_Int16 vbLet = 4; + const sal_Int16 vbMethod = 1; + const sal_Int16 vbSet = 8; + + // At least 3 parameter needed plus function itself -> 4 + sal_uInt32 nParCount = rPar.Count32(); + if ( nParCount < 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // 1. parameter is object + SbxBase* pObjVar = rPar.Get32(1)->GetObject(); + SbxObject* pObj = nullptr; + if( pObjVar ) + pObj = dynamic_cast<SbxObject*>( pObjVar ); + if( !pObj && dynamic_cast<const SbxVariable*>( pObjVar) ) + { + SbxBase* pObjVarObj = static_cast<SbxVariable*>(pObjVar)->GetObject(); + pObj = dynamic_cast<SbxObject*>( pObjVarObj ); + } + if( !pObj ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_PARAMETER ); + return; + } + + // 2. parameter is ProcedureName + OUString aNameStr = rPar.Get32(2)->GetOUString(); + + // 3. parameter is CallType + sal_Int16 nCallType = rPar.Get32(3)->GetInteger(); + + //SbxObject* pFindObj = NULL; + SbxVariable* pFindVar = pObj->Find( aNameStr, SbxClassType::DontCare ); + if( pFindVar == nullptr ) + { + StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED ); + return; + } + + switch( nCallType ) + { + case vbGet: + { + SbxValues aVals; + aVals.eType = SbxVARIANT; + pFindVar->Get( aVals ); + + SbxVariableRef refVar = rPar.Get32(0); + refVar->Put( aVals ); + } + break; + case vbLet: + case vbSet: + { + if ( nParCount != 5 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + SbxVariableRef pValVar = rPar.Get32(4); + if( nCallType == vbLet ) + { + SbxValues aVals; + aVals.eType = SbxVARIANT; + pValVar->Get( aVals ); + pFindVar->Put( aVals ); + } + else + { + SbxVariableRef rFindVar = pFindVar; + SbiInstance* pInst = GetSbData()->pInst; + SbiRuntime* pRT = pInst ? pInst->pRun : nullptr; + if( pRT != nullptr ) + { + pRT->StepSET_Impl( pValVar, rFindVar ); + } + } + } + break; + case vbMethod: + { + SbMethod* pMeth = dynamic_cast<SbMethod*>( pFindVar ); + if( pMeth == nullptr ) + { + StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED ); + return; + } + + // Setup parameters + SbxArrayRef xArray; + sal_uInt32 nMethParamCount = nParCount - 4; + if( nMethParamCount > 0 ) + { + xArray = new SbxArray; + for( sal_uInt32 i = 0 ; i < nMethParamCount ; i++ ) + { + SbxVariable* pPar = rPar.Get32( i + 4 ); + xArray->Put32( pPar, i + 1 ); + } + } + + // Call method + SbxVariableRef refVar = rPar.Get32(0); + if( xArray.is() ) + pMeth->SetParameters( xArray.get() ); + pMeth->Call( refVar.get() ); + pMeth->SetParameters( nullptr ); + } + break; + default: + StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED ); + } +} + +void SbRtl_CBool(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + bool bVal = false; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + bVal = pSbxVariable->GetBool(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutBool(bVal); +} + +void SbRtl_CByte(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + sal_uInt8 nByte = 0; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + nByte = pSbxVariable->GetByte(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutByte(nByte); +} + +void SbRtl_CCur(StarBASIC *, SbxArray & rPar, bool) +{ + sal_Int64 nCur = 0; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + nCur = pSbxVariable->GetCurrency(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutCurrency( nCur ); +} + +void SbRtl_CDec(StarBASIC * pBasic, SbxArray & rPar, bool bWrite) +{ + (void)pBasic; + (void)bWrite; + +#ifdef _WIN32 + SbxDecimal* pDec = nullptr; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + pDec = pSbxVariable->GetDecimal(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutDecimal( pDec ); +#else + rPar.Get32(0)->PutEmpty(); + StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED); +#endif +} + +void SbRtl_CDate(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + double nVal = 0.0; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + nVal = pSbxVariable->GetDate(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutDate(nVal); +} + +void SbRtl_CDbl(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + double nVal = 0.0; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + if( pSbxVariable->GetType() == SbxSTRING ) + { + // #41690 + OUString aScanStr = pSbxVariable->GetOUString(); + ErrCode Error = SbxValue::ScanNumIntnl( aScanStr, nVal ); + if( Error != ERRCODE_NONE ) + { + StarBASIC::Error( Error ); + } + } + else + { + nVal = pSbxVariable->GetDouble(); + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + + rPar.Get32(0)->PutDouble(nVal); +} + +void SbRtl_CInt(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + sal_Int16 nVal = 0; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + nVal = pSbxVariable->GetInteger(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutInteger(nVal); +} + +void SbRtl_CLng(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + sal_Int32 nVal = 0; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + nVal = pSbxVariable->GetLong(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutLong(nVal); +} + +void SbRtl_CSng(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + float nVal = float(0.0); + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + if( pSbxVariable->GetType() == SbxSTRING ) + { + // #41690 + double dVal = 0.0; + OUString aScanStr = pSbxVariable->GetOUString(); + ErrCode Error = SbxValue::ScanNumIntnl( aScanStr, dVal, /*bSingle=*/true ); + if( SbxBase::GetError() == ERRCODE_NONE && Error != ERRCODE_NONE ) + { + StarBASIC::Error( Error ); + } + nVal = static_cast<float>(dVal); + } + else + { + nVal = pSbxVariable->GetSingle(); + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutSingle(nVal); +} + +void SbRtl_CStr(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + OUString aString; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + aString = pSbxVariable->GetOUString(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutString(aString); +} + +void SbRtl_CVar(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + SbxValues aVals( SbxVARIANT ); + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + pSbxVariable->Get( aVals ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->Put( aVals ); +} + +void SbRtl_CVErr(StarBASIC *, SbxArray & rPar, bool) +{ + sal_Int16 nErrCode = 0; + if ( rPar.Count32() == 2 ) + { + SbxVariable *pSbxVariable = rPar.Get32(1); + nErrCode = pSbxVariable->GetInteger(); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + rPar.Get32(0)->PutErr( nErrCode ); +} + +void SbRtl_Iif(StarBASIC *, SbxArray & rPar, bool) // JSM +{ + if ( rPar.Count32() == 4 ) + { + if (rPar.Get32(1)->GetBool()) + { + *rPar.Get32(0) = *rPar.Get32(2); + } + else + { + *rPar.Get32(0) = *rPar.Get32(3); + } + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_GetSystemType(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + // Removed for SRC595 + rPar.Get32(0)->PutInteger( -1 ); + } +} + +void SbRtl_GetGUIType(StarBASIC * pBasic, SbxArray & rPar, bool bWrite) +{ + (void)pBasic; + (void)bWrite; + + if ( rPar.Count32() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + // 17.7.2000 Make simple solution for testtool / fat office +#if defined(_WIN32) + rPar.Get32(0)->PutInteger( 1 ); +#elif defined(UNX) + rPar.Get32(0)->PutInteger( 4 ); +#else + rPar.Get32(0)->PutInteger( -1 ); +#endif + } +} + +void SbRtl_Red(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int32 nRGB = rPar.Get32(1)->GetLong(); + nRGB &= 0x00FF0000; + nRGB >>= 16; + rPar.Get32(0)->PutInteger( static_cast<sal_Int16>(nRGB) ); + } +} + +void SbRtl_Green(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int32 nRGB = rPar.Get32(1)->GetLong(); + nRGB &= 0x0000FF00; + nRGB >>= 8; + rPar.Get32(0)->PutInteger( static_cast<sal_Int16>(nRGB) ); + } +} + +void SbRtl_Blue(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + sal_Int32 nRGB = rPar.Get32(1)->GetLong(); + nRGB &= 0x000000FF; + rPar.Get32(0)->PutInteger( static_cast<sal_Int16>(nRGB) ); + } +} + + +void SbRtl_Switch(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nCount = rPar.Count32(); + if( !(nCount & 0x0001 )) + { + // number of arguments must be odd + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + sal_uInt32 nCurExpr = 1; + while( nCurExpr < (nCount-1) ) + { + if( rPar.Get32( nCurExpr )->GetBool()) + { + (*rPar.Get32(0)) = *(rPar.Get32(nCurExpr+1)); + return; + } + nCurExpr += 2; + } + rPar.Get32(0)->PutNull(); +} + +//i#64882# Common wait impl for existing Wait and new WaitUntil +// rtl functions +void Wait_Impl( bool bDurationBased, SbxArray& rPar ) +{ + if( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + long nWait = 0; + if ( bDurationBased ) + { + double dWait = rPar.Get32(1)->GetDouble(); + double dNow = Now_Impl(); + double dSecs = ( dWait - dNow ) * 24.0 * 3600.0; + nWait = static_cast<long>( dSecs * 1000 ); // wait in thousands of sec + } + else + { + nWait = rPar.Get32(1)->GetLong(); + } + if( nWait < 0 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + Timer aTimer; + aTimer.SetTimeout( nWait ); + aTimer.Start(); + while ( aTimer.IsActive() ) + { + Application::Yield(); + } +} + +//i#64882# +void SbRtl_Wait(StarBASIC *, SbxArray & rPar, bool) +{ + Wait_Impl( false, rPar ); +} + +//i#64882# add new WaitUntil ( for application.wait ) +// share wait_impl with 'normal' oobasic wait +void SbRtl_WaitUntil(StarBASIC *, SbxArray & rPar, bool) +{ + Wait_Impl( true, rPar ); +} + +void SbRtl_DoEvents(StarBASIC *, SbxArray & rPar, bool) +{ +// don't understand what upstream are up to +// we already process application events etc. in between +// basic runtime pcode ( on a timed basis ) + // always return 0 + rPar.Get32(0)->PutInteger( 0 ); + Application::Reschedule( true ); +} + +void SbRtl_GetGUIVersion(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + // Removed for SRC595 + rPar.Get32(0)->PutLong( -1 ); + } +} + +void SbRtl_Choose(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + sal_Int16 nIndex = rPar.Get32(1)->GetInteger(); + sal_uInt32 nCount = rPar.Count32(); + nCount--; + if( nCount == 1 || nIndex > sal::static_int_cast<sal_Int16>(nCount-1) || nIndex < 1 ) + { + rPar.Get32(0)->PutNull(); + return; + } + (*rPar.Get32(0)) = *(rPar.Get32(nIndex+1)); +} + + +void SbRtl_Trim(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + OUString aStr(comphelper::string::strip(rPar.Get32(1)->GetOUString(), ' ')); + rPar.Get32(0)->PutString(aStr); + } +} + +void SbRtl_GetSolarVersion(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutLong( LIBO_VERSION_MAJOR * 10000 + LIBO_VERSION_MINOR * 100 + LIBO_VERSION_MICRO * 1); +} + +void SbRtl_TwipsPerPixelX(StarBASIC *, SbxArray & rPar, bool) +{ + sal_Int32 nResult = 0; + Size aSize( 100,0 ); + MapMode aMap( MapUnit::MapTwip ); + OutputDevice* pDevice = Application::GetDefaultDevice(); + if( pDevice ) + { + aSize = pDevice->PixelToLogic( aSize, aMap ); + nResult = aSize.Width() / 100; + } + rPar.Get32(0)->PutLong( nResult ); +} + +void SbRtl_TwipsPerPixelY(StarBASIC *, SbxArray & rPar, bool) +{ + sal_Int32 nResult = 0; + Size aSize( 0,100 ); + MapMode aMap( MapUnit::MapTwip ); + OutputDevice* pDevice = Application::GetDefaultDevice(); + if( pDevice ) + { + aSize = pDevice->PixelToLogic( aSize, aMap ); + nResult = aSize.Height() / 100; + } + rPar.Get32(0)->PutLong( nResult ); +} + + +void SbRtl_FreeLibrary(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + GetSbData()->pInst->GetDllMgr()->FreeDll( rPar.Get32(1)->GetOUString() ); +} +bool IsBaseIndexOne() +{ + bool bResult = false; + if ( GetSbData()->pInst && GetSbData()->pInst->pRun ) + { + sal_uInt16 res = GetSbData()->pInst->pRun->GetBase(); + if ( res ) + { + bResult = true; + } + } + return bResult; +} + +void SbRtl_Array(StarBASIC *, SbxArray & rPar, bool) +{ + SbxDimArray* pArray = new SbxDimArray( SbxVARIANT ); + sal_uInt32 nArraySize = rPar.Count32() - 1; + + // ignore Option Base so far (unfortunately only known by the compiler) + bool bIncIndex = (IsBaseIndexOne() && SbiRuntime::isVBAEnabled() ); + if( nArraySize ) + { + if ( bIncIndex ) + { + pArray->AddDim32( 1, sal::static_int_cast<sal_Int32>(nArraySize) ); + } + else + { + pArray->AddDim32( 0, sal::static_int_cast<sal_Int32>(nArraySize) - 1 ); + } + } + else + { + pArray->unoAddDim32( 0, -1 ); + } + + // insert parameters into the array + for( sal_uInt32 i = 0 ; i < nArraySize ; i++ ) + { + SbxVariable* pVar = rPar.Get32(i+1); + SbxVariable* pNew = new SbxEnsureParentVariable(*pVar); + pNew->SetFlag( SbxFlagBits::Write ); + sal_Int32 aIdx[1]; + aIdx[0] = static_cast<sal_Int32>(i); + if ( bIncIndex ) + { + ++aIdx[0]; + } + pArray->Put32(pNew, aIdx); + } + + // return array + SbxVariableRef refVar = rPar.Get32(0); + SbxFlagBits nFlags = refVar->GetFlags(); + refVar->ResetFlag( SbxFlagBits::Fixed ); + refVar->PutObject( pArray ); + refVar->SetFlags( nFlags ); + refVar->SetParameters( nullptr ); +} + + +// Featurewish #57868 +// The function returns a variant-array; if there are no parameters passed, +// an empty array is created (according to dim a(); equal to a sequence of +// the length 0 in Uno). +// If there are parameters passed, there's a dimension created for each of +// them; DimArray( 2, 2, 4 ) is equal to DIM a( 2, 2, 4 ) +// the array is always of the type variant +void SbRtl_DimArray(StarBASIC *, SbxArray & rPar, bool) +{ + SbxDimArray * pArray = new SbxDimArray( SbxVARIANT ); + sal_uInt32 nArrayDims = rPar.Count32() - 1; + if( nArrayDims > 0 ) + { + for( sal_uInt32 i = 0; i < nArrayDims ; i++ ) + { + sal_Int32 ub = rPar.Get32(i+1)->GetLong(); + if( ub < 0 ) + { + StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE ); + ub = 0; + } + pArray->AddDim32( 0, ub ); + } + } + else + { + pArray->unoAddDim32( 0, -1 ); + } + SbxVariableRef refVar = rPar.Get32(0); + SbxFlagBits nFlags = refVar->GetFlags(); + refVar->ResetFlag( SbxFlagBits::Fixed ); + refVar->PutObject( pArray ); + refVar->SetFlags( nFlags ); + refVar->SetParameters( nullptr ); +} + +/* + * FindObject and FindPropertyObject make it possible to + * address objects and properties of the type Object with + * their name as string-parameters at the runtime. + * + * Example: + * MyObj.Prop1.Bla = 5 + * + * is equal to: + * dim ObjVar as Object + * dim ObjProp as Object + * ObjName$ = "MyObj" + * ObjVar = FindObject( ObjName$ ) + * PropName$ = "Prop1" + * ObjProp = FindPropertyObject( ObjVar, PropName$ ) + * ObjProp.Bla = 5 + * + * The names can be created dynamically at the runtime + * so that e. g. via controls "TextEdit1" to "TextEdit5" + * can be iterated in a dialog in a loop. + */ + + +// 1st parameter = the object's name as string +void SbRtl_FindObject(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + OUString aNameStr = rPar.Get32(1)->GetOUString(); + + SbxBase* pFind = StarBASIC::FindSBXInCurrentScope( aNameStr ); + SbxObject* pFindObj = nullptr; + if( pFind ) + { + pFindObj = dynamic_cast<SbxObject*>( pFind ); + } + SbxVariableRef refVar = rPar.Get32(0); + refVar->PutObject( pFindObj ); +} + +// address object-property in an object +// 1st parameter = object +// 2nd parameter = the property's name as string +void SbRtl_FindPropertyObject(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() < 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + SbxBase* pObjVar = rPar.Get32(1)->GetObject(); + SbxObject* pObj = nullptr; + if( pObjVar ) + { + pObj = dynamic_cast<SbxObject*>( pObjVar ); + } + if( !pObj && dynamic_cast<const SbxVariable*>( pObjVar) ) + { + SbxBase* pObjVarObj = static_cast<SbxVariable*>(pObjVar)->GetObject(); + pObj = dynamic_cast<SbxObject*>( pObjVarObj ); + } + + OUString aNameStr = rPar.Get32(2)->GetOUString(); + + SbxObject* pFindObj = nullptr; + if( pObj ) + { + SbxVariable* pFindVar = pObj->Find( aNameStr, SbxClassType::Object ); + pFindObj = dynamic_cast<SbxObject*>( pFindVar ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_PARAMETER ); + } + + SbxVariableRef refVar = rPar.Get32(0); + refVar->PutObject( pFindObj ); +} + + +static bool lcl_WriteSbxVariable( const SbxVariable& rVar, SvStream* pStrm, + bool bBinary, short nBlockLen, bool bIsArray ) +{ + sal_uInt64 const nFPos = pStrm->Tell(); + + bool bIsVariant = !rVar.IsFixed(); + SbxDataType eType = rVar.GetType(); + + switch( eType ) + { + case SbxBOOL: + case SbxCHAR: + case SbxBYTE: + if( bIsVariant ) + { + pStrm->WriteUInt16( SbxBYTE ); // VarType Id + } + pStrm->WriteUChar( rVar.GetByte() ); + break; + + case SbxEMPTY: + case SbxNULL: + case SbxVOID: + case SbxINTEGER: + case SbxUSHORT: + case SbxINT: + case SbxUINT: + if( bIsVariant ) + { + pStrm->WriteUInt16( SbxINTEGER ); // VarType Id + } + pStrm->WriteInt16( rVar.GetInteger() ); + break; + + case SbxLONG: + case SbxULONG: + if( bIsVariant ) + { + pStrm->WriteUInt16( SbxLONG ); // VarType Id + } + pStrm->WriteInt32( rVar.GetLong() ); + break; + case SbxSALINT64: + case SbxSALUINT64: + if( bIsVariant ) + { + pStrm->WriteUInt16( SbxSALINT64 ); // VarType Id + } + pStrm->WriteUInt64( rVar.GetInt64() ); + break; + case SbxSINGLE: + if( bIsVariant ) + { + pStrm->WriteUInt16( eType ); // VarType Id + } + pStrm->WriteFloat( rVar.GetSingle() ); + break; + + case SbxDOUBLE: + case SbxCURRENCY: + case SbxDATE: + if( bIsVariant ) + { + pStrm->WriteUInt16( eType ); // VarType Id + } + pStrm->WriteDouble( rVar.GetDouble() ); + break; + + case SbxSTRING: + case SbxLPSTR: + { + const OUString& rStr = rVar.GetOUString(); + if( !bBinary || bIsArray ) + { + if( bIsVariant ) + { + pStrm->WriteUInt16( SbxSTRING ); + } + pStrm->WriteUniOrByteString( rStr, osl_getThreadTextEncoding() ); + } + else + { + // without any length information! without end-identifier! + // What does that mean for Unicode?! Choosing conversion to ByteString... + OString aByteStr(OUStringToOString(rStr, osl_getThreadTextEncoding())); + pStrm->WriteOString( aByteStr ); + } + } + break; + + default: + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return false; + } + + if( nBlockLen ) + { + pStrm->Seek( nFPos + nBlockLen ); + } + return pStrm->GetErrorCode() == ERRCODE_NONE; +} + +static bool lcl_ReadSbxVariable( SbxVariable& rVar, SvStream* pStrm, + bool bBinary, short nBlockLen ) +{ + double aDouble; + + sal_uInt64 const nFPos = pStrm->Tell(); + + bool bIsVariant = !rVar.IsFixed(); + SbxDataType eVarType = rVar.GetType(); + + SbxDataType eSrcType = eVarType; + if( bIsVariant ) + { + sal_uInt16 nTemp; + pStrm->ReadUInt16( nTemp ); + eSrcType = static_cast<SbxDataType>(nTemp); + } + + switch( eSrcType ) + { + case SbxBOOL: + case SbxCHAR: + case SbxBYTE: + { + sal_uInt8 aByte; + pStrm->ReadUChar( aByte ); + + if( bBinary && SbiRuntime::isVBAEnabled() && aByte == 1 && pStrm->eof() ) + { + aByte = 0; + } + rVar.PutByte( aByte ); + } + break; + + case SbxEMPTY: + case SbxNULL: + case SbxVOID: + case SbxINTEGER: + case SbxUSHORT: + case SbxINT: + case SbxUINT: + { + sal_Int16 aInt; + pStrm->ReadInt16( aInt ); + rVar.PutInteger( aInt ); + } + break; + + case SbxLONG: + case SbxULONG: + { + sal_Int32 aInt; + pStrm->ReadInt32( aInt ); + rVar.PutLong( aInt ); + } + break; + case SbxSALINT64: + case SbxSALUINT64: + { + sal_uInt32 aInt; + pStrm->ReadUInt32( aInt ); + rVar.PutInt64( static_cast<sal_Int64>(aInt) ); + } + break; + case SbxSINGLE: + { + float nS; + pStrm->ReadFloat( nS ); + rVar.PutSingle( nS ); + } + break; + + case SbxDOUBLE: + case SbxCURRENCY: + { + pStrm->ReadDouble( aDouble ); + rVar.PutDouble( aDouble ); + } + break; + + case SbxDATE: + { + pStrm->ReadDouble( aDouble ); + rVar.PutDate( aDouble ); + } + break; + + case SbxSTRING: + case SbxLPSTR: + { + OUString aStr = pStrm->ReadUniOrByteString(osl_getThreadTextEncoding()); + rVar.PutString( aStr ); + } + break; + + default: + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return false; + } + + if( nBlockLen ) + { + pStrm->Seek( nFPos + nBlockLen ); + } + return pStrm->GetErrorCode() == ERRCODE_NONE; +} + + +// nCurDim = 1...n +static bool lcl_WriteReadSbxArray( SbxDimArray& rArr, SvStream* pStrm, + bool bBinary, sal_Int32 nCurDim, sal_Int32* pOtherDims, bool bWrite ) +{ + SAL_WARN_IF( nCurDim <= 0,"basic", "Bad Dim"); + sal_Int32 nLower, nUpper; + if( !rArr.GetDim32( nCurDim, nLower, nUpper ) ) + return false; + for(sal_Int32 nCur = nLower; nCur <= nUpper; nCur++ ) + { + pOtherDims[ nCurDim-1 ] = nCur; + if( nCurDim != 1 ) + lcl_WriteReadSbxArray(rArr, pStrm, bBinary, nCurDim-1, pOtherDims, bWrite); + else + { + SbxVariable* pVar = rArr.Get32( pOtherDims ); + bool bRet; + if( bWrite ) + bRet = lcl_WriteSbxVariable(*pVar, pStrm, bBinary, 0, true ); + else + bRet = lcl_ReadSbxVariable(*pVar, pStrm, bBinary, 0 ); + if( !bRet ) + return false; + } + } + return true; +} + +static void PutGet( SbxArray& rPar, bool bPut ) +{ + if ( rPar.Count32() != 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + sal_Int16 nFileNo = rPar.Get32(1)->GetInteger(); + SbxVariable* pVar2 = rPar.Get32(2); + SbxDataType eType2 = pVar2->GetType(); + bool bHasRecordNo = (eType2 != SbxEMPTY && eType2 != SbxERROR); + long nRecordNo = pVar2->GetLong(); + if ( nFileNo < 1 || ( bHasRecordNo && nRecordNo < 1 ) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + nRecordNo--; + SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem(); + SbiStream* pSbStrm = pIO->GetStream( nFileNo ); + + if ( !pSbStrm || !(pSbStrm->GetMode() & (SbiStreamFlags::Binary | SbiStreamFlags::Random)) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL ); + return; + } + + SvStream* pStrm = pSbStrm->GetStrm(); + bool bRandom = pSbStrm->IsRandom(); + short nBlockLen = bRandom ? pSbStrm->GetBlockLen() : 0; + + if( bPut ) + { + pSbStrm->ExpandFile(); + } + + if( bHasRecordNo ) + { + sal_uInt64 const nFilePos = bRandom + ? static_cast<sal_uInt64>(nBlockLen * nRecordNo) + : static_cast<sal_uInt64>(nRecordNo); + pStrm->Seek( nFilePos ); + } + + SbxDimArray* pArr = nullptr; + SbxVariable* pVar = rPar.Get32(3); + if( pVar->GetType() & SbxARRAY ) + { + SbxBase* pParObj = pVar->GetObject(); + pArr = dynamic_cast<SbxDimArray*>( pParObj ); + } + + bool bRet; + + if( pArr ) + { + sal_uInt64 const nFPos = pStrm->Tell(); + sal_Int32 nDims = pArr->GetDims32(); + std::unique_ptr<sal_Int32[]> pDims(new sal_Int32[ nDims ]); + bRet = lcl_WriteReadSbxArray(*pArr,pStrm,!bRandom,nDims,pDims.get(),bPut); + pDims.reset(); + if( nBlockLen ) + pStrm->Seek( nFPos + nBlockLen ); + } + else + { + if( bPut ) + bRet = lcl_WriteSbxVariable(*pVar, pStrm, !bRandom, nBlockLen, false); + else + bRet = lcl_ReadSbxVariable(*pVar, pStrm, !bRandom, nBlockLen); + } + if( !bRet || pStrm->GetErrorCode() ) + StarBASIC::Error( ERRCODE_BASIC_IO_ERROR ); +} + +void SbRtl_Put(StarBASIC *, SbxArray & rPar, bool) +{ + PutGet( rPar, true ); +} + +void SbRtl_Get(StarBASIC *, SbxArray & rPar, bool) +{ + PutGet( rPar, false ); +} + +void SbRtl_Environ(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + OUString aResult; + // should be ANSI but that's not possible under Win16 in the DLL + OString aByteStr(OUStringToOString(rPar.Get32(1)->GetOUString(), + osl_getThreadTextEncoding())); + const char* pEnvStr = getenv(aByteStr.getStr()); + if ( pEnvStr ) + { + aResult = OUString(pEnvStr, strlen(pEnvStr), osl_getThreadTextEncoding()); + } + rPar.Get32(0)->PutString( aResult ); +} + +static double GetDialogZoomFactor( bool bX, long nValue ) +{ + OutputDevice* pDevice = Application::GetDefaultDevice(); + double nResult = 0; + if( pDevice ) + { + Size aRefSize( nValue, nValue ); + Fraction aFracX( 1, 26 ); + Fraction aFracY( 1, 24 ); + MapMode aMap( MapUnit::MapAppFont, Point(), aFracX, aFracY ); + Size aScaledSize = pDevice->LogicToPixel( aRefSize, aMap ); + aRefSize = pDevice->LogicToPixel( aRefSize, MapMode(MapUnit::MapTwip) ); + + double nRef, nScaled; + if( bX ) + { + nRef = aRefSize.Width(); + nScaled = aScaledSize.Width(); + } + else + { + nRef = aRefSize.Height(); + nScaled = aScaledSize.Height(); + } + nResult = nScaled / nRef; + } + return nResult; +} + + +void SbRtl_GetDialogZoomFactorX(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + rPar.Get32(0)->PutDouble( GetDialogZoomFactor( true, rPar.Get32(1)->GetLong() )); +} + +void SbRtl_GetDialogZoomFactorY(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + rPar.Get32(0)->PutDouble( GetDialogZoomFactor( false, rPar.Get32(1)->GetLong())); +} + + +void SbRtl_EnableReschedule(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutEmpty(); + if ( rPar.Count32() != 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + if( GetSbData()->pInst ) + GetSbData()->pInst->EnableReschedule( rPar.Get32(1)->GetBool() ); +} + +void SbRtl_GetSystemTicks(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + rPar.Get32(0)->PutLong( tools::Time::GetSystemTicks() ); +} + +void SbRtl_GetPathSeparator(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + rPar.Get32(0)->PutString( OUString( SAL_PATHDELIMITER ) ); +} + +void SbRtl_ResolvePath(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() == 2 ) + { + OUString aStr = rPar.Get32(1)->GetOUString(); + rPar.Get32(0)->PutString( aStr ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_TypeLen(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + SbxDataType eType = rPar.Get32(1)->GetType(); + sal_Int16 nLen = 0; + switch( eType ) + { + case SbxEMPTY: + case SbxNULL: + case SbxVECTOR: + case SbxARRAY: + case SbxBYREF: + case SbxVOID: + case SbxHRESULT: + case SbxPOINTER: + case SbxDIMARRAY: + case SbxCARRAY: + case SbxUSERDEF: + nLen = 0; + break; + + case SbxINTEGER: + case SbxERROR: + case SbxUSHORT: + case SbxINT: + case SbxUINT: + nLen = 2; + break; + + case SbxLONG: + case SbxSINGLE: + case SbxULONG: + nLen = 4; + break; + + case SbxDOUBLE: + case SbxCURRENCY: + case SbxDATE: + case SbxSALINT64: + case SbxSALUINT64: + nLen = 8; + break; + + case SbxOBJECT: + case SbxVARIANT: + case SbxDATAOBJECT: + nLen = 0; + break; + + case SbxCHAR: + case SbxBYTE: + case SbxBOOL: + nLen = 1; + break; + + case SbxLPSTR: + case SbxLPWSTR: + case SbxCoreSTRING: + case SbxSTRING: + nLen = static_cast<sal_Int16>(rPar.Get32(1)->GetOUString().getLength()); + break; + + default: + nLen = 0; + break; + } + rPar.Get32(0)->PutInteger( nLen ); + } +} + + +// 1st parameter == class name, other parameters for initialisation +void SbRtl_CreateUnoStruct(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_CreateUnoStruct( rPar ); +} + + +// 1st parameter == service-name +void SbRtl_CreateUnoService(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_CreateUnoService( rPar ); +} + +void SbRtl_CreateUnoServiceWithArguments(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_CreateUnoServiceWithArguments( rPar ); +} + + +void SbRtl_CreateUnoValue(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_CreateUnoValue( rPar ); +} + + +// no parameters +void SbRtl_GetProcessServiceManager(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_GetProcessServiceManager( rPar ); +} + + +// 1st parameter == Sequence<PropertyValue> +void SbRtl_CreatePropertySet(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_CreatePropertySet( rPar ); +} + + +// multiple interface-names as parameters +void SbRtl_HasUnoInterfaces(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_HasInterfaces( rPar ); +} + + +void SbRtl_IsUnoStruct(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_IsUnoStruct( rPar ); +} + + +void SbRtl_EqualUnoObjects(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_EqualUnoObjects( rPar ); +} + +void SbRtl_CreateUnoDialog(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_CreateUnoDialog( rPar ); +} + +// Return the application standard lib as root scope +void SbRtl_GlobalScope(StarBASIC * pBasic, SbxArray & rPar, bool) +{ + SbxObject* p = pBasic; + while( p->GetParent() ) + { + p = p->GetParent(); + } + SbxVariableRef refVar = rPar.Get32(0); + refVar->PutObject( p ); +} + +// Helper functions to convert Url from/to system paths +void SbRtl_ConvertToUrl(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() == 2 ) + { + OUString aStr = rPar.Get32(1)->GetOUString(); + INetURLObject aURLObj( aStr, INetProtocol::File ); + OUString aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( aFileURL.isEmpty() ) + { + osl::File::getFileURLFromSystemPath(aStr, aFileURL); + } + if( aFileURL.isEmpty() ) + { + aFileURL = aStr; + } + rPar.Get32(0)->PutString(aFileURL); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_ConvertFromUrl(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() == 2 ) + { + OUString aStr = rPar.Get32(1)->GetOUString(); + OUString aSysPath; + ::osl::File::getSystemPathFromFileURL( aStr, aSysPath ); + if( aSysPath.isEmpty() ) + { + aSysPath = aStr; + } + rPar.Get32(0)->PutString(aSysPath); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + + +// Provide DefaultContext +void SbRtl_GetDefaultContext(StarBASIC *, SbxArray & rPar, bool) +{ + RTL_Impl_GetDefaultContext( rPar ); +} + +void SbRtl_Join(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if ( nParCount != 3 && nParCount != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + SbxBase* pParObj = rPar.Get32(1)->GetObject(); + SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj ); + if( pArr ) + { + if( pArr->GetDims32() != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_WRONG_DIMS ); // Syntax Error?! + return; + } + OUString aDelim; + if( nParCount == 3 ) + { + aDelim = rPar.Get32(2)->GetOUString(); + } + else + { + aDelim = " "; + } + OUStringBuffer aRetStr(32); + sal_Int32 nLower, nUpper; + pArr->GetDim32( 1, nLower, nUpper ); + sal_Int32 aIdx[1]; + for (aIdx[0] = nLower; aIdx[0] <= nUpper; ++aIdx[0]) + { + OUString aStr = pArr->Get32(aIdx)->GetOUString(); + aRetStr.append(aStr); + if (aIdx[0] != nUpper) + { + aRetStr.append(aDelim); + } + } + rPar.Get32(0)->PutString( aRetStr.makeStringAndClear() ); + } + else + { + StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS ); + } +} + + +void SbRtl_Split(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if ( nParCount < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + OUString aExpression = rPar.Get32(1)->GetOUString(); + sal_Int32 nArraySize = 0; + std::vector< OUString > vRet; + if( !aExpression.isEmpty() ) + { + OUString aDelim; + if( nParCount >= 3 ) + { + aDelim = rPar.Get32(2)->GetOUString(); + } + else + { + aDelim = " "; + } + + sal_Int32 nCount = -1; + if( nParCount == 4 ) + { + nCount = rPar.Get32(3)->GetLong(); + } + sal_Int32 nDelimLen = aDelim.getLength(); + if( nDelimLen ) + { + sal_Int32 iSearch = -1; + sal_Int32 iStart = 0; + do + { + bool bBreak = false; + if( nCount >= 0 && nArraySize == nCount - 1 ) + { + bBreak = true; + } + iSearch = aExpression.indexOf( aDelim, iStart ); + OUString aSubStr; + if( iSearch >= 0 && !bBreak ) + { + aSubStr = aExpression.copy( iStart, iSearch - iStart ); + iStart = iSearch + nDelimLen; + } + else + { + aSubStr = aExpression.copy( iStart ); + } + vRet.push_back( aSubStr ); + nArraySize++; + + if( bBreak ) + { + break; + } + } + while( iSearch >= 0 ); + } + else + { + vRet.push_back( aExpression ); + nArraySize = 1; + } + } + + SbxDimArray* pArray = new SbxDimArray( SbxVARIANT ); + pArray->unoAddDim32( 0, nArraySize-1 ); + + // insert parameter(s) into the array + for(sal_Int32 i = 0 ; i < nArraySize ; i++ ) + { + SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); + xVar->PutString( vRet[i] ); + pArray->Put32( xVar.get(), &i ); + } + + // return array + SbxVariableRef refVar = rPar.Get32(0); + SbxFlagBits nFlags = refVar->GetFlags(); + refVar->ResetFlag( SbxFlagBits::Fixed ); + refVar->PutObject( pArray ); + refVar->SetFlags( nFlags ); + refVar->SetParameters( nullptr ); +} + +// MonthName(month[, abbreviate]) +void SbRtl_MonthName(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if( nParCount != 2 && nParCount != 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + const Reference< XCalendar4 >& xCalendar = getLocaleCalendar(); + if( !xCalendar.is() ) + { + StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR ); + return; + } + Sequence< CalendarItem2 > aMonthSeq = xCalendar->getMonths2(); + sal_Int32 nMonthCount = aMonthSeq.getLength(); + + sal_Int16 nVal = rPar.Get32(1)->GetInteger(); + if( nVal < 1 || nVal > nMonthCount ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + bool bAbbreviate = false; + if( nParCount == 3 ) + bAbbreviate = rPar.Get32(2)->GetBool(); + + const CalendarItem2* pCalendarItems = aMonthSeq.getConstArray(); + const CalendarItem2& rItem = pCalendarItems[nVal - 1]; + + OUString aRetStr = ( bAbbreviate ? rItem.AbbrevName : rItem.FullName ); + rPar.Get32(0)->PutString(aRetStr); +} + +// WeekdayName(weekday, abbreviate, firstdayofweek) +void SbRtl_WeekdayName(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if( nParCount < 2 || nParCount > 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + const Reference< XCalendar4 >& xCalendar = getLocaleCalendar(); + if( !xCalendar.is() ) + { + StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR ); + return; + } + + Sequence< CalendarItem2 > aDaySeq = xCalendar->getDays2(); + sal_Int16 nDayCount = static_cast<sal_Int16>(aDaySeq.getLength()); + sal_Int16 nDay = rPar.Get32(1)->GetInteger(); + sal_Int16 nFirstDay = 0; + if( nParCount == 4 ) + { + nFirstDay = rPar.Get32(3)->GetInteger(); + if( nFirstDay < 0 || nFirstDay > 7 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + } + if( nFirstDay == 0 ) + { + nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 ); + } + nDay = 1 + (nDay + nDayCount + nFirstDay - 2) % nDayCount; + if( nDay < 1 || nDay > nDayCount ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + bool bAbbreviate = false; + if( nParCount >= 3 ) + { + SbxVariable* pPar2 = rPar.Get32(2); + if( !pPar2->IsErr() ) + { + bAbbreviate = pPar2->GetBool(); + } + } + + const CalendarItem2* pCalendarItems = aDaySeq.getConstArray(); + const CalendarItem2& rItem = pCalendarItems[nDay - 1]; + + OUString aRetStr = ( bAbbreviate ? rItem.AbbrevName : rItem.FullName ); + rPar.Get32(0)->PutString( aRetStr ); +} + +void SbRtl_Weekday(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if ( nParCount < 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } + else + { + double aDate = rPar.Get32(1)->GetDate(); + + bool bFirstDay = false; + sal_Int16 nFirstDay = 0; + if ( nParCount > 2 ) + { + nFirstDay = rPar.Get32(2)->GetInteger(); + bFirstDay = true; + } + sal_Int16 nDay = implGetWeekDay( aDate, bFirstDay, nFirstDay ); + rPar.Get32(0)->PutInteger( nDay ); + } +} + +namespace { + +enum Interval +{ + INTERVAL_YYYY, + INTERVAL_Q, + INTERVAL_M, + INTERVAL_Y, + INTERVAL_D, + INTERVAL_W, + INTERVAL_WW, + INTERVAL_H, + INTERVAL_N, + INTERVAL_S +}; + +struct IntervalInfo +{ + Interval meInterval; + char const * mStringCode; + double mdValue; + bool mbSimple; +}; + +} + +static IntervalInfo const * getIntervalInfo( const OUString& rStringCode ) +{ + static IntervalInfo const aIntervalTable[] = + { + { INTERVAL_YYYY, "yyyy", 0.0, false }, // Year + { INTERVAL_Q, "q", 0.0, false }, // Quarter + { INTERVAL_M, "m", 0.0, false }, // Month + { INTERVAL_Y, "y", 1.0, true }, // Day of year + { INTERVAL_D, "d", 1.0, true }, // Day + { INTERVAL_W, "w", 1.0, true }, // Weekday + { INTERVAL_WW, "ww", 7.0, true }, // Week + { INTERVAL_H, "h", 1.0 / 24.0, true }, // Hour + { INTERVAL_N, "n", 1.0 / 1440.0, true }, // Minute + { INTERVAL_S, "s", 1.0 / 86400.0, true } // Second + }; + for( std::size_t i = 0; i != SAL_N_ELEMENTS(aIntervalTable); ++i ) + { + if( rStringCode.equalsIgnoreAsciiCaseAscii( + aIntervalTable[i].mStringCode ) ) + { + return &aIntervalTable[i]; + } + } + return nullptr; +} + +static void implGetDayMonthYear( sal_Int16& rnYear, sal_Int16& rnMonth, sal_Int16& rnDay, double dDate ) +{ + rnDay = implGetDateDay( dDate ); + rnMonth = implGetDateMonth( dDate ); + rnYear = implGetDateYear( dDate ); +} + +/** Limits a date to valid dates within tools' class Date capabilities. + + @return the year number, truncated if necessary and in that case also + rMonth and rDay adjusted. + */ +static sal_Int16 limitDate( sal_Int32 n32Year, sal_Int16& rMonth, sal_Int16& rDay ) +{ + if( n32Year > SAL_MAX_INT16 ) + { + n32Year = SAL_MAX_INT16; + rMonth = 12; + rDay = 31; + } + else if( n32Year < SAL_MIN_INT16 ) + { + n32Year = SAL_MIN_INT16; + rMonth = 1; + rDay = 1; + } + return static_cast<sal_Int16>(n32Year); +} + +void SbRtl_DateAdd(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if( nParCount != 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + OUString aStringCode = rPar.Get32(1)->GetOUString(); + IntervalInfo const * pInfo = getIntervalInfo( aStringCode ); + if( !pInfo ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + sal_Int32 lNumber = rPar.Get32(2)->GetLong(); + double dDate = rPar.Get32(3)->GetDate(); + double dNewDate = 0; + if( pInfo->mbSimple ) + { + double dAdd = pInfo->mdValue * lNumber; + dNewDate = dDate + dAdd; + } + else + { + // Keep hours, minutes, seconds + double dHoursMinutesSeconds = dDate - floor( dDate ); + + bool bOk = true; + sal_Int16 nYear, nMonth, nDay; + sal_Int16 nTargetYear16 = 0, nTargetMonth = 0; + implGetDayMonthYear( nYear, nMonth, nDay, dDate ); + switch( pInfo->meInterval ) + { + case INTERVAL_YYYY: + { + sal_Int32 nTargetYear = lNumber + nYear; + nTargetYear16 = limitDate( nTargetYear, nMonth, nDay ); + /* TODO: should the result be error if the date was limited? It never was. */ + nTargetMonth = nMonth; + bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, SbDateCorrection::TruncateToMonth, dNewDate ); + break; + } + case INTERVAL_Q: + case INTERVAL_M: + { + bool bNeg = (lNumber < 0); + if( bNeg ) + lNumber = -lNumber; + sal_Int32 nYearsAdd; + sal_Int16 nMonthAdd; + if( pInfo->meInterval == INTERVAL_Q ) + { + nYearsAdd = lNumber / 4; + nMonthAdd = static_cast<sal_Int16>( 3 * (lNumber % 4) ); + } + else + { + nYearsAdd = lNumber / 12; + nMonthAdd = static_cast<sal_Int16>( lNumber % 12 ); + } + + sal_Int32 nTargetYear; + if( bNeg ) + { + nTargetMonth = nMonth - nMonthAdd; + if( nTargetMonth <= 0 ) + { + nTargetMonth += 12; + nYearsAdd++; + } + nTargetYear = static_cast<sal_Int32>(nYear) - nYearsAdd; + } + else + { + nTargetMonth = nMonth + nMonthAdd; + if( nTargetMonth > 12 ) + { + nTargetMonth -= 12; + nYearsAdd++; + } + nTargetYear = static_cast<sal_Int32>(nYear) + nYearsAdd; + } + nTargetYear16 = limitDate( nTargetYear, nTargetMonth, nDay ); + /* TODO: should the result be error if the date was limited? It never was. */ + bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, SbDateCorrection::TruncateToMonth, dNewDate ); + break; + } + default: break; + } + + if( bOk ) + dNewDate += dHoursMinutesSeconds; + } + + rPar.Get32(0)->PutDate( dNewDate ); +} + +static double RoundImpl( double d ) +{ + return ( d >= 0 ) ? floor( d + 0.5 ) : -floor( -d + 0.5 ); +} + +void SbRtl_DateDiff(StarBASIC *, SbxArray & rPar, bool) +{ + // DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]]) + + sal_uInt32 nParCount = rPar.Count32(); + if( nParCount < 4 || nParCount > 6 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + OUString aStringCode = rPar.Get32(1)->GetOUString(); + IntervalInfo const * pInfo = getIntervalInfo( aStringCode ); + if( !pInfo ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + double dDate1 = rPar.Get32(2)->GetDate(); + double dDate2 = rPar.Get32(3)->GetDate(); + + double dRet = 0.0; + switch( pInfo->meInterval ) + { + case INTERVAL_YYYY: + { + sal_Int16 nYear1 = implGetDateYear( dDate1 ); + sal_Int16 nYear2 = implGetDateYear( dDate2 ); + dRet = nYear2 - nYear1; + break; + } + case INTERVAL_Q: + { + sal_Int16 nYear1 = implGetDateYear( dDate1 ); + sal_Int16 nYear2 = implGetDateYear( dDate2 ); + sal_Int16 nQ1 = 1 + (implGetDateMonth( dDate1 ) - 1) / 3; + sal_Int16 nQ2 = 1 + (implGetDateMonth( dDate2 ) - 1) / 3; + sal_Int16 nQGes1 = 4 * nYear1 + nQ1; + sal_Int16 nQGes2 = 4 * nYear2 + nQ2; + dRet = nQGes2 - nQGes1; + break; + } + case INTERVAL_M: + { + sal_Int16 nYear1 = implGetDateYear( dDate1 ); + sal_Int16 nYear2 = implGetDateYear( dDate2 ); + sal_Int16 nMonth1 = implGetDateMonth( dDate1 ); + sal_Int16 nMonth2 = implGetDateMonth( dDate2 ); + sal_Int16 nMonthGes1 = 12 * nYear1 + nMonth1; + sal_Int16 nMonthGes2 = 12 * nYear2 + nMonth2; + dRet = nMonthGes2 - nMonthGes1; + break; + } + case INTERVAL_Y: + case INTERVAL_D: + { + double dDays1 = floor( dDate1 ); + double dDays2 = floor( dDate2 ); + dRet = dDays2 - dDays1; + break; + } + case INTERVAL_W: + case INTERVAL_WW: + { + double dDays1 = floor( dDate1 ); + double dDays2 = floor( dDate2 ); + if( pInfo->meInterval == INTERVAL_WW ) + { + sal_Int16 nFirstDay = 1; // Default + if( nParCount >= 5 ) + { + nFirstDay = rPar.Get32(4)->GetInteger(); + if( nFirstDay < 0 || nFirstDay > 7 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + if( nFirstDay == 0 ) + { + const Reference< XCalendar4 >& xCalendar = getLocaleCalendar(); + if( !xCalendar.is() ) + { + StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR ); + return; + } + nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 ); + } + } + sal_Int16 nDay1 = implGetWeekDay( dDate1 ); + sal_Int16 nDay1_Diff = nDay1 - nFirstDay; + if( nDay1_Diff < 0 ) + nDay1_Diff += 7; + dDays1 -= nDay1_Diff; + + sal_Int16 nDay2 = implGetWeekDay( dDate2 ); + sal_Int16 nDay2_Diff = nDay2 - nFirstDay; + if( nDay2_Diff < 0 ) + nDay2_Diff += 7; + dDays2 -= nDay2_Diff; + } + + double dDiff = dDays2 - dDays1; + dRet = ( dDiff >= 0 ) ? floor( dDiff / 7.0 ) : -floor( -dDiff / 7.0 ); + break; + } + case INTERVAL_H: + { + dRet = RoundImpl( 24.0 * (dDate2 - dDate1) ); + break; + } + case INTERVAL_N: + { + dRet = RoundImpl( 1440.0 * (dDate2 - dDate1) ); + break; + } + case INTERVAL_S: + { + dRet = RoundImpl( 86400.0 * (dDate2 - dDate1) ); + break; + } + } + rPar.Get32(0)->PutDouble( dRet ); +} + +static double implGetDateOfFirstDayInFirstWeek + ( sal_Int16 nYear, sal_Int16& nFirstDay, sal_Int16& nFirstWeek, bool* pbError = nullptr ) +{ + ErrCode nError = ERRCODE_NONE; + if( nFirstDay < 0 || nFirstDay > 7 ) + nError = ERRCODE_BASIC_BAD_ARGUMENT; + + if( nFirstWeek < 0 || nFirstWeek > 3 ) + nError = ERRCODE_BASIC_BAD_ARGUMENT; + + Reference< XCalendar4 > xCalendar; + if( nFirstDay == 0 || nFirstWeek == 0 ) + { + xCalendar = getLocaleCalendar(); + if( !xCalendar.is() ) + nError = ERRCODE_BASIC_BAD_ARGUMENT; + } + + if( nError != ERRCODE_NONE ) + { + StarBASIC::Error( nError ); + if( pbError ) + *pbError = true; + return 0.0; + } + + if( nFirstDay == 0 ) + nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 ); + + sal_Int16 nFirstWeekMinDays = 0; // Not used for vbFirstJan1 = default + if( nFirstWeek == 0 ) + { + nFirstWeekMinDays = xCalendar->getMinimumNumberOfDaysForFirstWeek(); + if( nFirstWeekMinDays == 1 ) + { + nFirstWeekMinDays = 0; + nFirstWeek = 1; + } + else if( nFirstWeekMinDays == 4 ) + nFirstWeek = 2; + else if( nFirstWeekMinDays == 7 ) + nFirstWeek = 3; + } + else if( nFirstWeek == 2 ) + nFirstWeekMinDays = 4; // vbFirstFourDays + else if( nFirstWeek == 3 ) + nFirstWeekMinDays = 7; // vbFirstFourDays + + double dBaseDate; + implDateSerial( nYear, 1, 1, false, SbDateCorrection::None, dBaseDate ); + + sal_Int16 nWeekDay0101 = implGetWeekDay( dBaseDate ); + sal_Int16 nDayDiff = nWeekDay0101 - nFirstDay; + if( nDayDiff < 0 ) + nDayDiff += 7; + + if( nFirstWeekMinDays ) + { + sal_Int16 nThisWeeksDaysInYearCount = 7 - nDayDiff; + if( nThisWeeksDaysInYearCount < nFirstWeekMinDays ) + nDayDiff -= 7; + } + double dRetDate = dBaseDate - nDayDiff; + return dRetDate; +} + +void SbRtl_DatePart(StarBASIC *, SbxArray & rPar, bool) +{ + // DatePart(interval, date[,firstdayofweek[, firstweekofyear]]) + + sal_uInt32 nParCount = rPar.Count32(); + if( nParCount < 3 || nParCount > 5 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + OUString aStringCode = rPar.Get32(1)->GetOUString(); + IntervalInfo const * pInfo = getIntervalInfo( aStringCode ); + if( !pInfo ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + double dDate = rPar.Get32(2)->GetDate(); + + sal_Int32 nRet = 0; + switch( pInfo->meInterval ) + { + case INTERVAL_YYYY: + { + nRet = implGetDateYear( dDate ); + break; + } + case INTERVAL_Q: + { + nRet = 1 + (implGetDateMonth( dDate ) - 1) / 3; + break; + } + case INTERVAL_M: + { + nRet = implGetDateMonth( dDate ); + break; + } + case INTERVAL_Y: + { + sal_Int16 nYear = implGetDateYear( dDate ); + double dBaseDate; + implDateSerial( nYear, 1, 1, false, SbDateCorrection::None, dBaseDate ); + nRet = 1 + sal_Int32( dDate - dBaseDate ); + break; + } + case INTERVAL_D: + { + nRet = implGetDateDay( dDate ); + break; + } + case INTERVAL_W: + { + bool bFirstDay = false; + sal_Int16 nFirstDay = 1; // Default + if( nParCount >= 4 ) + { + nFirstDay = rPar.Get32(3)->GetInteger(); + bFirstDay = true; + } + nRet = implGetWeekDay( dDate, bFirstDay, nFirstDay ); + break; + } + case INTERVAL_WW: + { + sal_Int16 nFirstDay = 1; // Default + if( nParCount >= 4 ) + nFirstDay = rPar.Get32(3)->GetInteger(); + + sal_Int16 nFirstWeek = 1; // Default + if( nParCount == 5 ) + nFirstWeek = rPar.Get32(4)->GetInteger(); + + sal_Int16 nYear = implGetDateYear( dDate ); + bool bError = false; + double dYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear, nFirstDay, nFirstWeek, &bError ); + if( !bError ) + { + if( dYearFirstDay > dDate ) + { + // Date belongs to last year's week + dYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear - 1, nFirstDay, nFirstWeek ); + } + else if( nFirstWeek != 1 ) + { + // Check if date belongs to next year + double dNextYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear + 1, nFirstDay, nFirstWeek ); + if( dDate >= dNextYearFirstDay ) + dYearFirstDay = dNextYearFirstDay; + } + + // Calculate week + double dDiff = dDate - dYearFirstDay; + nRet = 1 + sal_Int32( dDiff / 7 ); + } + break; + } + case INTERVAL_H: + { + nRet = implGetHour( dDate ); + break; + } + case INTERVAL_N: + { + nRet = implGetMinute( dDate ); + break; + } + case INTERVAL_S: + { + nRet = implGetSecond( dDate ); + break; + } + } + rPar.Get32(0)->PutLong( nRet ); +} + +// FormatDateTime(Date[,NamedFormat]) +void SbRtl_FormatDateTime(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if( nParCount < 2 || nParCount > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + double dDate = rPar.Get32(1)->GetDate(); + sal_Int16 nNamedFormat = 0; + if( nParCount > 2 ) + { + nNamedFormat = rPar.Get32(2)->GetInteger(); + if( nNamedFormat < 0 || nNamedFormat > 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + } + + const Reference< XCalendar4 >& xCalendar = getLocaleCalendar(); + if( !xCalendar.is() ) + { + StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR ); + return; + } + + OUString aRetStr; + SbxVariableRef pSbxVar = new SbxVariable( SbxSTRING ); + switch( nNamedFormat ) + { + // GeneralDate: + // Display a date and/or time. If there is a date part, + // display it as a short date. If there is a time part, + // display it as a long time. If present, both parts are displayed. + + // 12/21/2004 11:24:50 AM + // 21.12.2004 12:13:51 + case 0: + pSbxVar->PutDate( dDate ); + aRetStr = pSbxVar->GetOUString(); + break; + + // LongDate: Display a date using the long date format specified + // in your computer's regional settings. + // Tuesday, December 21, 2004 + // Dienstag, 21. December 2004 + case 1: + { + std::shared_ptr<SvNumberFormatter> pFormatter; + if( GetSbData()->pInst ) + { + pFormatter = GetSbData()->pInst->GetNumberFormatter(); + } + else + { + sal_uInt32 n; // Dummy + pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n ); + } + + LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType(); + const sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_LONG, eLangType ); + Color* pCol; + pFormatter->GetOutputString( dDate, nIndex, aRetStr, &pCol ); + break; + } + + // ShortDate: Display a date using the short date format specified + // in your computer's regional settings. + // 21.12.2004 + case 2: + pSbxVar->PutDate( floor(dDate) ); + aRetStr = pSbxVar->GetOUString(); + break; + + // LongTime: Display a time using the time format specified + // in your computer's regional settings. + // 11:24:50 AM + // 12:13:51 + case 3: + // ShortTime: Display a time using the 24-hour format (hh:mm). + // 11:24 + case 4: + double dTime = modf( dDate, &o3tl::temporary(double()) ); + pSbxVar->PutDate( dTime ); + if( nNamedFormat == 3 ) + { + aRetStr = pSbxVar->GetOUString(); + } + else + { + aRetStr = pSbxVar->GetOUString().copy( 0, 5 ); + } + break; + } + + rPar.Get32(0)->PutString( aRetStr ); +} + +void SbRtl_Frac(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if( nParCount != 2) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + SbxVariable *pSbxVariable = rPar.Get32(1); + double dVal = pSbxVariable->GetDouble(); + if(dVal >= 0) + rPar.Get32(0)->PutDouble(dVal - ::rtl::math::approxFloor(dVal)); + else + rPar.Get32(0)->PutDouble(dVal - ::rtl::math::approxCeil(dVal)); +} + +void SbRtl_Round(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nParCount = rPar.Count32(); + if( nParCount != 2 && nParCount != 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + SbxVariable *pSbxVariable = rPar.Get32(1); + double dVal = pSbxVariable->GetDouble(); + double dRes = 0.0; + if( dVal != 0.0 ) + { + bool bNeg = false; + if( dVal < 0.0 ) + { + bNeg = true; + dVal = -dVal; + } + + sal_Int16 numdecimalplaces = 0; + if( nParCount == 3 ) + { + numdecimalplaces = rPar.Get32(2)->GetInteger(); + if( numdecimalplaces < 0 || numdecimalplaces > 22 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + } + + if( numdecimalplaces == 0 ) + { + dRes = floor( dVal + 0.5 ); + } + else + { + double dFactor = pow( 10.0, numdecimalplaces ); + dVal *= dFactor; + dRes = floor( dVal + 0.5 ); + dRes /= dFactor; + } + + if( bNeg ) + dRes = -dRes; + } + rPar.Get32(0)->PutDouble( dRes ); +} + +static void CallFunctionAccessFunction( const Sequence< Any >& aArgs, const OUString& sFuncName, SbxVariable* pRet ) +{ + static Reference< XFunctionAccess > xFunc; + try + { + if ( !xFunc.is() ) + { + Reference< XMultiServiceFactory > xFactory( getProcessServiceFactory() ); + if( xFactory.is() ) + { + xFunc.set( xFactory->createInstance("com.sun.star.sheet.FunctionAccess"), UNO_QUERY_THROW); + } + } + Any aRet = xFunc->callFunction( sFuncName, aArgs ); + + unoToSbxValue( pRet, aRet ); + + } + catch(const Exception& ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + } +} + +void SbRtl_SYD(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 4 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // retrieve non-optional params + + Sequence< Any > aParams( 4 ); + aParams[ 0 ] <<= rPar.Get32(1)->GetDouble(); + aParams[ 1 ] <<= rPar.Get32(2)->GetDouble(); + aParams[ 2 ] <<= rPar.Get32(3)->GetDouble(); + aParams[ 3 ] <<= rPar.Get32(4)->GetDouble(); + + CallFunctionAccessFunction( aParams, "SYD", rPar.Get32( 0 ) ); +} + +void SbRtl_SLN(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // retrieve non-optional params + + Sequence< Any > aParams( 3 ); + aParams[ 0 ] <<= rPar.Get32(1)->GetDouble(); + aParams[ 1 ] <<= rPar.Get32(2)->GetDouble(); + aParams[ 2 ] <<= rPar.Get32(3)->GetDouble(); + + CallFunctionAccessFunction( aParams, "SLN", rPar.Get32( 0 ) ); +} + +void SbRtl_Pmt(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 3 || nArgCount > 5 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + + double rate = rPar.Get32(1)->GetDouble(); + double nper = rPar.Get32(2)->GetDouble(); + double pmt = rPar.Get32(3)->GetDouble(); + + // set default values for Optional args + double fv = 0; + double type = 0; + + // fv + if ( nArgCount >= 4 ) + { + if( rPar.Get32(4)->GetType() != SbxEMPTY ) + fv = rPar.Get32(4)->GetDouble(); + } + // type + if ( nArgCount >= 5 ) + { + if( rPar.Get32(5)->GetType() != SbxEMPTY ) + type = rPar.Get32(5)->GetDouble(); + } + + Sequence< Any > aParams( 5 ); + aParams[ 0 ] <<= rate; + aParams[ 1 ] <<= nper; + aParams[ 2 ] <<= pmt; + aParams[ 3 ] <<= fv; + aParams[ 4 ] <<= type; + + CallFunctionAccessFunction( aParams, "Pmt", rPar.Get32( 0 ) ); +} + +void SbRtl_PPmt(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 4 || nArgCount > 6 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + + double rate = rPar.Get32(1)->GetDouble(); + double per = rPar.Get32(2)->GetDouble(); + double nper = rPar.Get32(3)->GetDouble(); + double pv = rPar.Get32(4)->GetDouble(); + + // set default values for Optional args + double fv = 0; + double type = 0; + + // fv + if ( nArgCount >= 5 ) + { + if( rPar.Get32(5)->GetType() != SbxEMPTY ) + fv = rPar.Get32(5)->GetDouble(); + } + // type + if ( nArgCount >= 6 ) + { + if( rPar.Get32(6)->GetType() != SbxEMPTY ) + type = rPar.Get32(6)->GetDouble(); + } + + Sequence< Any > aParams( 6 ); + aParams[ 0 ] <<= rate; + aParams[ 1 ] <<= per; + aParams[ 2 ] <<= nper; + aParams[ 3 ] <<= pv; + aParams[ 4 ] <<= fv; + aParams[ 5 ] <<= type; + + CallFunctionAccessFunction( aParams, "PPmt", rPar.Get32( 0 ) ); +} + +void SbRtl_PV(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 3 || nArgCount > 5 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + + double rate = rPar.Get32(1)->GetDouble(); + double nper = rPar.Get32(2)->GetDouble(); + double pmt = rPar.Get32(3)->GetDouble(); + + // set default values for Optional args + double fv = 0; + double type = 0; + + // fv + if ( nArgCount >= 4 ) + { + if( rPar.Get32(4)->GetType() != SbxEMPTY ) + fv = rPar.Get32(4)->GetDouble(); + } + // type + if ( nArgCount >= 5 ) + { + if( rPar.Get32(5)->GetType() != SbxEMPTY ) + type = rPar.Get32(5)->GetDouble(); + } + + Sequence< Any > aParams( 5 ); + aParams[ 0 ] <<= rate; + aParams[ 1 ] <<= nper; + aParams[ 2 ] <<= pmt; + aParams[ 3 ] <<= fv; + aParams[ 4 ] <<= type; + + CallFunctionAccessFunction( aParams, "PV", rPar.Get32( 0 ) ); +} + +void SbRtl_NPV(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 1 || nArgCount > 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + Sequence< Any > aParams( 2 ); + aParams[ 0 ] <<= rPar.Get32(1)->GetDouble(); + Any aValues = sbxToUnoValue( rPar.Get32(2), + cppu::UnoType<Sequence<double>>::get() ); + + // convert for calc functions + Sequence< Sequence< double > > sValues(1); + aValues >>= sValues[ 0 ]; + aValues <<= sValues; + + aParams[ 1 ] = aValues; + + CallFunctionAccessFunction( aParams, "NPV", rPar.Get32( 0 ) ); +} + +void SbRtl_NPer(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 3 || nArgCount > 5 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + + double rate = rPar.Get32(1)->GetDouble(); + double pmt = rPar.Get32(2)->GetDouble(); + double pv = rPar.Get32(3)->GetDouble(); + + // set default values for Optional args + double fv = 0; + double type = 0; + + // fv + if ( nArgCount >= 4 ) + { + if( rPar.Get32(4)->GetType() != SbxEMPTY ) + fv = rPar.Get32(4)->GetDouble(); + } + // type + if ( nArgCount >= 5 ) + { + if( rPar.Get32(5)->GetType() != SbxEMPTY ) + type = rPar.Get32(5)->GetDouble(); + } + + Sequence< Any > aParams( 5 ); + aParams[ 0 ] <<= rate; + aParams[ 1 ] <<= pmt; + aParams[ 2 ] <<= pv; + aParams[ 3 ] <<= fv; + aParams[ 4 ] <<= type; + + CallFunctionAccessFunction( aParams, "NPer", rPar.Get32( 0 ) ); +} + +void SbRtl_MIRR(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + // retrieve non-optional params + + Sequence< Any > aParams( 3 ); + Any aValues = sbxToUnoValue( rPar.Get32(1), + cppu::UnoType<Sequence<double>>::get() ); + + // convert for calc functions + Sequence< Sequence< double > > sValues(1); + aValues >>= sValues[ 0 ]; + aValues <<= sValues; + + aParams[ 0 ] = aValues; + aParams[ 1 ] <<= rPar.Get32(2)->GetDouble(); + aParams[ 2 ] <<= rPar.Get32(3)->GetDouble(); + + CallFunctionAccessFunction( aParams, "MIRR", rPar.Get32( 0 ) ); +} + +void SbRtl_IRR(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 1 || nArgCount > 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + Any aValues = sbxToUnoValue( rPar.Get32(1), + cppu::UnoType<Sequence<double>>::get() ); + + // convert for calc functions + Sequence< Sequence< double > > sValues(1); + aValues >>= sValues[ 0 ]; + aValues <<= sValues; + + // set default values for Optional args + double guess = 0.1; + // guess + if ( nArgCount >= 2 ) + { + if( rPar.Get32(2)->GetType() != SbxEMPTY ) + guess = rPar.Get32(2)->GetDouble(); + } + + Sequence< Any > aParams( 2 ); + aParams[ 0 ] = aValues; + aParams[ 1 ] <<= guess; + + CallFunctionAccessFunction( aParams, "IRR", rPar.Get32( 0 ) ); +} + +void SbRtl_IPmt(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 4 || nArgCount > 6 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + + double rate = rPar.Get32(1)->GetDouble(); + double per = rPar.Get32(2)->GetInteger(); + double nper = rPar.Get32(3)->GetDouble(); + double pv = rPar.Get32(4)->GetDouble(); + + // set default values for Optional args + double fv = 0; + double type = 0; + + // fv + if ( nArgCount >= 5 ) + { + if( rPar.Get32(5)->GetType() != SbxEMPTY ) + fv = rPar.Get32(5)->GetDouble(); + } + // type + if ( nArgCount >= 6 ) + { + if( rPar.Get32(6)->GetType() != SbxEMPTY ) + type = rPar.Get32(6)->GetDouble(); + } + + Sequence< Any > aParams( 6 ); + aParams[ 0 ] <<= rate; + aParams[ 1 ] <<= per; + aParams[ 2 ] <<= nper; + aParams[ 3 ] <<= pv; + aParams[ 4 ] <<= fv; + aParams[ 5 ] <<= type; + + CallFunctionAccessFunction( aParams, "IPmt", rPar.Get32( 0 ) ); +} + +void SbRtl_FV(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 3 || nArgCount > 5 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + + double rate = rPar.Get32(1)->GetDouble(); + double nper = rPar.Get32(2)->GetDouble(); + double pmt = rPar.Get32(3)->GetDouble(); + + // set default values for Optional args + double pv = 0; + double type = 0; + + // pv + if ( nArgCount >= 4 ) + { + if( rPar.Get32(4)->GetType() != SbxEMPTY ) + pv = rPar.Get32(4)->GetDouble(); + } + // type + if ( nArgCount >= 5 ) + { + if( rPar.Get32(5)->GetType() != SbxEMPTY ) + type = rPar.Get32(5)->GetDouble(); + } + + Sequence< Any > aParams( 5 ); + aParams[ 0 ] <<= rate; + aParams[ 1 ] <<= nper; + aParams[ 2 ] <<= pmt; + aParams[ 3 ] <<= pv; + aParams[ 4 ] <<= type; + + CallFunctionAccessFunction( aParams, "FV", rPar.Get32( 0 ) ); +} + +void SbRtl_DDB(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 4 || nArgCount > 5 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + + double cost = rPar.Get32(1)->GetDouble(); + double salvage = rPar.Get32(2)->GetDouble(); + double life = rPar.Get32(3)->GetDouble(); + double period = rPar.Get32(4)->GetDouble(); + + // set default values for Optional args + double factor = 2; + + // factor + if ( nArgCount >= 5 ) + { + if( rPar.Get32(5)->GetType() != SbxEMPTY ) + factor = rPar.Get32(5)->GetDouble(); + } + + Sequence< Any > aParams( 5 ); + aParams[ 0 ] <<= cost; + aParams[ 1 ] <<= salvage; + aParams[ 2 ] <<= life; + aParams[ 3 ] <<= period; + aParams[ 4 ] <<= factor; + + CallFunctionAccessFunction( aParams, "DDB", rPar.Get32( 0 ) ); +} + +void SbRtl_Rate(StarBASIC *, SbxArray & rPar, bool) +{ + sal_uInt32 nArgCount = rPar.Count32()-1; + + if ( nArgCount < 3 || nArgCount > 6 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + // retrieve non-optional params + + double nper = 0; + double pmt = 0; + double pv = 0; + + nper = rPar.Get32(1)->GetDouble(); + pmt = rPar.Get32(2)->GetDouble(); + pv = rPar.Get32(3)->GetDouble(); + + // set default values for Optional args + double fv = 0; + double type = 0; + double guess = 0.1; + + // fv + if ( nArgCount >= 4 ) + { + if( rPar.Get32(4)->GetType() != SbxEMPTY ) + fv = rPar.Get32(4)->GetDouble(); + } + + // type + if ( nArgCount >= 5 ) + { + if( rPar.Get32(5)->GetType() != SbxEMPTY ) + type = rPar.Get32(5)->GetDouble(); + } + + // guess + if ( nArgCount >= 6 ) + { + if( rPar.Get32(6)->GetType() != SbxEMPTY ) + guess = rPar.Get32(6)->GetDouble(); + } + + Sequence< Any > aParams( 6 ); + aParams[ 0 ] <<= nper; + aParams[ 1 ] <<= pmt; + aParams[ 2 ] <<= pv; + aParams[ 3 ] <<= fv; + aParams[ 4 ] <<= type; + aParams[ 5 ] <<= guess; + + CallFunctionAccessFunction( aParams, "Rate", rPar.Get32( 0 ) ); +} + +void SbRtl_StrReverse(StarBASIC *, SbxArray & rPar, bool) +{ + if ( rPar.Count32() != 2 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + SbxVariable *pSbxVariable = rPar.Get32(1); + if( pSbxVariable->IsNull() ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + OUString aStr = comphelper::string::reverseString(pSbxVariable->GetOUString()); + rPar.Get32(0)->PutString( aStr ); +} + +void SbRtl_CompatibilityMode(StarBASIC *, SbxArray & rPar, bool) +{ + bool bEnabled = false; + sal_uInt32 nCount = rPar.Count32(); + if ( nCount != 1 && nCount != 2 ) + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + + SbiInstance* pInst = GetSbData()->pInst; + if( pInst ) + { + if ( nCount == 2 ) + { + pInst->EnableCompatibility( rPar.Get32(1)->GetBool() ); + } + bEnabled = pInst->IsCompatibility(); + } + rPar.Get32(0)->PutBool( bEnabled ); +} + +void SbRtl_Input(StarBASIC *, SbxArray & rPar, bool) +{ + // 2 parameters needed + if ( rPar.Count32() < 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + sal_uInt16 nByteCount = rPar.Get32(1)->GetUShort(); + sal_Int16 nFileNumber = rPar.Get32(2)->GetInteger(); + + SbiIoSystem* pIosys = GetSbData()->pInst->GetIoSystem(); + SbiStream* pSbStrm = pIosys->GetStream( nFileNumber ); + if ( !pSbStrm || !(pSbStrm->GetMode() & (SbiStreamFlags::Binary | SbiStreamFlags::Input)) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL ); + return; + } + + OString aByteBuffer; + ErrCode err = pSbStrm->Read( aByteBuffer, nByteCount, true ); + if( !err ) + err = pIosys->GetError(); + + if( err ) + { + StarBASIC::Error( err ); + return; + } + rPar.Get32(0)->PutString(OStringToOUString(aByteBuffer, osl_getThreadTextEncoding())); +} + +void SbRtl_Me(StarBASIC *, SbxArray & rPar, bool) +{ + SbModule* pActiveModule = GetSbData()->pInst->GetActiveModule(); + SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pActiveModule ); + SbxVariableRef refVar = rPar.Get32(0); + if( pClassModuleObject == nullptr ) + { + SbObjModule* pMod = dynamic_cast<SbObjModule*>( pActiveModule ); + if ( pMod ) + refVar->PutObject( pMod ); + else + StarBASIC::Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT ); + } + else + refVar->PutObject( pClassModuleObject ); +} + +#endif + +sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam, sal_Int16 nFirstDay ) +{ + Date aRefDate( 1,1,1900 ); + sal_Int32 nDays = static_cast<sal_Int32>(aDate); + nDays -= 2; // normalize: 1.1.1900 => 0 + aRefDate.AddDays( nDays); + DayOfWeek aDay = aRefDate.GetDayOfWeek(); + sal_Int16 nDay; + if ( aDay != SUNDAY ) + nDay = static_cast<sal_Int16>(aDay) + 2; + else + nDay = 1; // 1 == Sunday + + // #117253 optional 2nd parameter "firstdayofweek" + if( bFirstDayParam ) + { + if( nFirstDay < 0 || nFirstDay > 7 ) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); +#endif + return 0; + } + if( nFirstDay == 0 ) + { + const Reference< XCalendar4 >& xCalendar = getLocaleCalendar(); + if( !xCalendar.is() ) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR ); +#endif + return 0; + } + nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 ); + } + nDay = 1 + (nDay + 7 - nFirstDay) % 7; + } + return nDay; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/props.cxx b/basic/source/runtime/props.cxx new file mode 100644 index 000000000..1d81ee727 --- /dev/null +++ b/basic/source/runtime/props.cxx @@ -0,0 +1,448 @@ +/* -*- 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 <runtime.hxx> +#include <rtlproto.hxx> +#include <errobject.hxx> +#include <basegfx/numeric/ftools.hxx> + + +// Properties and methods lay the return value down at Get (bWrite = sal_False) +// at the element 0 of the Argv; at Put (bWrite = sal_True) the value from +// element 0 is stored. + +void SbRtl_Erl(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutLong( StarBASIC::GetErl() ); +} + +void SbRtl_Err(StarBASIC *, SbxArray & rPar, bool bWrite) +{ + if( SbiRuntime::isVBAEnabled() ) + { + rPar.Get32(0)->PutObject( SbxErrObject::getErrObject().get() ); + } + else + { + if( bWrite ) + { + sal_Int32 nVal = rPar.Get32(0)->GetLong(); + if( nVal <= 65535 ) + StarBASIC::Error( StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nVal) ) ); + } + else + rPar.Get32(0)->PutLong( StarBASIC::GetVBErrorCode( StarBASIC::GetErrBasic() ) ); + } +} + +void SbRtl_False(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutBool( false ); +} + +void SbRtl_Empty(StarBASIC *, SbxArray &, bool) {} + +void SbRtl_Nothing(StarBASIC *, SbxArray & rPar, bool) +{ + // return an empty object + rPar.Get32(0)->PutObject( nullptr ); +} + +void SbRtl_Null(StarBASIC *, SbxArray & rPar, bool) +{ + // returns an empty object-variable + rPar.Get32(0)->PutNull(); +} + +void SbRtl_PI(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutDouble( F_PI ); +} + +void SbRtl_True(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutBool( true ); +} + +void SbRtl_ATTR_NORMAL(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(0); +} +void SbRtl_ATTR_READONLY(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} +void SbRtl_ATTR_HIDDEN(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(2); +} +void SbRtl_ATTR_SYSTEM(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(4); +} +void SbRtl_ATTR_VOLUME(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(8); +} +void SbRtl_ATTR_DIRECTORY(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(16); +} +void SbRtl_ATTR_ARCHIVE(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(32); +} + +void SbRtl_V_EMPTY(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(0); +} +void SbRtl_V_NULL(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} +void SbRtl_V_INTEGER(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(2); +} +void SbRtl_V_LONG(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(3); +} +void SbRtl_V_SINGLE(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(4); +} +void SbRtl_V_DOUBLE(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(5); +} +void SbRtl_V_CURRENCY(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(6); +} +void SbRtl_V_DATE(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(7); +} +void SbRtl_V_STRING(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(8); +} + +void SbRtl_MB_OK(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(0); +} +void SbRtl_MB_OKCANCEL(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} +void SbRtl_MB_ABORTRETRYIGNORE(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(2); +} +void SbRtl_MB_YESNOCANCEL(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(3); +} +void SbRtl_MB_YESNO(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(4); +} +void SbRtl_MB_RETRYCANCEL(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(5); +} +void SbRtl_MB_ICONSTOP(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(16); +} +void SbRtl_MB_ICONQUESTION(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(32); +} +void SbRtl_MB_ICONEXCLAMATION(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(48); +} +void SbRtl_MB_ICONINFORMATION(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(64); +} +void SbRtl_MB_DEFBUTTON1(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(0); +} +void SbRtl_MB_DEFBUTTON2(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(256); +} +void SbRtl_MB_DEFBUTTON3(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(512); +} +void SbRtl_MB_APPLMODAL(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(0); +} +void SbRtl_MB_SYSTEMMODAL(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(4096); +} + +void SbRtl_IDOK(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} + +void SbRtl_IDCANCEL(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(2); +} +void SbRtl_IDABORT(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(3); +} +void SbRtl_IDRETRY(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(4); +} +void SbRtl_IDYES(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(6); +} +void SbRtl_IDNO(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(7); +} + +void SbRtl_CF_TEXT(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} +void SbRtl_CF_BITMAP(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(2); +} +void SbRtl_CF_METAFILEPICT(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(3); +} + +void SbRtl_TYP_AUTHORFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(7); +} +void SbRtl_TYP_CHAPTERFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(4); +} +void SbRtl_TYP_CONDTXTFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(27); +} +void SbRtl_TYP_DATEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(0); +} +void SbRtl_TYP_DBFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(19); +} +void SbRtl_TYP_DBNAMEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(3); +} +void SbRtl_TYP_DBNEXTSETFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(24); +} +void SbRtl_TYP_DBNUMSETFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(25); +} +void SbRtl_TYP_DBSETNUMBERFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(26); +} +void SbRtl_TYP_DDEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(14); +} +void SbRtl_TYP_DOCINFOFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(18); +} +void SbRtl_TYP_DOCSTATFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(6); +} +void SbRtl_TYP_EXTUSERFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(30); +} +void SbRtl_TYP_FILENAMEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(2); +} +void SbRtl_TYP_FIXDATEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(31); +} +void SbRtl_TYP_FIXTIMEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(32); +} +void SbRtl_TYP_FORMELFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(10); +} +void SbRtl_TYP_GETFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(9); +} +void SbRtl_TYP_GETREFFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(13); +} +void SbRtl_TYP_HIDDENPARAFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(17); +} +void SbRtl_TYP_HIDDENTXTFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(11); +} +void SbRtl_TYP_INPUTFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(16); +} +void SbRtl_TYP_MACROFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(15); +} +void SbRtl_TYP_NEXTPAGEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(28); +} +void SbRtl_TYP_PAGENUMBERFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(5); +} +void SbRtl_TYP_POSTITFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(21); +} +void SbRtl_TYP_PREVPAGEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(29); +} +void SbRtl_TYP_SEQFLD(StarBASIC * , SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(23); +} +void SbRtl_TYP_SETFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(8); +} +void SbRtl_TYP_SETINPFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(33); +} +void SbRtl_TYP_SETREFFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(12); +} +void SbRtl_TYP_TEMPLNAMEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(22); +} +void SbRtl_TYP_TIMEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} +void SbRtl_TYP_USERFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(20); +} +void SbRtl_TYP_USRINPFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(34); +} +void SbRtl_TYP_SETREFPAGEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(35); +} +void SbRtl_TYP_GETREFPAGEFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(36); +} +void SbRtl_TYP_INTERNETFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(37); +} + +void SbRtl_SET_ON(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} +void SbRtl_SET_OFF(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(0); +} +void SbRtl_TOGGLE(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(2); +} + +void SbRtl_FRAMEANCHORPAGE(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} +void SbRtl_FRAMEANCHORPARA(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(14); +} +void SbRtl_FRAMEANCHORCHAR(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(15); +} + +void SbRtl_CLEAR_ALLTABS(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(2); +} +void SbRtl_CLEAR_TAB(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(1); +} +void SbRtl_SET_TAB(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(0); +} + +void SbRtl_TYP_JUMPEDITFLD(StarBASIC *, SbxArray & rPar, bool) +{ + rPar.Get32(0)->PutInteger(38); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/runtime.cxx b/basic/source/runtime/runtime.cxx new file mode 100644 index 000000000..cf05da0f0 --- /dev/null +++ b/basic/source/runtime/runtime.cxx @@ -0,0 +1,4641 @@ +/* -*- 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 <stdlib.h> + +#include <algorithm> +#include <string_view> +#include <unordered_map> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/script/XDefaultMethod.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/util/SearchAlgorithms2.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> + +#include <tools/wldcrd.hxx> +#include <tools/diagnose_ex.h> + +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <rtl/instance.hxx> +#include <rtl/math.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/character.hxx> + +#include <svl/zforlist.hxx> + +#include <i18nutil/searchopt.hxx> +#include <unotools/syslocale.hxx> +#include <unotools/textsearch.hxx> + +#include <basic/sbuno.hxx> + +#include <codegen.hxx> +#include "comenumwrapper.hxx" +#include "ddectrl.hxx" +#include "dllmgr.hxx" +#include <errobject.hxx> +#include <image.hxx> +#include <iosys.hxx> +#include <opcodes.hxx> +#include <runtime.hxx> +#include <sb.hxx> +#include <sbintern.hxx> +#include <sbprop.hxx> +#include <sbunoobj.hxx> +#include <basic/codecompletecache.hxx> +#include <memory> + +using com::sun::star::uno::Reference; + +using namespace com::sun::star::uno; +using namespace com::sun::star::container; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::script; + +using namespace ::com::sun::star; + +static void lcl_clearImpl( SbxVariableRef const & refVar, SbxDataType const & eType ); +static void lcl_eraseImpl( SbxVariableRef const & refVar, bool bVBAEnabled ); + +bool SbiRuntime::isVBAEnabled() +{ + bool bResult = false; + SbiInstance* pInst = GetSbData()->pInst; + if ( pInst && GetSbData()->pInst->pRun ) + bResult = pInst->pRun->bVBAEnabled; + return bResult; +} + +void StarBASIC::SetVBAEnabled( bool bEnabled ) +{ + if ( bDocBasic ) + { + bVBAEnabled = bEnabled; + } +} + +bool StarBASIC::isVBAEnabled() const +{ + if ( bDocBasic ) + { + if( SbiRuntime::isVBAEnabled() ) + return true; + return bVBAEnabled; + } + return false; +} + +struct SbiArgv { // Argv stack: + SbxArrayRef refArgv; // Argv + short nArgc; // Argc + + SbiArgv(SbxArrayRef const & refArgv_, short nArgc_) : + refArgv(refArgv_), + nArgc(nArgc_) {} +}; + +struct SbiGosub { // GOSUB-Stack: + const sal_uInt8* pCode; // Return-Pointer + sal_uInt16 nStartForLvl; // #118235: For Level in moment of gosub + + SbiGosub(const sal_uInt8* pCode_, sal_uInt16 nStartForLvl_) : + pCode(pCode_), + nStartForLvl(nStartForLvl_) {} +}; + +SbiRuntime::pStep0 SbiRuntime::aStep0[] = { // all opcodes without operands + &SbiRuntime::StepNOP, + &SbiRuntime::StepEXP, + &SbiRuntime::StepMUL, + &SbiRuntime::StepDIV, + &SbiRuntime::StepMOD, + &SbiRuntime::StepPLUS, + &SbiRuntime::StepMINUS, + &SbiRuntime::StepNEG, + &SbiRuntime::StepEQ, + &SbiRuntime::StepNE, + &SbiRuntime::StepLT, + &SbiRuntime::StepGT, + &SbiRuntime::StepLE, + &SbiRuntime::StepGE, + &SbiRuntime::StepIDIV, + &SbiRuntime::StepAND, + &SbiRuntime::StepOR, + &SbiRuntime::StepXOR, + &SbiRuntime::StepEQV, + &SbiRuntime::StepIMP, + &SbiRuntime::StepNOT, + &SbiRuntime::StepCAT, + + &SbiRuntime::StepLIKE, + &SbiRuntime::StepIS, + // load/save + &SbiRuntime::StepARGC, // establish new Argv + &SbiRuntime::StepARGV, // TOS ==> current Argv + &SbiRuntime::StepINPUT, // Input ==> TOS + &SbiRuntime::StepLINPUT, // Line Input ==> TOS + &SbiRuntime::StepGET, // touch TOS + &SbiRuntime::StepSET, // save object TOS ==> TOS-1 + &SbiRuntime::StepPUT, // TOS ==> TOS-1 + &SbiRuntime::StepPUTC, // TOS ==> TOS-1, then ReadOnly + &SbiRuntime::StepDIM, // DIM + &SbiRuntime::StepREDIM, // REDIM + &SbiRuntime::StepREDIMP, // REDIM PRESERVE + &SbiRuntime::StepERASE, // delete TOS + // branch + &SbiRuntime::StepSTOP, // program end + &SbiRuntime::StepINITFOR, // initialize FOR-Variable + &SbiRuntime::StepNEXT, // increment FOR-Variable + &SbiRuntime::StepCASE, // beginning CASE + &SbiRuntime::StepENDCASE, // end CASE + &SbiRuntime::StepSTDERROR, // standard error handling + &SbiRuntime::StepNOERROR, // no error handling + &SbiRuntime::StepLEAVE, // leave UP + // E/A + &SbiRuntime::StepCHANNEL, // TOS = channel number + &SbiRuntime::StepPRINT, // print TOS + &SbiRuntime::StepPRINTF, // print TOS in field + &SbiRuntime::StepWRITE, // write TOS + &SbiRuntime::StepRENAME, // Rename Tos+1 to Tos + &SbiRuntime::StepPROMPT, // define Input Prompt from TOS + &SbiRuntime::StepRESTART, // Set restart point + &SbiRuntime::StepCHANNEL0, // set E/A-channel 0 + &SbiRuntime::StepEMPTY, // empty expression on stack + &SbiRuntime::StepERROR, // TOS = error code + &SbiRuntime::StepLSET, // save object TOS ==> TOS-1 + &SbiRuntime::StepRSET, // save object TOS ==> TOS-1 + &SbiRuntime::StepREDIMP_ERASE,// Copy array object for REDIMP + &SbiRuntime::StepINITFOREACH,// Init for each loop + &SbiRuntime::StepVBASET,// vba-like set statement + &SbiRuntime::StepERASE_CLEAR,// vba-like set statement + &SbiRuntime::StepARRAYACCESS,// access TOS as array + &SbiRuntime::StepBYVAL, // access TOS as array +}; + +SbiRuntime::pStep1 SbiRuntime::aStep1[] = { // all opcodes with one operand + &SbiRuntime::StepLOADNC, // loading a numeric constant (+ID) + &SbiRuntime::StepLOADSC, // loading a string constant (+ID) + &SbiRuntime::StepLOADI, // Immediate Load (+value) + &SbiRuntime::StepARGN, // save a named Args in Argv (+StringID) + &SbiRuntime::StepPAD, // bring string to a definite length (+length) + // branches + &SbiRuntime::StepJUMP, // jump (+Target) + &SbiRuntime::StepJUMPT, // evaluate TOS, conditional jump (+Target) + &SbiRuntime::StepJUMPF, // evaluate TOS, conditional jump (+Target) + &SbiRuntime::StepONJUMP, // evaluate TOS, jump into JUMP-table (+MaxVal) + &SbiRuntime::StepGOSUB, // UP-call (+Target) + &SbiRuntime::StepRETURN, // UP-return (+0 or Target) + &SbiRuntime::StepTESTFOR, // check FOR-variable, increment (+Endlabel) + &SbiRuntime::StepCASETO, // Tos+1 <= Case <= Tos), 2xremove (+Target) + &SbiRuntime::StepERRHDL, // error handler (+Offset) + &SbiRuntime::StepRESUME, // resume after errors (+0 or 1 or Label) + // E/A + &SbiRuntime::StepCLOSE, // (+channel/0) + &SbiRuntime::StepPRCHAR, // (+char) + // management + &SbiRuntime::StepSETCLASS, // check set + class names (+StringId) + &SbiRuntime::StepTESTCLASS, // Check TOS class (+StringId) + &SbiRuntime::StepLIB, // lib for declare-call (+StringId) + &SbiRuntime::StepBASED, // TOS is incremented by BASE, BASE is pushed before + &SbiRuntime::StepARGTYP, // convert last parameter in Argv (+Type) + &SbiRuntime::StepVBASETCLASS,// vba-like set statement +}; + +SbiRuntime::pStep2 SbiRuntime::aStep2[] = {// all opcodes with two operands + &SbiRuntime::StepRTL, // load from RTL (+StringID+Typ) + &SbiRuntime::StepFIND, // load (+StringID+Typ) + &SbiRuntime::StepELEM, // load element (+StringID+Typ) + &SbiRuntime::StepPARAM, // Parameter (+Offset+Typ) + // branches + &SbiRuntime::StepCALL, // Declare-Call (+StringID+Typ) + &SbiRuntime::StepCALLC, // CDecl-Declare-Call (+StringID+Typ) + &SbiRuntime::StepCASEIS, // Case-Test (+Test-Opcode+False-Target) + // management + &SbiRuntime::StepSTMNT, // beginning of a statement (+Line+Col) + // E/A + &SbiRuntime::StepOPEN, // (+StreamMode+Flags) + // Objects + &SbiRuntime::StepLOCAL, // define local variable (+StringId+Typ) + &SbiRuntime::StepPUBLIC, // module global variable (+StringID+Typ) + &SbiRuntime::StepGLOBAL, // define global variable (+StringID+Typ) + &SbiRuntime::StepCREATE, // create object (+StringId+StringId) + &SbiRuntime::StepSTATIC, // static variable (+StringId+StringId) + &SbiRuntime::StepTCREATE, // user-defined objects (+StringId+StringId) + &SbiRuntime::StepDCREATE, // create object-array (+StringID+StringID) + &SbiRuntime::StepGLOBAL_P, // define global variable which is not overwritten + // by the Basic on a restart (+StringID+Typ) + &SbiRuntime::StepFIND_G, // finds global variable with special treatment because of _GLOBAL_P + &SbiRuntime::StepDCREATE_REDIMP, // redimension object array (+StringID+StringID) + &SbiRuntime::StepFIND_CM, // Search inside a class module (CM) to enable global search in time + &SbiRuntime::StepPUBLIC_P, // Search inside a class module (CM) to enable global search in time + &SbiRuntime::StepFIND_STATIC, // Search inside a class module (CM) to enable global search in time +}; + + +// SbiRTLData + +SbiRTLData::SbiRTLData() +{ + nDirFlags = SbAttributes::NONE; + nCurDirPos = 0; +} + +SbiRTLData::~SbiRTLData() +{ +} + +// SbiInstance + +// 16.10.96: #31460 new concept for StepInto/Over/Out +// The decision whether StepPoint shall be called is done with the help of +// the CallLevel. It's stopped when the current CallLevel is <= nBreakCallLvl. +// The current CallLevel can never be smaller than 1, as it's also incremented +// during the call of a method (also main). Therefore a BreakCallLvl from 0 +// means that the program isn't stopped at all. +// (also have a look at: step2.cxx, SbiRuntime::StepSTMNT() ) + + +void SbiInstance::CalcBreakCallLevel( BasicDebugFlags nFlags ) +{ + + nFlags &= ~BasicDebugFlags::Break; + + sal_uInt16 nRet; + if (nFlags == BasicDebugFlags::StepInto) { + nRet = nCallLvl + 1; // CallLevel+1 is also stopped + } else if (nFlags == (BasicDebugFlags::StepOver | BasicDebugFlags::StepInto)) { + nRet = nCallLvl; // current CallLevel is stopped + } else if (nFlags == BasicDebugFlags::StepOut) { + nRet = nCallLvl - 1; // smaller CallLevel is stopped + } else { + // Basic-IDE returns 0 instead of BasicDebugFlags::Continue, so also default=continue + nRet = 0; // CallLevel is always > 0 -> no StepPoint + } + nBreakCallLvl = nRet; // take result +} + +SbiInstance::SbiInstance( StarBASIC* p ) + : pIosys(new SbiIoSystem) + , pDdeCtrl(new SbiDdeControl) + , pBasic(p) + , meFormatterLangType(LANGUAGE_DONTKNOW) + , meFormatterDateOrder(DateOrder::YMD) + , nStdDateIdx(0) + , nStdTimeIdx(0) + , nStdDateTimeIdx(0) + , nErr(0) + , nErl(0) + , bReschedule(true) + , bCompatibility(false) + , pRun(nullptr) + , nCallLvl(0) + , nBreakCallLvl(0) +{ +} + +SbiInstance::~SbiInstance() +{ + while( pRun ) + { + SbiRuntime* p = pRun->pNext; + delete pRun; + pRun = p; + } + + try + { + int nSize = ComponentVector.size(); + if( nSize ) + { + for( int i = nSize - 1 ; i >= 0 ; --i ) + { + Reference< XComponent > xDlgComponent = ComponentVector[i]; + if( xDlgComponent.is() ) + xDlgComponent->dispose(); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("basic", "SbiInstance::~SbiInstance: caught an exception while disposing the components" ); + } +} + +SbiDllMgr* SbiInstance::GetDllMgr() +{ + if( !pDllMgr ) + { + pDllMgr.reset(new SbiDllMgr); + } + return pDllMgr.get(); +} + +// #39629 create NumberFormatter with the help of a static method now +std::shared_ptr<SvNumberFormatter> const & SbiInstance::GetNumberFormatter() +{ + LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType(); + SvtSysLocale aSysLocale; + DateOrder eDate = aSysLocale.GetLocaleData().getDateOrder(); + if( pNumberFormatter ) + { + if( eLangType != meFormatterLangType || + eDate != meFormatterDateOrder ) + { + pNumberFormatter.reset(); + } + } + meFormatterLangType = eLangType; + meFormatterDateOrder = eDate; + if( !pNumberFormatter ) + { + pNumberFormatter = PrepareNumberFormatter( nStdDateIdx, nStdTimeIdx, nStdDateTimeIdx, + &meFormatterLangType, &meFormatterDateOrder); + } + return pNumberFormatter; +} + +// #39629 offer NumberFormatter static too +std::shared_ptr<SvNumberFormatter> SbiInstance::PrepareNumberFormatter( sal_uInt32 &rnStdDateIdx, + sal_uInt32 &rnStdTimeIdx, sal_uInt32 &rnStdDateTimeIdx, + LanguageType const * peFormatterLangType, DateOrder const * peFormatterDateOrder ) +{ + LanguageType eLangType; + if( peFormatterLangType ) + { + eLangType = *peFormatterLangType; + } + else + { + eLangType = Application::GetSettings().GetLanguageTag().getLanguageType(); + } + DateOrder eDate; + if( peFormatterDateOrder ) + { + eDate = *peFormatterDateOrder; + } + else + { + SvtSysLocale aSysLocale; + eDate = aSysLocale.GetLocaleData().getDateOrder(); + } + + std::shared_ptr<SvNumberFormatter> pNumberFormatter = + std::make_shared<SvNumberFormatter>( comphelper::getProcessComponentContext(), eLangType ); + + // Several parser methods pass SvNumberFormatter::IsNumberFormat() a number + // format index to parse against. Tell the formatter the proper date + // evaluation order, which also determines the date acceptance patterns to + // use if a format was passed. NF_EVALDATEFORMAT_FORMAT restricts to the + // format's locale's date patterns/order (no init/system locale match + // tried) and falls back to NF_EVALDATEFORMAT_INTL if no specific (i.e. 0) + // (or an unknown) format index was passed. + pNumberFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT); + + sal_Int32 nCheckPos = 0; + SvNumFormatType nType; + rnStdTimeIdx = pNumberFormatter->GetStandardFormat( SvNumFormatType::TIME, eLangType ); + + // the formatter's standard templates have only got a two-digit date + // -> registering an own format + + // HACK, because the numberformatter doesn't swap the place holders + // for month, day and year according to the system setting. + // Problem: Print Year(Date) under engl. BS + // also have a look at: basic/source/sbx/sbxdate.cxx + + OUString aDateStr; + switch( eDate ) + { + default: + case DateOrder::MDY: aDateStr = "MM/DD/YYYY"; break; + case DateOrder::DMY: aDateStr = "DD/MM/YYYY"; break; + case DateOrder::YMD: aDateStr = "YYYY/MM/DD"; break; + } + OUString aStr( aDateStr ); // PutandConvertEntry() modifies string! + pNumberFormatter->PutandConvertEntry( aStr, nCheckPos, nType, + rnStdDateIdx, LANGUAGE_ENGLISH_US, eLangType, true); + nCheckPos = 0; + aDateStr += " HH:MM:SS"; + aStr = aDateStr; + pNumberFormatter->PutandConvertEntry( aStr, nCheckPos, nType, + rnStdDateTimeIdx, LANGUAGE_ENGLISH_US, eLangType, true); + return pNumberFormatter; +} + + +// Let engine run. If Flags == BasicDebugFlags::Continue, take Flags over + +void SbiInstance::Stop() +{ + for( SbiRuntime* p = pRun; p; p = p->pNext ) + { + p->Stop(); + } +} + +// Allows Basic IDE to set watch mode to suppress errors +static bool bWatchMode = false; + +void setBasicWatchMode( bool bOn ) +{ + bWatchMode = bOn; +} + +void SbiInstance::Error( ErrCode n ) +{ + Error( n, OUString() ); +} + +void SbiInstance::Error( ErrCode n, const OUString& rMsg ) +{ + if( !bWatchMode ) + { + aErrorMsg = rMsg; + pRun->Error( n ); + } +} + +void SbiInstance::ErrorVB( sal_Int32 nVBNumber, const OUString& rMsg ) +{ + if( !bWatchMode ) + { + ErrCode n = StarBASIC::GetSfxFromVBError( static_cast< sal_uInt16 >( nVBNumber ) ); + if ( !n ) + { + n = ErrCode(nVBNumber); // force orig number, probably should have a specific table of vb ( localized ) errors + } + aErrorMsg = rMsg; + SbiRuntime::translateErrorToVba( n, aErrorMsg ); + + pRun->Error( ERRCODE_BASIC_COMPAT, true/*bVBATranslationAlreadyDone*/ ); + } +} + +void SbiInstance::setErrorVB( sal_Int32 nVBNumber ) +{ + ErrCode n = StarBASIC::GetSfxFromVBError( static_cast< sal_uInt16 >( nVBNumber ) ); + if( !n ) + { + n = ErrCode(nVBNumber); // force orig number, probably should have a specific table of vb ( localized ) errors + } + aErrorMsg = OUString(); + SbiRuntime::translateErrorToVba( n, aErrorMsg ); + + nErr = n; +} + + +void SbiInstance::FatalError( ErrCode n ) +{ + pRun->FatalError( n ); +} + +void SbiInstance::FatalError( ErrCode _errCode, const OUString& _details ) +{ + pRun->FatalError( _errCode, _details ); +} + +void SbiInstance::Abort() +{ + StarBASIC* pErrBasic = GetCurrentBasic( pBasic ); + pErrBasic->RTError( nErr, aErrorMsg, pRun->nLine, pRun->nCol1, pRun->nCol2 ); + StarBASIC::Stop(); +} + +// can be unequal to pRTBasic +StarBASIC* GetCurrentBasic( StarBASIC* pRTBasic ) +{ + StarBASIC* pCurBasic = pRTBasic; + SbModule* pActiveModule = StarBASIC::GetActiveModule(); + if( pActiveModule ) + { + SbxObject* pParent = pActiveModule->GetParent(); + if (StarBASIC *pBasic = dynamic_cast<StarBASIC*>(pParent)) + pCurBasic = pBasic; + } + return pCurBasic; +} + +SbModule* SbiInstance::GetActiveModule() +{ + if( pRun ) + { + return pRun->GetModule(); + } + else + { + return nullptr; + } +} + +SbMethod* SbiInstance::GetCaller( sal_uInt16 nLevel ) +{ + SbiRuntime* p = pRun; + while( nLevel-- && p ) + { + p = p->pNext; + } + return p ? p->GetCaller() : nullptr; +} + +// SbiInstance + +// Attention: pMeth can also be NULL (on a call of the init-code) + +SbiRuntime::SbiRuntime( SbModule* pm, SbMethod* pe, sal_uInt32 nStart ) + : rBasic( *static_cast<StarBASIC*>(pm->pParent) ), pInst( GetSbData()->pInst ), + pMod( pm ), pMeth( pe ), pImg( pMod->pImage ), mpExtCaller(nullptr), m_nLastTime(0) +{ + nFlags = pe ? pe->GetDebugFlags() : BasicDebugFlags::NONE; + pIosys = pInst->GetIoSystem(); + pForStk = nullptr; + pError = nullptr; + pErrCode = + pErrStmnt = + pRestart = nullptr; + pNext = nullptr; + pCode = + pStmnt = reinterpret_cast<const sal_uInt8*>(pImg->GetCode()) + nStart; + bRun = + bError = true; + bInError = false; + bBlocked = false; + nLine = 0; + nCol1 = 0; + nCol2 = 0; + nExprLvl = 0; + nArgc = 0; + nError = ERRCODE_NONE; + nForLvl = 0; + nOps = 0; + refExprStk = new SbxArray; + SetVBAEnabled( pMod->IsVBACompat() ); + SetParameters( pe ? pe->GetParameters() : nullptr ); +} + +SbiRuntime::~SbiRuntime() +{ + ClearArgvStack(); + ClearForStack(); +} + +void SbiRuntime::SetVBAEnabled(bool bEnabled ) +{ + bVBAEnabled = bEnabled; + if ( bVBAEnabled ) + { + if ( pMeth ) + { + mpExtCaller = pMeth->mCaller; + } + } + else + { + mpExtCaller = nullptr; + } +} + +// tdf#79426, tdf#125180 - adds the information about a missing parameter +void SbiRuntime::SetIsMissing( SbxVariable* pVar ) +{ + SbxInfo* pInfo = pVar->GetInfo() ? pVar->GetInfo() : new SbxInfo(); + pInfo->AddParam( pVar->GetName(), SbxMISSING, pVar->GetFlags() ); + pVar->SetInfo( pInfo ); +} + +// tdf#79426, tdf#125180 - checks if a variable contains the information about a missing parameter +bool SbiRuntime::IsMissing( SbxVariable* pVar, sal_uInt16 nIdx ) +{ + return pVar->GetInfo() && pVar->GetInfo()->GetParam( nIdx ) && pVar->GetInfo()->GetParam( nIdx )->eType & SbxMISSING; +} + +// Construction of the parameter list. All ByRef-parameters are directly +// taken over; copies of ByVal-parameters are created. If a particular +// data type is requested, it is converted. + +void SbiRuntime::SetParameters( SbxArray* pParams ) +{ + refParams = new SbxArray; + // for the return value + refParams->Put32( pMeth, 0 ); + + SbxInfo* pInfo = pMeth ? pMeth->GetInfo() : nullptr; + sal_uInt32 nParamCount = pParams ? pParams->Count32() : 1; + assert(nParamCount <= std::numeric_limits<sal_uInt16>::max()); + if( nParamCount > 1 ) + { + for( sal_uInt32 i = 1 ; i < nParamCount ; i++ ) + { + const SbxParamInfo* p = pInfo ? pInfo->GetParam( sal::static_int_cast<sal_uInt16>(i) ) : nullptr; + + // #111897 ParamArray + if( p && (p->nUserData & PARAM_INFO_PARAMARRAY) != 0 ) + { + SbxDimArray* pArray = new SbxDimArray( SbxVARIANT ); + sal_uInt32 nParamArrayParamCount = nParamCount - i; + pArray->unoAddDim32( 0, nParamArrayParamCount - 1 ); + for (sal_uInt32 j = i; j < nParamCount ; ++j) + { + SbxVariable* v = pParams->Get32( j ); + sal_Int32 aDimIndex[1]; + aDimIndex[0] = j - i; + pArray->Put32(v, aDimIndex); + } + SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT ); + pArrayVar->SetFlag( SbxFlagBits::ReadWrite ); + pArrayVar->PutObject( pArray ); + refParams->Put32( pArrayVar, i ); + + // Block ParamArray for missing parameter + pInfo = nullptr; + break; + } + + SbxVariable* v = pParams->Get32( i ); + // methods are always byval! + bool bByVal = dynamic_cast<const SbxMethod *>(v) != nullptr; + SbxDataType t = v->GetType(); + bool bTargetTypeIsArray = false; + if( p ) + { + bByVal |= ( p->eType & SbxBYREF ) == 0; + // tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type + if ( !IsMissing( v, 1 ) ) + { + t = static_cast<SbxDataType>( p->eType & 0x0FFF ); + } + + if( !bByVal && t != SbxVARIANT && + (!v->IsFixed() || static_cast<SbxDataType>(v->GetType() & 0x0FFF ) != t) ) + { + bByVal = true; + } + + bTargetTypeIsArray = (p->nUserData & PARAM_INFO_WITHBRACKETS) != 0; + } + if( bByVal ) + { + // tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type + if( bTargetTypeIsArray && !IsMissing( v, 1 ) ) + { + t = SbxOBJECT; + } + SbxVariable* v2 = new SbxVariable( t ); + v2->SetFlag( SbxFlagBits::ReadWrite ); + // tdf#79426, tdf#125180 - if parameter was missing, readd additional information about a missing parameter + if ( IsMissing( v, 1 ) ) + { + SetIsMissing( v2 ); + } + *v2 = *v; + refParams->Put32( v2, i ); + } + else + { + // tdf#79426, tdf#125180 - don't convert missing arguments to the requested parameter type + if( t != SbxVARIANT && !IsMissing( v, 1 ) && t != ( v->GetType() & 0x0FFF ) ) + { + if( p && (p->eType & SbxARRAY) ) + { + Error( ERRCODE_BASIC_CONVERSION ); + } + else + { + v->Convert( t ); + } + } + refParams->Put32( v, i ); + } + if( p ) + { + refParams->PutAlias32( p->aName, i ); + } + } + } + + // ParamArray for missing parameter + if( !pInfo ) + return; + + // #111897 Check first missing parameter for ParamArray + const SbxParamInfo* p = pInfo->GetParam(sal::static_int_cast<sal_uInt16>(nParamCount)); + if( p && (p->nUserData & PARAM_INFO_PARAMARRAY) != 0 ) + { + SbxDimArray* pArray = new SbxDimArray( SbxVARIANT ); + pArray->unoAddDim32( 0, -1 ); + SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT ); + pArrayVar->SetFlag( SbxFlagBits::ReadWrite ); + pArrayVar->PutObject( pArray ); + refParams->Put32( pArrayVar, nParamCount ); + } +} + + +// execute a P-Code + +bool SbiRuntime::Step() +{ + if( bRun ) + { + // in any case check casually! + if( !( ++nOps & 0xF ) && pInst->IsReschedule() ) + { + sal_uInt32 nTime = osl_getGlobalTimer(); + if (nTime - m_nLastTime > 5 ) // 20 ms + { + Application::Reschedule(); + m_nLastTime = nTime; + } + } + + // #i48868 blocked by next call level? + while( bBlocked ) + { + if( pInst->IsReschedule() ) + { + Application::Reschedule(); + } + } + + SbiOpcode eOp = static_cast<SbiOpcode>( *pCode++ ); + sal_uInt32 nOp1, nOp2; + if (eOp <= SbiOpcode::SbOP0_END) + { + (this->*( aStep0[ int(eOp) ] ) )(); + } + else if (eOp >= SbiOpcode::SbOP1_START && eOp <= SbiOpcode::SbOP1_END) + { + nOp1 = *pCode++; nOp1 |= *pCode++ << 8; nOp1 |= *pCode++ << 16; nOp1 |= *pCode++ << 24; + + (this->*( aStep1[ int(eOp) - int(SbiOpcode::SbOP1_START) ] ) )( nOp1 ); + } + else if (eOp >= SbiOpcode::SbOP2_START && eOp <= SbiOpcode::SbOP2_END) + { + nOp1 = *pCode++; nOp1 |= *pCode++ << 8; nOp1 |= *pCode++ << 16; nOp1 |= *pCode++ << 24; + nOp2 = *pCode++; nOp2 |= *pCode++ << 8; nOp2 |= *pCode++ << 16; nOp2 |= *pCode++ << 24; + (this->*( aStep2[ int(eOp) - int(SbiOpcode::SbOP2_START) ] ) )( nOp1, nOp2 ); + } + else + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + } + + ErrCode nErrCode = SbxBase::GetError(); + Error( nErrCode.IgnoreWarning() ); + + // from 13.2.1997, new error handling: + // ATTENTION: nError can be set already even if !nErrCode + // since nError can now also be set from other RT-instances + + if( nError ) + { + SbxBase::ResetError(); + } + + // from 15.3.96: display errors only if BASIC is still active + // (especially not after compiler errors at the runtime) + if( nError && bRun ) + { + ErrCode err = nError; + ClearExprStack(); + nError = ERRCODE_NONE; + pInst->nErr = err; + pInst->nErl = nLine; + pErrCode = pCode; + pErrStmnt = pStmnt; + // An error occurred in an error handler + // force parent handler ( if there is one ) + // to handle the error + bool bLetParentHandleThis = false; + + // in the error handler? so std-error + if ( !bInError ) + { + bInError = true; + + if( !bError ) // On Error Resume Next + { + StepRESUME( 1 ); + } + else if( pError ) // On Error Goto ... + { + pCode = pError; + } + else + { + bLetParentHandleThis = true; + } + } + else + { + bLetParentHandleThis = true; + pError = nullptr; //terminate the handler + } + if ( bLetParentHandleThis ) + { + // from 13.2.1997, new error handling: + // consider superior error handlers + + // there's no error handler -> find one farther above + SbiRuntime* pRtErrHdl = nullptr; + SbiRuntime* pRt = this; + while( (pRt = pRt->pNext) != nullptr ) + { + if( !pRt->bError || pRt->pError != nullptr ) + { + pRtErrHdl = pRt; + break; + } + } + + + if( pRtErrHdl ) + { + // manipulate all the RTs that are below in the call-stack + pRt = this; + do + { + pRt->nError = err; + if( pRt != pRtErrHdl ) + { + pRt->bRun = false; + } + else + { + break; + } + pRt = pRt->pNext; + } + while( pRt ); + } + // no error-hdl found -> old behaviour + else + { + pInst->Abort(); + } + } + } + } + return bRun; +} + +void SbiRuntime::Error( ErrCode n, bool bVBATranslationAlreadyDone ) +{ + if( !n ) + return; + + nError = n; + if( !(isVBAEnabled() && !bVBATranslationAlreadyDone) ) + return; + + OUString aMsg = pInst->GetErrorMsg(); + sal_Int32 nVBAErrorNumber = translateErrorToVba( nError, aMsg ); + SbxVariable* pSbxErrObjVar = SbxErrObject::getErrObject().get(); + SbxErrObject* pGlobErr = static_cast< SbxErrObject* >( pSbxErrObjVar ); + if( pGlobErr != nullptr ) + { + pGlobErr->setNumberAndDescription( nVBAErrorNumber, aMsg ); + } + pInst->aErrorMsg = aMsg; + nError = ERRCODE_BASIC_COMPAT; +} + +void SbiRuntime::Error( ErrCode _errCode, const OUString& _details ) +{ + if ( !_errCode ) + return; + + // Not correct for class module usage, remove for now + //OSL_WARN_IF( pInst->pRun != this, "basic", "SbiRuntime::Error: can't propagate the error message details!" ); + if ( pInst->pRun == this ) + { + pInst->Error( _errCode, _details ); + //OSL_WARN_IF( nError != _errCode, "basic", "SbiRuntime::Error: the instance is expected to propagate the error code back to me!" ); + } + else + { + nError = _errCode; + } +} + +void SbiRuntime::FatalError( ErrCode n ) +{ + StepSTDERROR(); + Error( n ); +} + +void SbiRuntime::FatalError( ErrCode _errCode, const OUString& _details ) +{ + StepSTDERROR(); + Error( _errCode, _details ); +} + +sal_Int32 SbiRuntime::translateErrorToVba( ErrCode nError, OUString& rMsg ) +{ + // If a message is defined use that ( in preference to + // the defined one for the error ) NB #TODO + // if there is an error defined it more than likely + // is not the one you want ( some are the same though ) + // we really need a new vba compatible error list + if ( rMsg.isEmpty() ) + { + StarBASIC::MakeErrorText( nError, rMsg ); + rMsg = StarBASIC::GetErrorText(); + if ( rMsg.isEmpty() ) // no message for err no, need localized resource here + { + rMsg = "Internal Object Error:"; + } + } + // no num? most likely then it *is* really a vba err + sal_uInt16 nVBErrorCode = StarBASIC::GetVBErrorCode( nError ); + sal_Int32 nVBAErrorNumber = ( nVBErrorCode == 0 ) ? sal_uInt32(nError) : nVBErrorCode; + return nVBAErrorNumber; +} + +// Stacks + +// The expression-stack is available for the continuous evaluation +// of expressions. + +void SbiRuntime::PushVar( SbxVariable* pVar ) +{ + if( pVar ) + { + refExprStk->Put32( pVar, nExprLvl++ ); + } +} + +SbxVariableRef SbiRuntime::PopVar() +{ +#ifdef DBG_UTIL + if( !nExprLvl ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + return new SbxVariable; + } +#endif + SbxVariableRef xVar = refExprStk->Get32( --nExprLvl ); + SAL_INFO_IF( xVar->GetName() == "Cells", "basic", "PopVar: Name equals 'Cells'" ); + // methods hold themselves in parameter 0 + if( dynamic_cast<const SbxMethod *>(xVar.get()) != nullptr ) + { + xVar->SetParameters(nullptr); + } + return xVar; +} + +void SbiRuntime::ClearExprStack() +{ + // Attention: Clear() doesn't suffice as methods must be deleted + while ( nExprLvl ) + { + PopVar(); + } + refExprStk->Clear(); +} + +// Take variable from the expression-stack without removing it +// n counts from 0 + +SbxVariable* SbiRuntime::GetTOS() +{ + short n = nExprLvl - 1; +#ifdef DBG_UTIL + if( n < 0 ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + return new SbxVariable; + } +#endif + return refExprStk->Get32( static_cast<sal_uInt32>(n) ); +} + + +void SbiRuntime::TOSMakeTemp() +{ + SbxVariable* p = refExprStk->Get32( nExprLvl - 1 ); + if ( p->GetType() == SbxEMPTY ) + { + p->Broadcast( SfxHintId::BasicDataWanted ); + } + + SbxVariable* pDflt = nullptr; + if ( bVBAEnabled && ( p->GetType() == SbxOBJECT || p->GetType() == SbxVARIANT ) && ((pDflt = getDefaultProp(p)) != nullptr) ) + { + pDflt->Broadcast( SfxHintId::BasicDataWanted ); + // replacing new p on stack causes object pointed by + // pDft->pParent to be deleted, when p2->Compute() is + // called below pParent is accessed (but it's deleted) + // so set it to NULL now + pDflt->SetParent( nullptr ); + p = new SbxVariable( *pDflt ); + p->SetFlag( SbxFlagBits::ReadWrite ); + refExprStk->Put32( p, nExprLvl - 1 ); + } + else if( p->GetRefCount() != 1 ) + { + SbxVariable* pNew = new SbxVariable( *p ); + pNew->SetFlag( SbxFlagBits::ReadWrite ); + refExprStk->Put32( pNew, nExprLvl - 1 ); + } +} + +// the GOSUB-stack collects return-addresses for GOSUBs +void SbiRuntime::PushGosub( const sal_uInt8* pc ) +{ + if( pGosubStk.size() >= MAXRECURSION ) + { + StarBASIC::FatalError( ERRCODE_BASIC_STACK_OVERFLOW ); + } + pGosubStk.emplace_back(pc, nForLvl); +} + +void SbiRuntime::PopGosub() +{ + if( pGosubStk.empty() ) + { + Error( ERRCODE_BASIC_NO_GOSUB ); + } + else + { + pCode = pGosubStk.back().pCode; + pGosubStk.pop_back(); + } +} + +// the Argv-stack collects current argument-vectors + +void SbiRuntime::PushArgv() +{ + pArgvStk.emplace_back(refArgv, nArgc); + nArgc = 1; + refArgv.clear(); +} + +void SbiRuntime::PopArgv() +{ + if( !pArgvStk.empty() ) + { + refArgv = pArgvStk.back().refArgv; + nArgc = pArgvStk.back().nArgc; + pArgvStk.pop_back(); + } +} + + +void SbiRuntime::ClearArgvStack() +{ + while( !pArgvStk.empty() ) + { + PopArgv(); + } +} + +// Push of the for-stack. The stack has increment, end, begin and variable. +// After the creation of the stack-element the stack's empty. + +void SbiRuntime::PushFor() +{ + SbiForStack* p = new SbiForStack; + p->eForType = ForType::To; + p->pNext = pForStk; + pForStk = p; + + p->refInc = PopVar(); + p->refEnd = PopVar(); + SbxVariableRef xBgn = PopVar(); + p->refVar = PopVar(); + *(p->refVar) = *xBgn; + nForLvl++; +} + +void SbiRuntime::PushForEach() +{ + SbiForStack* p = new SbiForStack; + // Set default value in case of error which is ignored in Resume Next + p->eForType = ForType::EachArray; + p->pNext = pForStk; + pForStk = p; + + SbxVariableRef xObjVar = PopVar(); + SbxBase* pObj = xObjVar && xObjVar->GetFullType() == SbxOBJECT ? xObjVar->GetObject() : nullptr; + + if (SbxDimArray* pArray = dynamic_cast<SbxDimArray*>(pObj)) + { + p->refEnd = reinterpret_cast<SbxVariable*>(pArray); + + sal_Int32 nDims = pArray->GetDims32(); + p->pArrayLowerBounds.reset( new sal_Int32[nDims] ); + p->pArrayUpperBounds.reset( new sal_Int32[nDims] ); + p->pArrayCurIndices.reset( new sal_Int32[nDims] ); + sal_Int32 lBound, uBound; + for( sal_Int32 i = 0 ; i < nDims ; i++ ) + { + pArray->GetDim32( i+1, lBound, uBound ); + p->pArrayCurIndices[i] = p->pArrayLowerBounds[i] = lBound; + p->pArrayUpperBounds[i] = uBound; + } + } + else if (BasicCollection* pCollection = dynamic_cast<BasicCollection*>(pObj)) + { + p->eForType = ForType::EachCollection; + p->refEnd = pCollection; + p->nCurCollectionIndex = 0; + } + else if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>(pObj)) + { + // XEnumerationAccess? + Any aAny = pUnoObj->getUnoAny(); + Reference< XEnumerationAccess > xEnumerationAccess; + if( aAny >>= xEnumerationAccess ) + { + p->xEnumeration = xEnumerationAccess->createEnumeration(); + p->eForType = ForType::EachXEnumeration; + } + else if ( isVBAEnabled() && pUnoObj->isNativeCOMObject() ) + { + uno::Reference< script::XInvocation > xInvocation; + if ( ( aAny >>= xInvocation ) && xInvocation.is() ) + { + try + { + p->xEnumeration = new ComEnumerationWrapper( xInvocation ); + p->eForType = ForType::EachXEnumeration; + } + catch(const uno::Exception& ) + {} + } + } + } + + // Container variable + p->refVar = PopVar(); + nForLvl++; +} + + +void SbiRuntime::PopFor() +{ + if( pForStk ) + { + SbiForStack* p = pForStk; + pForStk = p->pNext; + delete p; + nForLvl--; + } +} + + +void SbiRuntime::ClearForStack() +{ + while( pForStk ) + { + PopFor(); + } +} + +SbiForStack* SbiRuntime::FindForStackItemForCollection( class BasicCollection const * pCollection ) +{ + for (SbiForStack *p = pForStk; p; p = p->pNext) + { + SbxVariable* pVar = p->refEnd.is() ? p->refEnd.get() : nullptr; + if( p->eForType == ForType::EachCollection + && pVar != nullptr + && dynamic_cast<BasicCollection*>( pVar) == pCollection ) + { + return p; + } + } + + return nullptr; +} + + +// DLL-calls + +void SbiRuntime::DllCall + ( const OUString& aFuncName, + const OUString& aDLLName, + SbxArray* pArgs, // parameter (from index 1, can be NULL) + SbxDataType eResType, // return value + bool bCDecl ) // true: according to C-conventions +{ + // NOT YET IMPLEMENTED + + SbxVariable* pRes = new SbxVariable( eResType ); + SbiDllMgr* pDllMgr = pInst->GetDllMgr(); + ErrCode nErr = pDllMgr->Call( aFuncName, aDLLName, pArgs, *pRes, bCDecl ); + if( nErr ) + { + Error( nErr ); + } + PushVar( pRes ); +} + +bool SbiRuntime::IsImageFlag( SbiImageFlags n ) const +{ + return pImg->IsFlag( n ); +} + +sal_uInt16 SbiRuntime::GetBase() const +{ + return pImg->GetBase(); +} + +void SbiRuntime::StepNOP() +{} + +void SbiRuntime::StepArith( SbxOperator eOp ) +{ + SbxVariableRef p1 = PopVar(); + TOSMakeTemp(); + SbxVariable* p2 = GetTOS(); + + p2->ResetFlag( SbxFlagBits::Fixed ); + p2->Compute( eOp, *p1 ); + + checkArithmeticOverflow( p2 ); +} + +void SbiRuntime::StepUnary( SbxOperator eOp ) +{ + TOSMakeTemp(); + SbxVariable* p = GetTOS(); + p->Compute( eOp, *p ); +} + +void SbiRuntime::StepCompare( SbxOperator eOp ) +{ + SbxVariableRef p1 = PopVar(); + SbxVariableRef p2 = PopVar(); + + // Make sure objects with default params have + // values ( and type ) set as appropriate + SbxDataType p1Type = p1->GetType(); + SbxDataType p2Type = p2->GetType(); + if ( p1Type == SbxEMPTY ) + { + p1->Broadcast( SfxHintId::BasicDataWanted ); + p1Type = p1->GetType(); + } + if ( p2Type == SbxEMPTY ) + { + p2->Broadcast( SfxHintId::BasicDataWanted ); + p2Type = p2->GetType(); + } + if ( p1Type == p2Type ) + { + // if both sides are an object and have default props + // then we need to use the default props + // we don't need to worry if only one side ( lhs, rhs ) is an + // object ( object side will get coerced to correct type in + // Compare ) + if ( p1Type == SbxOBJECT ) + { + SbxVariable* pDflt = getDefaultProp( p1.get() ); + if ( pDflt ) + { + p1 = pDflt; + p1->Broadcast( SfxHintId::BasicDataWanted ); + } + pDflt = getDefaultProp( p2.get() ); + if ( pDflt ) + { + p2 = pDflt; + p2->Broadcast( SfxHintId::BasicDataWanted ); + } + } + + } + static SbxVariable* pTRUE = nullptr; + static SbxVariable* pFALSE = nullptr; + // why do this on non-windows ? + // why do this at all ? + // I dumbly follow the pattern :-/ + if ( bVBAEnabled && ( p1->IsNull() || p2->IsNull() ) ) + { + static SbxVariable* pNULL = [&]() { + SbxVariable* p = new SbxVariable; + p->PutNull(); + p->AddFirstRef(); + return p; + }(); + PushVar( pNULL ); + } + else if( p2->Compare( eOp, *p1 ) ) + { + if( !pTRUE ) + { + pTRUE = new SbxVariable; + pTRUE->PutBool( true ); + pTRUE->AddFirstRef(); + } + PushVar( pTRUE ); + } + else + { + if( !pFALSE ) + { + pFALSE = new SbxVariable; + pFALSE->PutBool( false ); + pFALSE->AddFirstRef(); + } + PushVar( pFALSE ); + } +} + +void SbiRuntime::StepEXP() { StepArith( SbxEXP ); } +void SbiRuntime::StepMUL() { StepArith( SbxMUL ); } +void SbiRuntime::StepDIV() { StepArith( SbxDIV ); } +void SbiRuntime::StepIDIV() { StepArith( SbxIDIV ); } +void SbiRuntime::StepMOD() { StepArith( SbxMOD ); } +void SbiRuntime::StepPLUS() { StepArith( SbxPLUS ); } +void SbiRuntime::StepMINUS() { StepArith( SbxMINUS ); } +void SbiRuntime::StepCAT() { StepArith( SbxCAT ); } +void SbiRuntime::StepAND() { StepArith( SbxAND ); } +void SbiRuntime::StepOR() { StepArith( SbxOR ); } +void SbiRuntime::StepXOR() { StepArith( SbxXOR ); } +void SbiRuntime::StepEQV() { StepArith( SbxEQV ); } +void SbiRuntime::StepIMP() { StepArith( SbxIMP ); } + +void SbiRuntime::StepNEG() { StepUnary( SbxNEG ); } +void SbiRuntime::StepNOT() { StepUnary( SbxNOT ); } + +void SbiRuntime::StepEQ() { StepCompare( SbxEQ ); } +void SbiRuntime::StepNE() { StepCompare( SbxNE ); } +void SbiRuntime::StepLT() { StepCompare( SbxLT ); } +void SbiRuntime::StepGT() { StepCompare( SbxGT ); } +void SbiRuntime::StepLE() { StepCompare( SbxLE ); } +void SbiRuntime::StepGE() { StepCompare( SbxGE ); } + +namespace +{ + bool NeedEsc(sal_Unicode cCode) + { + if(!rtl::isAscii(cCode)) + { + return false; + } + switch(cCode) + { + case '.': + case '^': + case '$': + case '+': + case '\\': + case '|': + case '{': + case '}': + case '(': + case ')': + return true; + default: + return false; + } + } + + OUString VBALikeToRegexp(const OUString &rIn) + { + OUStringBuffer sResult; + const sal_Unicode *start = rIn.getStr(); + const sal_Unicode *end = start + rIn.getLength(); + + int seenright = 0; + + sResult.append('^'); + + while (start < end) + { + switch (*start) + { + case '?': + sResult.append('.'); + start++; + break; + case '*': + sResult.append(".*"); + start++; + break; + case '#': + sResult.append("[0-9]"); + start++; + break; + case ']': + sResult.append('\\'); + sResult.append(*start++); + break; + case '[': + sResult.append(*start++); + seenright = 0; + while (start < end && !seenright) + { + switch (*start) + { + case '[': + case '?': + case '*': + sResult.append('\\'); + sResult.append(*start); + break; + case ']': + sResult.append(*start); + seenright = 1; + break; + case '!': + sResult.append('^'); + break; + default: + if (NeedEsc(*start)) + { + sResult.append('\\'); + } + sResult.append(*start); + break; + } + start++; + } + break; + default: + if (NeedEsc(*start)) + { + sResult.append('\\'); + } + sResult.append(*start++); + } + } + + sResult.append('$'); + + return sResult.makeStringAndClear(); + } +} + +void SbiRuntime::StepLIKE() +{ + SbxVariableRef refVar1 = PopVar(); + SbxVariableRef refVar2 = PopVar(); + + OUString pattern = VBALikeToRegexp(refVar1->GetOUString()); + OUString value = refVar2->GetOUString(); + + i18nutil::SearchOptions2 aSearchOpt; + + aSearchOpt.AlgorithmType2 = css::util::SearchAlgorithms2::REGEXP; + + aSearchOpt.Locale = Application::GetSettings().GetLanguageTag().getLocale(); + aSearchOpt.searchString = pattern; + + bool bTextMode(true); + bool bCompatibility = ( GetSbData()->pInst && GetSbData()->pInst->IsCompatibility() ); + if( bCompatibility ) + { + bTextMode = IsImageFlag( SbiImageFlags::COMPARETEXT ); + } + if( bTextMode ) + { + aSearchOpt.transliterateFlags |= TransliterationFlags::IGNORE_CASE; + } + SbxVariable* pRes = new SbxVariable; + utl::TextSearch aSearch( aSearchOpt); + sal_Int32 nStart=0, nEnd=value.getLength(); + bool bRes = aSearch.SearchForward(value, &nStart, &nEnd); + pRes->PutBool( bRes ); + + PushVar( pRes ); +} + +// TOS and TOS-1 are both object variables and contain the same pointer + +void SbiRuntime::StepIS() +{ + SbxVariableRef refVar1 = PopVar(); + SbxVariableRef refVar2 = PopVar(); + + SbxDataType eType1 = refVar1->GetType(); + SbxDataType eType2 = refVar2->GetType(); + if ( eType1 == SbxEMPTY ) + { + refVar1->Broadcast( SfxHintId::BasicDataWanted ); + eType1 = refVar1->GetType(); + } + if ( eType2 == SbxEMPTY ) + { + refVar2->Broadcast( SfxHintId::BasicDataWanted ); + eType2 = refVar2->GetType(); + } + + bool bRes = ( eType1 == SbxOBJECT && eType2 == SbxOBJECT ); + if ( bVBAEnabled && !bRes ) + { + Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT ); + } + bRes = ( bRes && refVar1->GetObject() == refVar2->GetObject() ); + SbxVariable* pRes = new SbxVariable; + pRes->PutBool( bRes ); + PushVar( pRes ); +} + +// update the value of TOS + +void SbiRuntime::StepGET() +{ + SbxVariable* p = GetTOS(); + p->Broadcast( SfxHintId::BasicDataWanted ); +} + +// #67607 copy Uno-Structs +static bool checkUnoStructCopy( bool bVBA, SbxVariableRef const & refVal, SbxVariableRef const & refVar ) +{ + SbxDataType eVarType = refVar->GetType(); + SbxDataType eValType = refVal->GetType(); + + if ( ( bVBA && ( eVarType == SbxEMPTY ) ) || !refVar->CanWrite() ) + return false; + + if ( eValType != SbxOBJECT ) + return false; + // we seem to be duplicating parts of SbxValue=operator, maybe we should just move this to + // there :-/ not sure if for every '=' we would want struct handling + if( eVarType != SbxOBJECT ) + { + if ( refVar->IsFixed() ) + return false; + } + // #115826: Exclude ProcedureProperties to avoid call to Property Get procedure + else if( dynamic_cast<const SbProcedureProperty*>( refVar.get() ) != nullptr ) + return false; + + SbxObjectRef xValObj = static_cast<SbxObject*>(refVal->GetObject()); + if( !xValObj.is() || dynamic_cast<const SbUnoAnyObject*>( xValObj.get() ) != nullptr ) + return false; + + SbUnoObject* pUnoVal = dynamic_cast<SbUnoObject*>( xValObj.get() ); + SbUnoStructRefObject* pUnoStructVal = dynamic_cast<SbUnoStructRefObject*>( xValObj.get() ); + Any aAny; + // make doubly sure value is either a Uno object or + // a uno struct + if ( pUnoVal || pUnoStructVal ) + aAny = pUnoVal ? pUnoVal->getUnoAny() : pUnoStructVal->getUnoAny(); + else + return false; + if ( aAny.getValueType().getTypeClass() == TypeClass_STRUCT ) + { + refVar->SetType( SbxOBJECT ); + ErrCode eOldErr = SbxBase::GetError(); + // There are some circumstances when calling GetObject + // will trigger an error, we need to squash those here. + // Alternatively it is possible that the same scenario + // could overwrite and existing error. Lets prevent that + SbxObjectRef xVarObj = static_cast<SbxObject*>(refVar->GetObject()); + if ( eOldErr != ERRCODE_NONE ) + SbxBase::SetError( eOldErr ); + else + SbxBase::ResetError(); + + SbUnoStructRefObject* pUnoStructObj = dynamic_cast<SbUnoStructRefObject*>( xVarObj.get() ); + + OUString sClassName = pUnoVal ? pUnoVal->GetClassName() : pUnoStructVal->GetClassName(); + OUString sName = pUnoVal ? pUnoVal->GetName() : pUnoStructVal->GetName(); + + if ( pUnoStructObj ) + { + StructRefInfo aInfo = pUnoStructObj->getStructInfo(); + aInfo.setValue( aAny ); + } + else + { + SbUnoObject* pNewUnoObj = new SbUnoObject( sName, aAny ); + // #70324: adopt ClassName + pNewUnoObj->SetClassName( sClassName ); + refVar->PutObject( pNewUnoObj ); + } + return true; + } + return false; +} + + +// laying down TOS in TOS-1 + +void SbiRuntime::StepPUT() +{ + SbxVariableRef refVal = PopVar(); + SbxVariableRef refVar = PopVar(); + // store on its own method (inside a function)? + bool bFlagsChanged = false; + SbxFlagBits n = SbxFlagBits::NONE; + if( refVar.get() == pMeth ) + { + bFlagsChanged = true; + n = refVar->GetFlags(); + refVar->SetFlag( SbxFlagBits::Write ); + } + + // if left side arg is an object or variant and right handside isn't + // either an object or a variant then try and see if a default + // property exists. + // to use e.g. Range{"A1") = 34 + // could equate to Range("A1").Value = 34 + if ( bVBAEnabled ) + { + // yet more hacking at this, I feel we don't quite have the correct + // heuristics for dealing with obj1 = obj2 ( where obj2 ( and maybe + // obj1 ) has default member/property ) ) It seems that default props + // aren't dealt with if the object is a member of some parent object + bool bObjAssign = false; + if ( refVar->GetType() == SbxEMPTY ) + refVar->Broadcast( SfxHintId::BasicDataWanted ); + if ( refVar->GetType() == SbxOBJECT ) + { + if ( dynamic_cast<const SbxMethod *>(refVar.get()) != nullptr || ! refVar->GetParent() ) + { + SbxVariable* pDflt = getDefaultProp( refVar.get() ); + + if ( pDflt ) + refVar = pDflt; + } + else + bObjAssign = true; + } + if ( refVal->GetType() == SbxOBJECT && !bObjAssign && ( dynamic_cast<const SbxMethod *>(refVal.get()) != nullptr || ! refVal->GetParent() ) ) + { + SbxVariable* pDflt = getDefaultProp( refVal.get() ); + if ( pDflt ) + refVal = pDflt; + } + } + + if ( !checkUnoStructCopy( bVBAEnabled, refVal, refVar ) ) + *refVar = *refVal; + + if( bFlagsChanged ) + refVar->SetFlags( n ); +} + +namespace { + +// VBA Dim As New behavior handling, save init object information +struct DimAsNewRecoverItem +{ + OUString m_aObjClass; + OUString m_aObjName; + SbxObject* m_pObjParent; + SbModule* m_pClassModule; + + DimAsNewRecoverItem() + : m_pObjParent( nullptr ) + , m_pClassModule( nullptr ) + {} + + DimAsNewRecoverItem( const OUString& rObjClass, const OUString& rObjName, + SbxObject* pObjParent, SbModule* pClassModule ) + : m_aObjClass( rObjClass ) + , m_aObjName( rObjName ) + , m_pObjParent( pObjParent ) + , m_pClassModule( pClassModule ) + {} + +}; + + +struct SbxVariablePtrHash +{ + size_t operator()( SbxVariable* pVar ) const + { return reinterpret_cast<size_t>(pVar); } +}; + +} + +typedef std::unordered_map< SbxVariable*, DimAsNewRecoverItem, + SbxVariablePtrHash > DimAsNewRecoverHash; + +namespace { + +class GaDimAsNewRecoverHash : public rtl::Static<DimAsNewRecoverHash, GaDimAsNewRecoverHash> {}; + +} + +void removeDimAsNewRecoverItem( SbxVariable* pVar ) +{ + DimAsNewRecoverHash &rDimAsNewRecoverHash = GaDimAsNewRecoverHash::get(); + DimAsNewRecoverHash::iterator it = rDimAsNewRecoverHash.find( pVar ); + if( it != rDimAsNewRecoverHash.end() ) + { + rDimAsNewRecoverHash.erase( it ); + } +} + + +// saving object variable +// not-object variables will cause errors + +static const char pCollectionStr[] = "Collection"; + +void SbiRuntime::StepSET_Impl( SbxVariableRef& refVal, SbxVariableRef& refVar, bool bHandleDefaultProp ) +{ + // #67733 types with array-flag are OK too + + // Check var, !object is no error for sure if, only if type is fixed + SbxDataType eVarType = refVar->GetType(); + if( !bHandleDefaultProp && eVarType != SbxOBJECT && !(eVarType & SbxARRAY) && refVar->IsFixed() ) + { + Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT ); + return; + } + + // Check value, !object is no error for sure if, only if type is fixed + SbxDataType eValType = refVal->GetType(); + if( !bHandleDefaultProp && eValType != SbxOBJECT && !(eValType & SbxARRAY) && refVal->IsFixed() ) + { + Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT ); + return; + } + + // Getting in here causes problems with objects with default properties + // if they are SbxEMPTY I guess + if ( !bHandleDefaultProp || eValType == SbxOBJECT ) + { + // activate GetObject for collections on refVal + SbxBase* pObjVarObj = refVal->GetObject(); + if( pObjVarObj ) + { + SbxVariableRef refObjVal = dynamic_cast<SbxObject*>( pObjVarObj ); + + if( refObjVal.is() ) + { + refVal = refObjVal; + } + else if( !(eValType & SbxARRAY) ) + { + refVal = nullptr; + } + } + } + + // #52896 refVal can be invalid here, if uno-sequences - or more + // general arrays - are assigned to variables that are declared + // as an object! + if( !refVal.is() ) + { + Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT ); + } + else + { + bool bFlagsChanged = false; + SbxFlagBits n = SbxFlagBits::NONE; + if( refVar.get() == pMeth ) + { + bFlagsChanged = true; + n = refVar->GetFlags(); + refVar->SetFlag( SbxFlagBits::Write ); + } + SbProcedureProperty* pProcProperty = dynamic_cast<SbProcedureProperty*>( refVar.get() ); + if( pProcProperty ) + { + pProcProperty->setSet( true ); + } + if ( bHandleDefaultProp ) + { + // get default properties for lhs & rhs where necessary + // SbxVariable* defaultProp = NULL; unused variable + // LHS try determine if a default prop exists + // again like in StepPUT (see there too ) we are tweaking the + // heuristics again for when to assign an object reference or + // use default members if they exist + // #FIXME we really need to get to the bottom of this mess + bool bObjAssign = false; + if ( refVar->GetType() == SbxOBJECT ) + { + if ( dynamic_cast<const SbxMethod *>(refVar.get()) != nullptr || ! refVar->GetParent() ) + { + SbxVariable* pDflt = getDefaultProp( refVar.get() ); + if ( pDflt ) + { + refVar = pDflt; + } + } + else + bObjAssign = true; + } + // RHS only get a default prop is the rhs has one + if ( refVal->GetType() == SbxOBJECT ) + { + // check if lhs is a null object + // if it is then use the object not the default property + SbxObject* pObj = dynamic_cast<SbxObject*>( refVar.get() ); + + // calling GetObject on a SbxEMPTY variable raises + // object not set errors, make sure it's an Object + if ( !pObj && refVar->GetType() == SbxOBJECT ) + { + SbxBase* pObjVarObj = refVar->GetObject(); + pObj = dynamic_cast<SbxObject*>( pObjVarObj ); + } + SbxVariable* pDflt = nullptr; + if ( pObj && !bObjAssign ) + { + // lhs is either a valid object || or has a defaultProp + pDflt = getDefaultProp( refVal.get() ); + } + if ( pDflt ) + { + refVal = pDflt; + } + } + } + + // Handle Dim As New + bool bDimAsNew = bVBAEnabled && refVar->IsSet( SbxFlagBits::DimAsNew ); + SbxBaseRef xPrevVarObj; + if( bDimAsNew ) + { + xPrevVarObj = refVar->GetObject(); + } + // Handle withevents + bool bWithEvents = refVar->IsSet( SbxFlagBits::WithEvents ); + if ( bWithEvents ) + { + Reference< XInterface > xComListener; + + SbxBase* pObj = refVal->GetObject(); + SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pObj ); + if( pUnoObj != nullptr ) + { + Any aControlAny = pUnoObj->getUnoAny(); + OUString aDeclareClassName = refVar->GetDeclareClassName(); + OUString aPrefix = refVar->GetName(); + SbxObjectRef xScopeObj = refVar->GetParent(); + xComListener = createComListener( aControlAny, aDeclareClassName, aPrefix, xScopeObj ); + + refVal->SetDeclareClassName( aDeclareClassName ); + refVal->SetComListener( xComListener, &rBasic ); // Hold reference + } + + } + + // lhs is a property who's value is currently (Empty e.g. no broadcast yet) + // in this case if there is a default prop involved the value of the + // default property may in fact be void so the type will also be SbxEMPTY + // in this case we do not want to call checkUnoStructCopy 'cause that will + // cause an error also + if ( !checkUnoStructCopy( bHandleDefaultProp, refVal, refVar ) ) + { + *refVar = *refVal; + } + if ( bDimAsNew ) + { + if( dynamic_cast<const SbxObject*>( refVar.get() ) == nullptr ) + { + SbxBase* pValObjBase = refVal->GetObject(); + if( pValObjBase == nullptr ) + { + if( xPrevVarObj.is() ) + { + // Object is overwritten with NULL, instantiate init object + DimAsNewRecoverHash &rDimAsNewRecoverHash = GaDimAsNewRecoverHash::get(); + DimAsNewRecoverHash::iterator it = rDimAsNewRecoverHash.find( refVar.get() ); + if( it != rDimAsNewRecoverHash.end() ) + { + const DimAsNewRecoverItem& rItem = it->second; + if( rItem.m_pClassModule != nullptr ) + { + SbClassModuleObject* pNewObj = new SbClassModuleObject( rItem.m_pClassModule ); + pNewObj->SetName( rItem.m_aObjName ); + pNewObj->SetParent( rItem.m_pObjParent ); + refVar->PutObject( pNewObj ); + } + else if( rItem.m_aObjClass.equalsIgnoreAsciiCase( pCollectionStr ) ) + { + BasicCollection* pNewCollection = new BasicCollection( pCollectionStr ); + pNewCollection->SetName( rItem.m_aObjName ); + pNewCollection->SetParent( rItem.m_pObjParent ); + refVar->PutObject( pNewCollection ); + } + } + } + } + else + { + // Does old value exist? + bool bFirstInit = !xPrevVarObj.is(); + if( bFirstInit ) + { + // Store information to instantiate object later + SbxObject* pValObj = dynamic_cast<SbxObject*>( pValObjBase ); + if( pValObj != nullptr ) + { + OUString aObjClass = pValObj->GetClassName(); + + SbClassModuleObject* pClassModuleObj = dynamic_cast<SbClassModuleObject*>( pValObjBase ); + DimAsNewRecoverHash &rDimAsNewRecoverHash = GaDimAsNewRecoverHash::get(); + if( pClassModuleObj != nullptr ) + { + SbModule* pClassModule = pClassModuleObj->getClassModule(); + rDimAsNewRecoverHash[refVar.get()] = + DimAsNewRecoverItem( aObjClass, pValObj->GetName(), pValObj->GetParent(), pClassModule ); + } + else if( aObjClass.equalsIgnoreAsciiCase( "Collection" ) ) + { + rDimAsNewRecoverHash[refVar.get()] = + DimAsNewRecoverItem( aObjClass, pValObj->GetName(), pValObj->GetParent(), nullptr ); + } + } + } + } + } + } + + if( bFlagsChanged ) + { + refVar->SetFlags( n ); + } + } +} + +void SbiRuntime::StepSET() +{ + SbxVariableRef refVal = PopVar(); + SbxVariableRef refVar = PopVar(); + StepSET_Impl( refVal, refVar, bVBAEnabled ); // this is really assignment +} + +void SbiRuntime::StepVBASET() +{ + SbxVariableRef refVal = PopVar(); + SbxVariableRef refVar = PopVar(); + // don't handle default property + StepSET_Impl( refVal, refVar ); // set obj = something +} + + +void SbiRuntime::StepLSET() +{ + SbxVariableRef refVal = PopVar(); + SbxVariableRef refVar = PopVar(); + if( refVar->GetType() != SbxSTRING || + refVal->GetType() != SbxSTRING ) + { + Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT ); + } + else + { + SbxFlagBits n = refVar->GetFlags(); + if( refVar.get() == pMeth ) + { + refVar->SetFlag( SbxFlagBits::Write ); + } + OUString aRefVarString = refVar->GetOUString(); + OUString aRefValString = refVal->GetOUString(); + + sal_Int32 nVarStrLen = aRefVarString.getLength(); + sal_Int32 nValStrLen = aRefValString.getLength(); + OUString aNewStr; + if( nVarStrLen > nValStrLen ) + { + OUStringBuffer buf(aRefValString); + comphelper::string::padToLength(buf, nVarStrLen, ' '); + aNewStr = buf.makeStringAndClear(); + } + else + { + aNewStr = aRefValString.copy( 0, nVarStrLen ); + } + + refVar->PutString(aNewStr); + refVar->SetFlags( n ); + } +} + +void SbiRuntime::StepRSET() +{ + SbxVariableRef refVal = PopVar(); + SbxVariableRef refVar = PopVar(); + if( refVar->GetType() != SbxSTRING || refVal->GetType() != SbxSTRING ) + { + Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT ); + } + else + { + SbxFlagBits n = refVar->GetFlags(); + if( refVar.get() == pMeth ) + { + refVar->SetFlag( SbxFlagBits::Write ); + } + OUString aRefVarString = refVar->GetOUString(); + OUString aRefValString = refVal->GetOUString(); + sal_Int32 nVarStrLen = aRefVarString.getLength(); + sal_Int32 nValStrLen = aRefValString.getLength(); + + OUStringBuffer aNewStr(nVarStrLen); + if (nVarStrLen > nValStrLen) + { + comphelper::string::padToLength(aNewStr, nVarStrLen - nValStrLen, ' '); + aNewStr.append(aRefValString); + } + else + { + aNewStr.append(std::u16string_view(aRefValString).substr(0, nVarStrLen)); + } + refVar->PutString(aNewStr.makeStringAndClear()); + + refVar->SetFlags( n ); + } +} + +// laying down TOS in TOS-1, then set ReadOnly-Bit + +void SbiRuntime::StepPUTC() +{ + SbxVariableRef refVal = PopVar(); + SbxVariableRef refVar = PopVar(); + refVar->SetFlag( SbxFlagBits::Write ); + *refVar = *refVal; + refVar->ResetFlag( SbxFlagBits::Write ); + refVar->SetFlag( SbxFlagBits::Const ); +} + +// DIM +// TOS = variable for the array with dimension information as parameter + +void SbiRuntime::StepDIM() +{ + SbxVariableRef refVar = PopVar(); + DimImpl( refVar ); +} + +// #56204 swap out DIM-functionality into a help method (step0.cxx) +void SbiRuntime::DimImpl(const SbxVariableRef& refVar) +{ + // If refDim then this DIM statement is terminating a ReDIM and + // previous StepERASE_CLEAR for an array, the following actions have + // been delayed from ( StepERASE_CLEAR ) 'till here + if ( refRedim.is() ) + { + if ( !refRedimpArray.is() ) // only erase the array not ReDim Preserve + { + lcl_eraseImpl( refVar, bVBAEnabled ); + } + SbxDataType eType = refVar->GetType(); + lcl_clearImpl( refVar, eType ); + refRedim = nullptr; + } + SbxArray* pDims = refVar->GetParameters(); + // must have an even number of arguments + // have in mind that Arg[0] does not count! + if( pDims && !( pDims->Count32() & 1 ) ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + } + else + { + SbxDataType eType = refVar->IsFixed() ? refVar->GetType() : SbxVARIANT; + SbxDimArray* pArray = new SbxDimArray( eType ); + // allow arrays without dimension information, too (VB-compatible) + if( pDims ) + { + refVar->ResetFlag( SbxFlagBits::VarToDim ); + + for( sal_uInt32 i = 1; i < pDims->Count32(); ) + { + sal_Int32 lb = pDims->Get32( i++ )->GetLong(); + sal_Int32 ub = pDims->Get32( i++ )->GetLong(); + if( ub < lb ) + { + Error( ERRCODE_BASIC_OUT_OF_RANGE ); + ub = lb; + } + pArray->AddDim32( lb, ub ); + if ( lb != ub ) + { + pArray->setHasFixedSize( true ); + } + } + } + else + { + // #62867 On creating an array of the length 0, create + // a dimension (like for Uno-Sequences of the length 0) + pArray->unoAddDim32( 0, -1 ); + } + SbxFlagBits nSavFlags = refVar->GetFlags(); + refVar->ResetFlag( SbxFlagBits::Fixed ); + refVar->PutObject( pArray ); + refVar->SetFlags( nSavFlags ); + refVar->SetParameters( nullptr ); + } +} + +// REDIM +// TOS = variable for the array +// argv = dimension information + +void SbiRuntime::StepREDIM() +{ + // Nothing different than dim at the moment because + // a double dim is already recognized by the compiler. + StepDIM(); +} + + +// Helper function for StepREDIMP and StepDCREATE_IMPL / bRedimp = true +static void implCopyDimArray( SbxDimArray* pNewArray, SbxDimArray* pOldArray, sal_Int32 nMaxDimIndex, + sal_Int32 nActualDim, sal_Int32* pActualIndices, sal_Int32* pLowerBounds, sal_Int32* pUpperBounds ) +{ + sal_Int32& ri = pActualIndices[nActualDim]; + for( ri = pLowerBounds[nActualDim] ; ri <= pUpperBounds[nActualDim] ; ri++ ) + { + if( nActualDim < nMaxDimIndex ) + { + implCopyDimArray( pNewArray, pOldArray, nMaxDimIndex, nActualDim + 1, + pActualIndices, pLowerBounds, pUpperBounds ); + } + else + { + SbxVariable* pSource = pOldArray->Get32( pActualIndices ); + if (pSource && pOldArray->GetRefCount() > 1) + // tdf#134692: old array will stay alive after the redim - we need to copy deep + pSource = new SbxVariable(*pSource); + pNewArray->Put32(pSource, pActualIndices); + } + } +} + +// Returns true when actually restored +static bool implRestorePreservedArray(SbxDimArray* pNewArray, SbxArrayRef& rrefRedimpArray, bool* pbWasError = nullptr) +{ + assert(pNewArray); + bool bResult = false; + if (pbWasError) + *pbWasError = false; + if (rrefRedimpArray) + { + SbxDimArray* pOldArray = static_cast<SbxDimArray*>(rrefRedimpArray.get()); + const sal_Int32 nDimsNew = pNewArray->GetDims32(); + const sal_Int32 nDimsOld = pOldArray->GetDims32(); + + if (nDimsOld != nDimsNew) + { + StarBASIC::Error(ERRCODE_BASIC_OUT_OF_RANGE); + if (pbWasError) + *pbWasError = true; + } + else if (nDimsNew > 0) + { + // Store dims to use them for copying later + std::unique_ptr<sal_Int32[]> pLowerBounds(new sal_Int32[nDimsNew]); + std::unique_ptr<sal_Int32[]> pUpperBounds(new sal_Int32[nDimsNew]); + std::unique_ptr<sal_Int32[]> pActualIndices(new sal_Int32[nDimsNew]); + bool bNeedsPreallocation = true; + + // Compare bounds + for (sal_Int32 i = 1; i <= nDimsNew; i++) + { + sal_Int32 lBoundNew, uBoundNew; + sal_Int32 lBoundOld, uBoundOld; + pNewArray->GetDim32(i, lBoundNew, uBoundNew); + pOldArray->GetDim32(i, lBoundOld, uBoundOld); + lBoundNew = std::max(lBoundNew, lBoundOld); + uBoundNew = std::min(uBoundNew, uBoundOld); + sal_Int32 j = i - 1; + pActualIndices[j] = pLowerBounds[j] = lBoundNew; + pUpperBounds[j] = uBoundNew; + if (lBoundNew > uBoundNew) // No elements in the dimension -> no elements to restore + bNeedsPreallocation = false; + } + + // Optimization: pre-allocate underlying container + if (bNeedsPreallocation) + pNewArray->Put32(nullptr, pUpperBounds.get()); + + // Copy data from old array by going recursively through all dimensions + // (It would be faster to work on the flat internal data array of an + // SbyArray but this solution is clearer and easier) + implCopyDimArray(pNewArray, pOldArray, nDimsNew - 1, 0, pActualIndices.get(), + pLowerBounds.get(), pUpperBounds.get()); + bResult = true; + } + + rrefRedimpArray.clear(); + } + return bResult; +} + +// REDIM PRESERVE +// TOS = variable for the array +// argv = dimension information + +void SbiRuntime::StepREDIMP() +{ + SbxVariableRef refVar = PopVar(); + DimImpl( refVar ); + + // Now check, if we can copy from the old array + if( refRedimpArray.is() ) + { + if (SbxDimArray* pNewArray = dynamic_cast<SbxDimArray*>(refVar->GetObject())) + implRestorePreservedArray(pNewArray, refRedimpArray); + } +} + +// REDIM_COPY +// TOS = Array-Variable, Reference to array is copied +// Variable is cleared as in ERASE + +void SbiRuntime::StepREDIMP_ERASE() +{ + SbxVariableRef refVar = PopVar(); + refRedim = refVar; + SbxDataType eType = refVar->GetType(); + if( eType & SbxARRAY ) + { + SbxBase* pElemObj = refVar->GetObject(); + SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( pElemObj ); + if( pDimArray ) + { + refRedimpArray = pDimArray; + } + + } + else if( refVar->IsFixed() ) + { + refVar->Clear(); + } + else + { + refVar->SetType( SbxEMPTY ); + } +} + +static void lcl_clearImpl( SbxVariableRef const & refVar, SbxDataType const & eType ) +{ + SbxFlagBits nSavFlags = refVar->GetFlags(); + refVar->ResetFlag( SbxFlagBits::Fixed ); + refVar->SetType( SbxDataType(eType & 0x0FFF) ); + refVar->SetFlags( nSavFlags ); + refVar->Clear(); +} + +static void lcl_eraseImpl( SbxVariableRef const & refVar, bool bVBAEnabled ) +{ + SbxDataType eType = refVar->GetType(); + if( eType & SbxARRAY ) + { + if ( bVBAEnabled ) + { + SbxBase* pElemObj = refVar->GetObject(); + SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( pElemObj ); + if( pDimArray ) + { + if ( pDimArray->hasFixedSize() ) + { + // Clear all Value(s) + pDimArray->SbxArray::Clear(); + } + else + { + pDimArray->Clear(); // clear dims and values + } + } + else + { + SbxArray* pArray = dynamic_cast<SbxArray*>( pElemObj ); + if ( pArray ) + { + pArray->Clear(); + } + } + } + else + { + // Arrays have on an erase to VB quite a complex behaviour. Here are + // only the type problems at REDIM (#26295) removed at first: + // Set type hard onto the array-type, because a variable with array is + // SbxOBJECT. At REDIM there's an SbxOBJECT-array generated then and + // the original type is lost -> runtime error + lcl_clearImpl( refVar, eType ); + } + } + else if( refVar->IsFixed() ) + { + refVar->Clear(); + } + else + { + refVar->SetType( SbxEMPTY ); + } +} + +// delete variable +// TOS = variable + +void SbiRuntime::StepERASE() +{ + SbxVariableRef refVar = PopVar(); + lcl_eraseImpl( refVar, bVBAEnabled ); +} + +void SbiRuntime::StepERASE_CLEAR() +{ + refRedim = PopVar(); +} + +void SbiRuntime::StepARRAYACCESS() +{ + if( !refArgv.is() ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + } + SbxVariableRef refVar = PopVar(); + refVar->SetParameters( refArgv.get() ); + PopArgv(); + PushVar( CheckArray( refVar.get() ) ); +} + +void SbiRuntime::StepBYVAL() +{ + // Copy variable on stack to break call by reference + SbxVariableRef pVar = PopVar(); + SbxDataType t = pVar->GetType(); + + SbxVariable* pCopyVar = new SbxVariable( t ); + pCopyVar->SetFlag( SbxFlagBits::ReadWrite ); + *pCopyVar = *pVar; + + PushVar( pCopyVar ); +} + +// establishing an argv +// nOp1 stays as it is -> 1st element is the return value + +void SbiRuntime::StepARGC() +{ + PushArgv(); + refArgv = new SbxArray; + nArgc = 1; +} + +// storing an argument in Argv + +void SbiRuntime::StepARGV() +{ + if( !refArgv.is() ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + } + else + { + SbxVariableRef pVal = PopVar(); + + // Before fix of #94916: + if( dynamic_cast<const SbxMethod*>( pVal.get() ) != nullptr + || dynamic_cast<const SbUnoProperty*>( pVal.get() ) != nullptr + || dynamic_cast<const SbProcedureProperty*>( pVal.get() ) != nullptr ) + { + // evaluate methods and properties! + SbxVariable* pRes = new SbxVariable( *pVal ); + pVal = pRes; + } + refArgv->Put32( pVal.get(), nArgc++ ); + } +} + +// Input to Variable. The variable is on TOS and is +// is removed afterwards. +void SbiRuntime::StepINPUT() +{ + OUStringBuffer sin; + OUString s; + char ch = 0; + ErrCode err; + // Skip whitespace + while( ( err = pIosys->GetError() ) == ERRCODE_NONE ) + { + ch = pIosys->Read(); + if( ch != ' ' && ch != '\t' && ch != '\n' ) + { + break; + } + } + if( !err ) + { + // Scan until comma or whitespace + char sep = ( ch == '"' ) ? ch : 0; + if( sep ) + { + ch = pIosys->Read(); + } + while( ( err = pIosys->GetError() ) == ERRCODE_NONE ) + { + if( ch == sep ) + { + ch = pIosys->Read(); + if( ch != sep ) + { + break; + } + } + else if( !sep && (ch == ',' || ch == '\n') ) + { + break; + } + sin.append( ch ); + ch = pIosys->Read(); + } + // skip whitespace + if( ch == ' ' || ch == '\t' ) + { + while( ( err = pIosys->GetError() ) == ERRCODE_NONE ) + { + if( ch != ' ' && ch != '\t' && ch != '\n' ) + { + break; + } + ch = pIosys->Read(); + } + } + } + if( !err ) + { + s = sin.makeStringAndClear(); + SbxVariableRef pVar = GetTOS(); + // try to fill the variable with a numeric value first, + // then with a string value + if( !pVar->IsFixed() || pVar->IsNumeric() ) + { + sal_uInt16 nLen = 0; + if( !pVar->Scan( s, &nLen ) ) + { + err = SbxBase::GetError(); + SbxBase::ResetError(); + } + // the value has to be scanned in completely + else if( nLen != s.getLength() && !pVar->PutString( s ) ) + { + err = SbxBase::GetError(); + SbxBase::ResetError(); + } + else if( nLen != s.getLength() && pVar->IsNumeric() ) + { + err = SbxBase::GetError(); + SbxBase::ResetError(); + if( !err ) + { + err = ERRCODE_BASIC_CONVERSION; + } + } + } + else + { + pVar->PutString( s ); + err = SbxBase::GetError(); + SbxBase::ResetError(); + } + } + if( err == ERRCODE_BASIC_USER_ABORT ) + { + Error( err ); + } + else if( err ) + { + if( pRestart && !pIosys->GetChannel() ) + { + pCode = pRestart; + } + else + { + Error( err ); + } + } + else + { + PopVar(); + } +} + +// Line Input to Variable. The variable is on TOS and is +// deleted afterwards. + +void SbiRuntime::StepLINPUT() +{ + OString aInput; + pIosys->Read( aInput ); + Error( pIosys->GetError() ); + SbxVariableRef p = PopVar(); + p->PutString(OStringToOUString(aInput, osl_getThreadTextEncoding())); +} + +// end of program + +void SbiRuntime::StepSTOP() +{ + pInst->Stop(); +} + + +void SbiRuntime::StepINITFOR() +{ + PushFor(); +} + +void SbiRuntime::StepINITFOREACH() +{ + PushForEach(); +} + +// increment FOR-variable + +void SbiRuntime::StepNEXT() +{ + if( !pForStk ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + return; + } + if (pForStk->eForType != ForType::To) + return; + if (!pForStk->refVar) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + return; + } + pForStk->refVar->Compute( SbxPLUS, *pForStk->refInc ); +} + +// beginning CASE: TOS in CASE-stack + +void SbiRuntime::StepCASE() +{ + if( !refCaseStk.is() ) + { + refCaseStk = new SbxArray; + } + SbxVariableRef xVar = PopVar(); + refCaseStk->Put32( xVar.get(), refCaseStk->Count32() ); +} + +// end CASE: free variable + +void SbiRuntime::StepENDCASE() +{ + if( !refCaseStk.is() || !refCaseStk->Count32() ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + } + else + { + refCaseStk->Remove( refCaseStk->Count32() - 1 ); + } +} + + +void SbiRuntime::StepSTDERROR() +{ + pError = nullptr; bError = true; + pInst->aErrorMsg.clear(); + pInst->nErr = ERRCODE_NONE; + pInst->nErl = 0; + nError = ERRCODE_NONE; + SbxErrObject::getUnoErrObject()->Clear(); +} + +void SbiRuntime::StepNOERROR() +{ + pInst->aErrorMsg.clear(); + pInst->nErr = ERRCODE_NONE; + pInst->nErl = 0; + nError = ERRCODE_NONE; + SbxErrObject::getUnoErrObject()->Clear(); + bError = false; +} + +// leave UP + +void SbiRuntime::StepLEAVE() +{ + bRun = false; + // If VBA and we are leaving an ErrorHandler then clear the error ( it's been processed ) + if ( bInError && pError ) + { + SbxErrObject::getUnoErrObject()->Clear(); + } +} + +void SbiRuntime::StepCHANNEL() // TOS = channel number +{ + SbxVariableRef pChan = PopVar(); + short nChan = pChan->GetInteger(); + pIosys->SetChannel( nChan ); + Error( pIosys->GetError() ); +} + +void SbiRuntime::StepCHANNEL0() +{ + pIosys->ResetChannel(); +} + +void SbiRuntime::StepPRINT() // print TOS +{ + SbxVariableRef p = PopVar(); + OUString s1 = p->GetOUString(); + OUString s; + if( p->GetType() >= SbxINTEGER && p->GetType() <= SbxDOUBLE ) + { + s = " "; // one blank before + } + s += s1; + pIosys->Write( s ); + Error( pIosys->GetError() ); +} + +void SbiRuntime::StepPRINTF() // print TOS in field +{ + SbxVariableRef p = PopVar(); + OUString s1 = p->GetOUString(); + OUStringBuffer s; + if( p->GetType() >= SbxINTEGER && p->GetType() <= SbxDOUBLE ) + { + s.append(' '); + } + s.append(s1); + comphelper::string::padToLength(s, 14, ' '); + pIosys->Write( s.makeStringAndClear() ); + Error( pIosys->GetError() ); +} + +void SbiRuntime::StepWRITE() // write TOS +{ + SbxVariableRef p = PopVar(); + // Does the string have to be encapsulated? + char ch = 0; + switch (p->GetType() ) + { + case SbxSTRING: ch = '"'; break; + case SbxCURRENCY: + case SbxBOOL: + case SbxDATE: ch = '#'; break; + default: break; + } + OUString s; + if( ch ) + { + s += OUString(ch); + } + s += p->GetOUString(); + if( ch ) + { + s += OUString(ch); + } + pIosys->Write( s ); + Error( pIosys->GetError() ); +} + +void SbiRuntime::StepRENAME() // Rename Tos+1 to Tos +{ + SbxVariableRef pTos1 = PopVar(); + SbxVariableRef pTos = PopVar(); + OUString aDest = pTos1->GetOUString(); + OUString aSource = pTos->GetOUString(); + + if( hasUno() ) + { + implStepRenameUCB( aSource, aDest ); + } + else + { + implStepRenameOSL( aSource, aDest ); + } +} + +// TOS = Prompt + +void SbiRuntime::StepPROMPT() +{ + SbxVariableRef p = PopVar(); + OString aStr(OUStringToOString(p->GetOUString(), osl_getThreadTextEncoding())); + pIosys->SetPrompt( aStr ); +} + +// Set Restart point + +void SbiRuntime::StepRESTART() +{ + pRestart = pCode; +} + +// empty expression on stack for missing parameter + +void SbiRuntime::StepEMPTY() +{ + // #57915 The semantics of StepEMPTY() is the representation of a missing argument. + // This is represented by the value 448 (ERRCODE_BASIC_NAMED_NOT_FOUND) of the type error + // in VB. StepEmpty should now rather be named StepMISSING() but the name is kept + // to simplify matters. + SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); + xVar->PutErr( 448 ); + // tdf#79426, tdf#125180 - add additional information about a missing parameter + SetIsMissing( xVar.get() ); + PushVar( xVar.get() ); +} + +// TOS = error code + +void SbiRuntime::StepERROR() +{ + SbxVariableRef refCode = PopVar(); + sal_uInt16 n = refCode->GetUShort(); + ErrCode error = StarBASIC::GetSfxFromVBError( n ); + if ( bVBAEnabled ) + { + pInst->Error( error ); + } + else + { + Error( error ); + } +} + +// loading a numeric constant (+ID) + +void SbiRuntime::StepLOADNC( sal_uInt32 nOp1 ) +{ + // #57844 use localized function + OUString aStr = pImg->GetString( static_cast<short>( nOp1 ) ); + // also allow , !!! + sal_Int32 iComma = aStr.indexOf(','); + if( iComma >= 0 ) + { + aStr = aStr.replaceAt(iComma, 1, "."); + } + sal_Int32 nParseEnd = 0; + rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok; + double n = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd ); + + // tdf#131296 - retrieve data type put in SbiExprNode::Gen + SbxDataType eType = SbxDOUBLE; + if ( nParseEnd < aStr.getLength() ) + { + switch ( aStr[nParseEnd] ) + { + // See GetSuffixType in basic/source/comp/scanner.cxx for type characters + case '%': eType = SbxINTEGER; break; + case '&': eType = SbxLONG; break; + case '!': eType = SbxSINGLE; break; + case '@': eType = SbxCURRENCY; break; + } + } + SbxVariable* p = new SbxVariable( eType ); + p->PutDouble( n ); + // tdf#133913 - create variable with Variant/Type in order to prevent type conversion errors + p->ResetFlag( SbxFlagBits::Fixed ); + PushVar( p ); +} + +// loading a string constant (+ID) + +void SbiRuntime::StepLOADSC( sal_uInt32 nOp1 ) +{ + SbxVariable* p = new SbxVariable; + p->PutString( pImg->GetString( static_cast<short>( nOp1 ) ) ); + PushVar( p ); +} + +// Immediate Load (+value) +// The opcode is not generated in SbiExprNode::Gen anymore; used for legacy images + +void SbiRuntime::StepLOADI( sal_uInt32 nOp1 ) +{ + SbxVariable* p = new SbxVariable; + p->PutInteger( static_cast<sal_Int16>( nOp1 ) ); + PushVar( p ); +} + +// store a named argument in Argv (+Arg-no. from 1!) + +void SbiRuntime::StepARGN( sal_uInt32 nOp1 ) +{ + if( !refArgv.is() ) + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + else + { + OUString aAlias( pImg->GetString( static_cast<short>( nOp1 ) ) ); + SbxVariableRef pVal = PopVar(); + if( bVBAEnabled && + ( dynamic_cast<const SbxMethod*>( pVal.get()) != nullptr + || dynamic_cast<const SbUnoProperty*>( pVal.get()) != nullptr + || dynamic_cast<const SbProcedureProperty*>( pVal.get()) != nullptr ) ) + { + // named variables ( that are Any especially properties ) can be empty at this point and need a broadcast + if ( pVal->GetType() == SbxEMPTY ) + pVal->Broadcast( SfxHintId::BasicDataWanted ); + // evaluate methods and properties! + SbxVariable* pRes = new SbxVariable( *pVal ); + pVal = pRes; + } + refArgv->Put32( pVal.get(), nArgc ); + refArgv->PutAlias32( aAlias, nArgc++ ); + } +} + +// converting the type of an argument in Argv for DECLARE-Fkt. (+type) + +void SbiRuntime::StepARGTYP( sal_uInt32 nOp1 ) +{ + if( !refArgv.is() ) + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + else + { + bool bByVal = (nOp1 & 0x8000) != 0; // Is BYVAL requested? + SbxDataType t = static_cast<SbxDataType>(nOp1 & 0x7FFF); + SbxVariable* pVar = refArgv->Get32( refArgv->Count32() - 1 ); // last Arg + + // check BYVAL + if( pVar->GetRefCount() > 2 ) // 2 is normal for BYVAL + { + // parameter is a reference + if( bByVal ) + { + // Call by Value is requested -> create a copy + pVar = new SbxVariable( *pVar ); + pVar->SetFlag( SbxFlagBits::ReadWrite ); + refExprStk->Put32( pVar, refArgv->Count32() - 1 ); + } + else + pVar->SetFlag( SbxFlagBits::Reference ); // Ref-Flag for DllMgr + } + else + { + // parameter is NO reference + if( bByVal ) + pVar->ResetFlag( SbxFlagBits::Reference ); // no reference -> OK + else + Error( ERRCODE_BASIC_BAD_PARAMETERS ); // reference needed + } + + if( pVar->GetType() != t ) + { + // variant for correct conversion + // besides error, if SbxBYREF + pVar->Convert( SbxVARIANT ); + pVar->Convert( t ); + } + } +} + +// bring string to a definite length (+length) + +void SbiRuntime::StepPAD( sal_uInt32 nOp1 ) +{ + SbxVariable* p = GetTOS(); + OUString s = p->GetOUString(); + sal_Int32 nLen(nOp1); + if( s.getLength() == nLen ) + return; + + OUStringBuffer aBuf(s); + if (aBuf.getLength() > nLen) + { + comphelper::string::truncateToLength(aBuf, nLen); + } + else + { + comphelper::string::padToLength(aBuf, nLen, ' '); + } + s = aBuf.makeStringAndClear(); +} + +// jump (+target) + +void SbiRuntime::StepJUMP( sal_uInt32 nOp1 ) +{ +#ifdef DBG_UTIL + // #QUESTION shouldn't this be + // if( (sal_uInt8*)( nOp1+pImagGetCode() ) >= pImg->GetCodeSize() ) + if( nOp1 >= pImg->GetCodeSize() ) + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); +#endif + pCode = reinterpret_cast<const sal_uInt8*>(pImg->GetCode()) + nOp1; +} + +// evaluate TOS, conditional jump (+target) + +void SbiRuntime::StepJUMPT( sal_uInt32 nOp1 ) +{ + SbxVariableRef p = PopVar(); + if( p->GetBool() ) + StepJUMP( nOp1 ); +} + +// evaluate TOS, conditional jump (+target) + +void SbiRuntime::StepJUMPF( sal_uInt32 nOp1 ) +{ + SbxVariableRef p = PopVar(); + // In a test e.g. If Null then + // will evaluate Null will act as if False + if( ( bVBAEnabled && p->IsNull() ) || !p->GetBool() ) + StepJUMP( nOp1 ); +} + +// evaluate TOS, jump into JUMP-table (+MaxVal) +// looks like this: +// ONJUMP 2 +// JUMP target1 +// JUMP target2 + +// if 0x8000 is set in the operand, push the return address (ON..GOSUB) + +void SbiRuntime::StepONJUMP( sal_uInt32 nOp1 ) +{ + SbxVariableRef p = PopVar(); + sal_Int16 n = p->GetInteger(); + if( nOp1 & 0x8000 ) + { + nOp1 &= 0x7FFF; + PushGosub( pCode + 5 * nOp1 ); + } + if( n < 1 || o3tl::make_unsigned(n) > nOp1 ) + n = static_cast<sal_Int16>( nOp1 + 1 ); + nOp1 = static_cast<sal_uInt32>( reinterpret_cast<const char*>(pCode) - pImg->GetCode() ) + 5 * --n; + StepJUMP( nOp1 ); +} + +// UP-call (+target) + +void SbiRuntime::StepGOSUB( sal_uInt32 nOp1 ) +{ + PushGosub( pCode ); + if( nOp1 >= pImg->GetCodeSize() ) + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + pCode = reinterpret_cast<const sal_uInt8*>(pImg->GetCode()) + nOp1; +} + +// UP-return (+0 or target) + +void SbiRuntime::StepRETURN( sal_uInt32 nOp1 ) +{ + PopGosub(); + if( nOp1 ) + StepJUMP( nOp1 ); +} + +// check FOR-variable (+Endlabel) + +void SbiRuntime::StepTESTFOR( sal_uInt32 nOp1 ) +{ + if( !pForStk ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + return; + } + + bool bEndLoop = false; + switch( pForStk->eForType ) + { + case ForType::To: + { + SbxOperator eOp = ( pForStk->refInc->GetDouble() < 0 ) ? SbxLT : SbxGT; + if( pForStk->refVar->Compare( eOp, *pForStk->refEnd ) ) + bEndLoop = true; + if (SbxBase::IsError()) + pForStk->eForType = ForType::Error; // terminate loop at the next iteration + break; + } + case ForType::EachArray: + { + SbiForStack* p = pForStk; + if (!p->refEnd) + { + SbxBase::SetError(ERRCODE_BASIC_CONVERSION); + pForStk->eForType = ForType::Error; // terminate loop at the next iteration + } + else if (p->pArrayCurIndices == nullptr) + { + bEndLoop = true; + } + else + { + SbxDimArray* pArray = reinterpret_cast<SbxDimArray*>(p->refEnd.get()); + sal_Int32 nDims = pArray->GetDims32(); + + // Empty array? + if( nDims == 1 && p->pArrayLowerBounds[0] > p->pArrayUpperBounds[0] ) + { + bEndLoop = true; + break; + } + SbxVariable* pVal = pArray->Get32( p->pArrayCurIndices.get() ); + *(p->refVar) = *pVal; + + bool bFoundNext = false; + for(sal_Int32 i = 0 ; i < nDims ; i++ ) + { + if( p->pArrayCurIndices[i] < p->pArrayUpperBounds[i] ) + { + bFoundNext = true; + p->pArrayCurIndices[i]++; + for( sal_Int32 j = i - 1 ; j >= 0 ; j-- ) + p->pArrayCurIndices[j] = p->pArrayLowerBounds[j]; + break; + } + } + if( !bFoundNext ) + { + p->pArrayCurIndices.reset(); + } + } + break; + } + case ForType::EachCollection: + { + if (!pForStk->refEnd) + { + SbxBase::SetError(ERRCODE_BASIC_CONVERSION); + pForStk->eForType = ForType::Error; // terminate loop at the next iteration + break; + } + + BasicCollection* pCollection = static_cast<BasicCollection*>(pForStk->refEnd.get()); + SbxArrayRef xItemArray = pCollection->xItemArray; + sal_Int32 nCount = xItemArray->Count32(); + if( pForStk->nCurCollectionIndex < nCount ) + { + SbxVariable* pRes = xItemArray->Get32( pForStk->nCurCollectionIndex ); + pForStk->nCurCollectionIndex++; + (*pForStk->refVar) = *pRes; + } + else + { + bEndLoop = true; + } + break; + } + case ForType::EachXEnumeration: + { + SbiForStack* p = pForStk; + if (!p->xEnumeration) + { + SbxBase::SetError(ERRCODE_BASIC_CONVERSION); + pForStk->eForType = ForType::Error; // terminate loop at the next iteration + } + else if (p->xEnumeration->hasMoreElements()) + { + Any aElem = p->xEnumeration->nextElement(); + SbxVariableRef xVar = new SbxVariable( SbxVARIANT ); + unoToSbxValue( xVar.get(), aElem ); + (*pForStk->refVar) = *xVar; + } + else + { + bEndLoop = true; + } + break; + } + case ForType::Error: + { + // We are in Resume Next mode after failed loop initialization + bEndLoop = true; + Error(ERRCODE_BASIC_BAD_PARAMETER); + break; + } + } + if( bEndLoop ) + { + PopFor(); + StepJUMP( nOp1 ); + } +} + +// Tos+1 <= Tos+2 <= Tos, 2xremove (+Target) + +void SbiRuntime::StepCASETO( sal_uInt32 nOp1 ) +{ + if( !refCaseStk.is() || !refCaseStk->Count32() ) + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + else + { + SbxVariableRef xTo = PopVar(); + SbxVariableRef xFrom = PopVar(); + SbxVariableRef xCase = refCaseStk->Get32( refCaseStk->Count32() - 1 ); + if( *xCase >= *xFrom && *xCase <= *xTo ) + StepJUMP( nOp1 ); + } +} + + +void SbiRuntime::StepERRHDL( sal_uInt32 nOp1 ) +{ + const sal_uInt8* p = pCode; + StepJUMP( nOp1 ); + pError = pCode; + pCode = p; + pInst->aErrorMsg.clear(); + pInst->nErr = ERRCODE_NONE; + pInst->nErl = 0; + nError = ERRCODE_NONE; + SbxErrObject::getUnoErrObject()->Clear(); +} + +// Resume after errors (+0=statement, 1=next or Label) + +void SbiRuntime::StepRESUME( sal_uInt32 nOp1 ) +{ + // #32714 Resume without error? -> error + if( !bInError ) + { + Error( ERRCODE_BASIC_BAD_RESUME ); + return; + } + if( nOp1 ) + { + // set Code-pointer to the next statement + sal_uInt16 n1, n2; + pCode = pMod->FindNextStmnt( pErrCode, n1, n2, true, pImg ); + } + else + pCode = pErrStmnt; + if ( pError ) // current in error handler ( and got a Resume Next statement ) + SbxErrObject::getUnoErrObject()->Clear(); + + if( nOp1 > 1 ) + StepJUMP( nOp1 ); + pInst->aErrorMsg.clear(); + pInst->nErr = ERRCODE_NONE; + pInst->nErl = 0; + nError = ERRCODE_NONE; + bInError = false; +} + +// close channel (+channel, 0=all) +void SbiRuntime::StepCLOSE( sal_uInt32 nOp1 ) +{ + ErrCode err; + if( !nOp1 ) + pIosys->Shutdown(); + else + { + err = pIosys->GetError(); + if( !err ) + { + pIosys->Close(); + } + } + err = pIosys->GetError(); + Error( err ); +} + +// output character (+char) + +void SbiRuntime::StepPRCHAR( sal_uInt32 nOp1 ) +{ + OUString s(static_cast<sal_Unicode>(nOp1)); + pIosys->Write( s ); + Error( pIosys->GetError() ); +} + +// check whether TOS is a certain object class (+StringID) + +bool SbiRuntime::implIsClass( SbxObject const * pObj, const OUString& aClass ) +{ + bool bRet = true; + + if( !aClass.isEmpty() ) + { + bRet = pObj->IsClass( aClass ); + if( !bRet ) + bRet = aClass.equalsIgnoreAsciiCase( "object" ); + if( !bRet ) + { + const OUString& aObjClass = pObj->GetClassName(); + SbModule* pClassMod = GetSbData()->pClassFac->FindClass( aObjClass ); + if( pClassMod ) + { + SbClassData* pClassData = pClassMod->pClassData.get(); + if (pClassData != nullptr ) + { + SbxVariable* pClassVar = pClassData->mxIfaces->Find( aClass, SbxClassType::DontCare ); + bRet = (pClassVar != nullptr); + } + } + } + } + return bRet; +} + +bool SbiRuntime::checkClass_Impl( const SbxVariableRef& refVal, + const OUString& aClass, bool bRaiseErrors, bool bDefault ) +{ + bool bOk = bDefault; + + SbxDataType t = refVal->GetType(); + SbxVariable* pVal = refVal.get(); + // we don't know the type of uno properties that are (maybevoid) + if ( t == SbxEMPTY ) + { + if ( auto pProp = dynamic_cast<SbUnoProperty*>( refVal.get() ) ) + { + t = pProp->getRealType(); + } + } + if( t == SbxOBJECT || bVBAEnabled ) + { + SbxObject* pObj = dynamic_cast<SbxObject*>(pVal); + if (!pObj) + { + pObj = dynamic_cast<SbxObject*>(refVal->GetObject()); + } + if( pObj ) + { + if( !implIsClass( pObj, aClass ) ) + { + SbUnoObject* pUnoObj(nullptr); + if (bVBAEnabled || CodeCompleteOptions::IsExtendedTypeDeclaration()) + { + pUnoObj = dynamic_cast<SbUnoObject*>(pObj); + } + + if (pUnoObj) + bOk = checkUnoObjectType(*pUnoObj, aClass); + else + bOk = false; + if ( !bOk && bRaiseErrors ) + Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT ); + } + else + { + bOk = true; + + SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pObj ); + if( pClassModuleObject != nullptr ) + pClassModuleObject->triggerInitializeEvent(); + } + } + } + else + { + if( bRaiseErrors ) + Error( ERRCODE_BASIC_NEEDS_OBJECT ); + bOk = false; + } + return bOk; +} + +void SbiRuntime::StepSETCLASS_impl( sal_uInt32 nOp1, bool bHandleDflt ) +{ + SbxVariableRef refVal = PopVar(); + SbxVariableRef refVar = PopVar(); + OUString aClass( pImg->GetString( static_cast<short>( nOp1 ) ) ); + + bool bOk = checkClass_Impl( refVal, aClass, true, true ); + if( bOk ) + { + StepSET_Impl( refVal, refVar, bHandleDflt ); // don't do handle default prop for a "proper" set + } +} + +void SbiRuntime::StepVBASETCLASS( sal_uInt32 nOp1 ) +{ + StepSETCLASS_impl( nOp1, false ); +} + +void SbiRuntime::StepSETCLASS( sal_uInt32 nOp1 ) +{ + StepSETCLASS_impl( nOp1, true ); +} + +void SbiRuntime::StepTESTCLASS( sal_uInt32 nOp1 ) +{ + SbxVariableRef xObjVal = PopVar(); + OUString aClass( pImg->GetString( static_cast<short>( nOp1 ) ) ); + bool bDefault = !bVBAEnabled; + bool bOk = checkClass_Impl( xObjVal, aClass, false, bDefault ); + + SbxVariable* pRet = new SbxVariable; + pRet->PutBool( bOk ); + PushVar( pRet ); +} + +// define library for following declare-call + +void SbiRuntime::StepLIB( sal_uInt32 nOp1 ) +{ + aLibName = pImg->GetString( static_cast<short>( nOp1 ) ); +} + +// TOS is incremented by BASE, BASE is pushed before (+BASE) +// This opcode is pushed before DIM/REDIM-commands, +// if there's been only one index named. + +void SbiRuntime::StepBASED( sal_uInt32 nOp1 ) +{ + SbxVariable* p1 = new SbxVariable; + SbxVariableRef x2 = PopVar(); + + // #109275 Check compatibility mode + bool bCompatible = ((nOp1 & 0x8000) != 0); + sal_uInt16 uBase = static_cast<sal_uInt16>(nOp1 & 1); // Can only be 0 or 1 + p1->PutInteger( uBase ); + if( !bCompatible ) + x2->Compute( SbxPLUS, *p1 ); + PushVar( x2.get() ); // first the Expr + PushVar( p1 ); // then the Base +} + +// the bits in the String-ID: +// 0x8000 - Argv is reserved + +SbxVariable* SbiRuntime::FindElement( SbxObject* pObj, sal_uInt32 nOp1, sal_uInt32 nOp2, + ErrCode nNotFound, bool bLocal, bool bStatic ) +{ + bool bIsVBAInterOp = SbiRuntime::isVBAEnabled(); + if( bIsVBAInterOp ) + { + StarBASIC* pMSOMacroRuntimeLib = GetSbData()->pMSOMacroRuntimLib; + if( pMSOMacroRuntimeLib != nullptr ) + { + pMSOMacroRuntimeLib->ResetFlag( SbxFlagBits::ExtSearch ); + } + } + + SbxVariable* pElem = nullptr; + if( !pObj ) + { + Error( ERRCODE_BASIC_NO_OBJECT ); + pElem = new SbxVariable; + } + else + { + bool bFatalError = false; + SbxDataType t = static_cast<SbxDataType>(nOp2); + OUString aName( pImg->GetString( static_cast<short>( nOp1 & 0x7FFF ) ) ); + // Hacky capture of Evaluate [] syntax + // this should be tackled I feel at the pcode level + if ( bIsVBAInterOp && aName.startsWith("[") ) + { + // emulate pcode here + StepARGC(); + // pseudo StepLOADSC + OUString sArg = aName.copy( 1, aName.getLength() - 2 ); + SbxVariable* p = new SbxVariable; + p->PutString( sArg ); + PushVar( p ); + StepARGV(); + nOp1 = nOp1 | 0x8000; // indicate params are present + aName = "Evaluate"; + } + if( bLocal ) + { + if ( bStatic && pMeth ) + { + pElem = pMeth->GetStatics()->Find( aName, SbxClassType::DontCare ); + } + + if ( !pElem ) + { + pElem = refLocals->Find( aName, SbxClassType::DontCare ); + } + } + if( !pElem ) + { + bool bSave = rBasic.bNoRtl; + rBasic.bNoRtl = true; + pElem = pObj->Find( aName, SbxClassType::DontCare ); + + // #110004, #112015: Make private really private + if( bLocal && pElem ) // Local as flag for global search + { + if( pElem->IsSet( SbxFlagBits::Private ) ) + { + SbiInstance* pInst_ = GetSbData()->pInst; + if( pInst_ && pInst_->IsCompatibility() && pObj != pElem->GetParent() ) + { + pElem = nullptr; // Found but in wrong module! + } + // Interfaces: Use SbxFlagBits::ExtFound + } + } + rBasic.bNoRtl = bSave; + + // is it a global uno-identifier? + if( bLocal && !pElem ) + { + bool bSetName = true; // preserve normal behaviour + + // i#i68894# if VBAInterOp favour searching vba globals + // over searching for uno classes + if ( bVBAEnabled ) + { + // Try Find in VBA symbols space + pElem = rBasic.VBAFind( aName, SbxClassType::DontCare ); + if ( pElem ) + { + bSetName = false; // don't overwrite uno name + } + else + { + pElem = VBAConstantHelper::instance().getVBAConstant( aName ); + } + } + + if( !pElem ) + { + // #72382 ATTENTION! ALWAYS returns a result now + // because of unknown modules! + SbUnoClass* pUnoClass = findUnoClass( aName ); + if( pUnoClass ) + { + pElem = new SbxVariable( t ); + SbxValues aRes( SbxOBJECT ); + aRes.pObj = pUnoClass; + pElem->SbxVariable::Put( aRes ); + } + } + + // #62939 If a uno-class has been found, the wrapper + // object has to be held, because the uno-class, e. g. + // "stardiv", has to be read out of the registry + // every time again otherwise + if( pElem ) + { + // #63774 May not be saved too!!! + pElem->SetFlag( SbxFlagBits::DontStore ); + pElem->SetFlag( SbxFlagBits::NoModify); + + // #72382 save locally, all variables that have been declared + // implicit would become global automatically otherwise! + if ( bSetName ) + { + pElem->SetName( aName ); + } + refLocals->Put32( pElem, refLocals->Count32() ); + } + } + + if( !pElem ) + { + // not there and not in the object? + // don't establish if that thing has parameters! + if( nOp1 & 0x8000 ) + { + bFatalError = true; + } + + // else, if there are parameters, use different error code + if( !bLocal || pImg->IsFlag( SbiImageFlags::EXPLICIT ) ) + { + // #39108 if explicit and as ELEM always a fatal error + bFatalError = true; + + + if( !( nOp1 & 0x8000 ) && nNotFound == ERRCODE_BASIC_PROC_UNDEFINED ) + { + nNotFound = ERRCODE_BASIC_VAR_UNDEFINED; + } + } + if( bFatalError ) + { + // #39108 use dummy variable instead of fatal error + if( !xDummyVar.is() ) + { + xDummyVar = new SbxVariable( SbxVARIANT ); + } + pElem = xDummyVar.get(); + + ClearArgvStack(); + + Error( nNotFound, aName ); + } + else + { + if ( bStatic ) + { + pElem = StepSTATIC_Impl( aName, t, 0 ); + } + if ( !pElem ) + { + pElem = new SbxVariable( t ); + if( t != SbxVARIANT ) + { + pElem->SetFlag( SbxFlagBits::Fixed ); + } + pElem->SetName( aName ); + refLocals->Put32( pElem, refLocals->Count32() ); + } + } + } + } + // #39108 Args can already be deleted! + if( !bFatalError ) + { + SetupArgs( pElem, nOp1 ); + } + // because a particular call-type is requested + if (SbxMethod* pMethod = dynamic_cast<SbxMethod*>(pElem)) + { + // shall the type be converted? + SbxDataType t2 = pElem->GetType(); + bool bSet = false; + if( (pElem->GetFlags() & SbxFlagBits::Fixed) == SbxFlagBits::NONE ) + { + if( t != SbxVARIANT && t != t2 && + t >= SbxINTEGER && t <= SbxSTRING ) + { + pElem->SetType( t ); + bSet = true; + } + } + // assign pElem to a Ref, to delete a temp-var if applicable + SbxVariableRef xDeleteRef = pElem; + + // remove potential rests of the last call of the SbxMethod + // free Write before, so that there's no error + SbxFlagBits nSavFlags = pElem->GetFlags(); + pElem->SetFlag( SbxFlagBits::ReadWrite | SbxFlagBits::NoBroadcast ); + pElem->SbxValue::Clear(); + pElem->SetFlags( nSavFlags ); + + // don't touch before setting, as e. g. LEFT() + // has to know the difference between Left$() and Left() + + // because the methods' parameters are cut away in PopVar() + SbxVariable* pNew = new SbxMethod(*pMethod); + //OLD: SbxVariable* pNew = new SbxVariable( *pElem ); + + pElem->SetParameters(nullptr); + pNew->SetFlag( SbxFlagBits::ReadWrite ); + + if( bSet ) + { + pElem->SetType( t2 ); + } + pElem = pNew; + } + // consider index-access for UnoObjects + // definitely we want this for VBA where properties are often + // collections ( which need index access ), but lets only do + // this if we actually have params following + else if( bVBAEnabled && dynamic_cast<const SbUnoProperty*>( pElem) != nullptr && pElem->GetParameters() ) + { + SbxVariableRef xDeleteRef = pElem; + + // dissolve the notify while copying variable + SbxVariable* pNew = new SbxVariable( *pElem ); + pElem->SetParameters( nullptr ); + pElem = pNew; + } + } + return CheckArray( pElem ); +} + +// for current scope (e. g. query from BASIC-IDE) +SbxBase* SbiRuntime::FindElementExtern( const OUString& rName ) +{ + // don't expect pMeth to be != 0, as there are none set + // in the RunInit yet + + SbxVariable* pElem = nullptr; + if( !pMod || rName.isEmpty() ) + { + return nullptr; + } + if( refLocals.is() ) + { + pElem = refLocals->Find( rName, SbxClassType::DontCare ); + } + if ( !pElem && pMeth ) + { + // for statics, set the method's name in front + OUString aMethName = pMeth->GetName() + ":" + rName; + pElem = pMod->Find(aMethName, SbxClassType::DontCare); + } + + // search in parameter list + if( !pElem && pMeth ) + { + SbxInfo* pInfo = pMeth->GetInfo(); + if( pInfo && refParams.is() ) + { + sal_uInt32 nParamCount = refParams->Count32(); + assert(nParamCount <= std::numeric_limits<sal_uInt16>::max()); + sal_uInt16 j = 1; + const SbxParamInfo* pParam = pInfo->GetParam( j ); + while( pParam ) + { + if( pParam->aName.equalsIgnoreAsciiCase( rName ) ) + { + if( j >= nParamCount ) + { + // Parameter is missing + pElem = new SbxVariable( SbxSTRING ); + pElem->PutString( "<missing parameter>"); + } + else + { + pElem = refParams->Get32( j ); + } + break; + } + pParam = pInfo->GetParam( ++j ); + } + } + } + + // search in module + if( !pElem ) + { + bool bSave = rBasic.bNoRtl; + rBasic.bNoRtl = true; + pElem = pMod->Find( rName, SbxClassType::DontCare ); + rBasic.bNoRtl = bSave; + } + return pElem; +} + + +void SbiRuntime::SetupArgs( SbxVariable* p, sal_uInt32 nOp1 ) +{ + if( nOp1 & 0x8000 ) + { + if( !refArgv.is() ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + } + bool bHasNamed = false; + sal_uInt32 i; + sal_uInt32 nArgCount = refArgv->Count32(); + for( i = 1 ; i < nArgCount ; i++ ) + { + if( !refArgv->GetAlias32(i).isEmpty() ) + { + bHasNamed = true; break; + } + } + if( bHasNamed ) + { + SbxInfo* pInfo = p->GetInfo(); + if( !pInfo ) + { + bool bError_ = true; + + SbUnoMethod* pUnoMethod = dynamic_cast<SbUnoMethod*>( p ); + SbUnoProperty* pUnoProperty = dynamic_cast<SbUnoProperty*>( p ); + if( pUnoMethod || pUnoProperty ) + { + SbUnoObject* pParentUnoObj = dynamic_cast<SbUnoObject*>( p->GetParent() ); + if( pParentUnoObj ) + { + Any aUnoAny = pParentUnoObj->getUnoAny(); + Reference< XInvocation > xInvocation; + aUnoAny >>= xInvocation; + if( xInvocation.is() ) // TODO: if( xOLEAutomation.is() ) + { + bError_ = false; + + sal_uInt32 nCurPar = 1; + AutomationNamedArgsSbxArray* pArg = + new AutomationNamedArgsSbxArray( nArgCount ); + OUString* pNames = pArg->getNames().getArray(); + for( i = 1 ; i < nArgCount ; i++ ) + { + SbxVariable* pVar = refArgv->Get32( i ); + OUString aName = refArgv->GetAlias32(i); + if (!aName.isEmpty()) + { + pNames[i] = aName; + } + pArg->Put32( pVar, nCurPar++ ); + } + refArgv = pArg; + } + } + } + else if( bVBAEnabled && p->GetType() == SbxOBJECT && (dynamic_cast<const SbxMethod*>( p) == nullptr || !p->IsBroadcaster()) ) + { + // Check for default method with named parameters + SbxBaseRef xObj = p->GetObject(); + if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( xObj.get() )) + { + Any aAny = pUnoObj->getUnoAny(); + + if( aAny.getValueType().getTypeClass() == TypeClass_INTERFACE ) + { + Reference< XDefaultMethod > xDfltMethod( aAny, UNO_QUERY ); + + OUString sDefaultMethod; + if ( xDfltMethod.is() ) + { + sDefaultMethod = xDfltMethod->getDefaultMethodName(); + } + if ( !sDefaultMethod.isEmpty() ) + { + SbxVariable* meth = pUnoObj->Find( sDefaultMethod, SbxClassType::Method ); + if( meth != nullptr ) + { + pInfo = meth->GetInfo(); + } + if( pInfo ) + { + bError_ = false; + } + } + } + } + } + if( bError_ ) + { + Error( ERRCODE_BASIC_NO_NAMED_ARGS ); + } + } + else + { + sal_uInt32 nCurPar = 1; + SbxArray* pArg = new SbxArray; + for( i = 1 ; i < nArgCount ; i++ ) + { + SbxVariable* pVar = refArgv->Get32( i ); + OUString aName = refArgv->GetAlias32(i); + if (!aName.isEmpty()) + { + // nCurPar is set to the found parameter + sal_uInt16 j = 1; + const SbxParamInfo* pParam = pInfo->GetParam( j ); + while( pParam ) + { + if( pParam->aName.equalsIgnoreAsciiCase( aName ) ) + { + nCurPar = j; + break; + } + pParam = pInfo->GetParam( ++j ); + } + if( !pParam ) + { + Error( ERRCODE_BASIC_NAMED_NOT_FOUND ); break; + } + } + pArg->Put32( pVar, nCurPar++ ); + } + refArgv = pArg; + } + } + // own var as parameter 0 + refArgv->Put32( p, 0 ); + p->SetParameters( refArgv.get() ); + PopArgv(); + } + else + { + p->SetParameters( nullptr ); + } +} + +// getting an array element + +SbxVariable* SbiRuntime::CheckArray( SbxVariable* pElem ) +{ + SbxArray* pPar; + if( ( pElem->GetType() & SbxARRAY ) && refRedim.get() != pElem ) + { + SbxBase* pElemObj = pElem->GetObject(); + SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( pElemObj ); + pPar = pElem->GetParameters(); + if( pDimArray ) + { + // parameters may be missing, if an array is + // passed as an argument + if( pPar ) + pElem = pDimArray->Get( pPar ); + } + else + { + SbxArray* pArray = dynamic_cast<SbxArray*>( pElemObj ); + if( pArray ) + { + if( !pPar ) + { + Error( ERRCODE_BASIC_OUT_OF_RANGE ); + pElem = new SbxVariable; + } + else + { + pElem = pArray->Get32( pPar->Get32( 1 )->GetInteger() ); + } + } + } + + // #42940, set parameter 0 to NULL so that var doesn't contain itself + if( pPar ) + { + pPar->Put32( nullptr, 0 ); + } + } + // consider index-access for UnoObjects + else if( pElem->GetType() == SbxOBJECT && + dynamic_cast<const SbxMethod*>( pElem) == nullptr && + ( !bVBAEnabled || dynamic_cast<const SbxProperty*>( pElem) == nullptr ) ) + { + pPar = pElem->GetParameters(); + if ( pPar ) + { + // is it a uno-object? + SbxBaseRef pObj = pElem->GetObject(); + if( pObj.is() ) + { + if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pObj.get())) + { + Any aAny = pUnoObj->getUnoAny(); + + if( aAny.getValueType().getTypeClass() == TypeClass_INTERFACE ) + { + Reference< XIndexAccess > xIndexAccess( aAny, UNO_QUERY ); + if ( !bVBAEnabled ) + { + if( xIndexAccess.is() ) + { + sal_uInt32 nParamCount = pPar->Count32() - 1; + if( nParamCount != 1 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return pElem; + } + + // get index + sal_Int32 nIndex = pPar->Get32( 1 )->GetLong(); + Reference< XInterface > xRet; + try + { + Any aAny2 = xIndexAccess->getByIndex( nIndex ); + aAny2 >>= xRet; + } + catch (const IndexOutOfBoundsException&) + { + // usually expect converting problem + StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE ); + } + + // #57847 always create a new variable, else error + // due to PutObject(NULL) at ReadOnly-properties + pElem = new SbxVariable( SbxVARIANT ); + if( xRet.is() ) + { + aAny <<= xRet; + + // #67173 don't specify a name so that the real class name is entered + SbxObjectRef xWrapper = static_cast<SbxObject*>(new SbUnoObject( OUString(), aAny )); + pElem->PutObject( xWrapper.get() ); + } + else + { + pElem->PutObject( nullptr ); + } + } + } + else + { + // check if there isn't a default member between the current variable + // and the params, e.g. + // Dim rst1 As New ADODB.Recordset + // " + // val = rst1("FirstName") + // has the default 'Fields' member between rst1 and '("FirstName")' + Any x = aAny; + SbxVariable* pDflt = getDefaultProp( pElem ); + if ( pDflt ) + { + pDflt->Broadcast( SfxHintId::BasicDataWanted ); + SbxBaseRef pDfltObj = pDflt->GetObject(); + if( pDfltObj.is() ) + { + if (SbUnoObject* pSbObj = dynamic_cast<SbUnoObject*>(pDfltObj.get())) + { + pUnoObj = pSbObj; + Any aUnoAny = pUnoObj->getUnoAny(); + + if( aUnoAny.getValueType().getTypeClass() == TypeClass_INTERFACE ) + x = aUnoAny; + pElem = pDflt; + } + } + } + OUString sDefaultMethod; + + Reference< XDefaultMethod > xDfltMethod( x, UNO_QUERY ); + + if ( xDfltMethod.is() ) + { + sDefaultMethod = xDfltMethod->getDefaultMethodName(); + } + else if( xIndexAccess.is() ) + { + sDefaultMethod = "getByIndex"; + } + if ( !sDefaultMethod.isEmpty() ) + { + SbxVariable* meth = pUnoObj->Find( sDefaultMethod, SbxClassType::Method ); + SbxVariableRef refTemp = meth; + if ( refTemp.is() ) + { + meth->SetParameters( pPar ); + SbxVariable* pNew = new SbxMethod( *static_cast<SbxMethod*>(meth) ); + pElem = pNew; + } + } + } + } + + // #42940, set parameter 0 to NULL so that var doesn't contain itself + pPar->Put32( nullptr, 0 ); + } + else if (BasicCollection* pCol = dynamic_cast<BasicCollection*>(pObj.get())) + { + pElem = new SbxVariable( SbxVARIANT ); + pPar->Put32( pElem, 0 ); + pCol->CollItem( pPar ); + } + } + else if( bVBAEnabled ) // !pObj + { + SbxArray* pParam = pElem->GetParameters(); + if( pParam != nullptr && !pElem->IsSet( SbxFlagBits::VarToDim ) ) + { + Error( ERRCODE_BASIC_NO_OBJECT ); + } + } + } + } + + return pElem; +} + +// loading an element from the runtime-library (+StringID+type) + +void SbiRuntime::StepRTL( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + PushVar( FindElement( rBasic.pRtl.get(), nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED, false ) ); +} + +void SbiRuntime::StepFIND_Impl( SbxObject* pObj, sal_uInt32 nOp1, sal_uInt32 nOp2, + ErrCode nNotFound, bool bStatic ) +{ + if( !refLocals.is() ) + { + refLocals = new SbxArray; + } + PushVar( FindElement( pObj, nOp1, nOp2, nNotFound, true/*bLocal*/, bStatic ) ); +} +// loading a local/global variable (+StringID+type) + +void SbiRuntime::StepFIND( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED ); +} + +// Search inside a class module (CM) to enable global search in time +void SbiRuntime::StepFIND_CM( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + + SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pMod ); + if( pClassModuleObject ) + { + pMod->SetFlag( SbxFlagBits::GlobalSearch ); + } + StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED); + + if( pClassModuleObject ) + { + pMod->ResetFlag( SbxFlagBits::GlobalSearch ); + } +} + +void SbiRuntime::StepFIND_STATIC( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + StepFIND_Impl( pMod, nOp1, nOp2, ERRCODE_BASIC_PROC_UNDEFINED, true ); +} + +// loading an object-element (+StringID+type) +// the object lies on TOS + +void SbiRuntime::StepELEM( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + SbxVariableRef pObjVar = PopVar(); + + SbxObject* pObj = dynamic_cast<SbxObject*>( pObjVar.get() ); + if( !pObj ) + { + SbxBase* pObjVarObj = pObjVar->GetObject(); + pObj = dynamic_cast<SbxObject*>( pObjVarObj ); + } + + // #56368 save reference at StepElem, otherwise objects could + // lose their reference too early in qualification chains like + // ActiveComponent.Selection(0).Text + // #74254 now per list + if( pObj ) + { + aRefSaved.emplace_back(pObj ); + } + PushVar( FindElement( pObj, nOp1, nOp2, ERRCODE_BASIC_NO_METHOD, false ) ); +} + +/** Loading of a parameter (+offset+type) + If the data type is wrong, create a copy and search for optionals including + the default value. The data type SbxEMPTY shows that no parameters are given. + Get( 0 ) may be EMPTY + + @param nOp1 + the index of the current parameter being processed, + where the entry of the index 0 is for the return value. + + @param nOp2 + the data type of the parameter. + */ +void SbiRuntime::StepPARAM( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + sal_uInt16 nIdx = static_cast<sal_uInt16>( nOp1 & 0x7FFF ); + SbxDataType eType = static_cast<SbxDataType>(nOp2); + SbxVariable* pVar; + + // #57915 solve missing in a cleaner way + sal_uInt32 nParamCount = refParams->Count32(); + if( nIdx >= nParamCount ) + { + sal_uInt16 iLoop = nIdx; + while( iLoop >= nParamCount ) + { + pVar = new SbxVariable(); + pVar->PutErr( 448 ); // like in VB: Error-Code 448 (ERRCODE_BASIC_NAMED_NOT_FOUND) + // tdf#79426, tdf#125180 - add additional information about a missing parameter + SetIsMissing( pVar ); + refParams->Put32( pVar, iLoop ); + iLoop--; + } + } + pVar = refParams->Get32( nIdx ); + + // tdf#79426, tdf#125180 - check for optionals only if the parameter is actually missing + if( pVar->GetType() == SbxERROR && IsMissing( pVar, 1 ) && nIdx ) + { + // if there's a parameter missing, it can be OPTIONAL + bool bOpt = false; + if( pMeth ) + { + SbxInfo* pInfo = pMeth->GetInfo(); + if ( pInfo ) + { + const SbxParamInfo* pParam = pInfo->GetParam( nIdx ); + if( pParam && ( pParam->nFlags & SbxFlagBits::Optional ) ) + { + // tdf#136143 - reset SbxFlagBits::Fixed in order to prevent type conversion errors + pVar->ResetFlag( SbxFlagBits::Fixed ); + // Default value? + sal_uInt16 nDefaultId = static_cast<sal_uInt16>(pParam->nUserData & 0x0ffff); + if( nDefaultId > 0 ) + { + OUString aDefaultStr = pImg->GetString( nDefaultId ); + pVar = new SbxVariable(pParam-> eType); + pVar->PutString( aDefaultStr ); + refParams->Put32( pVar, nIdx ); + } + else if ( SbiRuntime::isVBAEnabled() && eType != SbxVARIANT ) + { + // tdf#36737 - initialize the parameter with the default value of its type + pVar = new SbxVariable( pParam->eType ); + refParams->Put32( pVar, nIdx ); + } + bOpt = true; + } + } + } + if( !bOpt ) + { + Error( ERRCODE_BASIC_NOT_OPTIONAL ); + } + } + else if( eType != SbxVARIANT && static_cast<SbxDataType>(pVar->GetType() & 0x0FFF ) != eType ) + { + SbxVariable* q = new SbxVariable( eType ); + aRefSaved.emplace_back(q ); + *q = *pVar; + pVar = q; + if ( nIdx ) + { + refParams->Put32( pVar, nIdx ); + } + } + SetupArgs( pVar, nOp1 ); + PushVar( CheckArray( pVar ) ); +} + +// Case-Test (+True-Target+Test-Opcode) + +void SbiRuntime::StepCASEIS( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + if( !refCaseStk.is() || !refCaseStk->Count32() ) + { + StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR ); + } + else + { + SbxVariableRef xComp = PopVar(); + SbxVariableRef xCase = refCaseStk->Get32( refCaseStk->Count32() - 1 ); + if( xCase->Compare( static_cast<SbxOperator>(nOp2), *xComp ) ) + { + StepJUMP( nOp1 ); + } + } +} + +// call of a DLL-procedure (+StringID+type) +// the StringID's MSB shows that Argv is occupied + +void SbiRuntime::StepCALL( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + OUString aName = pImg->GetString( static_cast<short>( nOp1 & 0x7FFF ) ); + SbxArray* pArgs = nullptr; + if( nOp1 & 0x8000 ) + { + pArgs = refArgv.get(); + } + DllCall( aName, aLibName, pArgs, static_cast<SbxDataType>(nOp2), false ); + aLibName.clear(); + if( nOp1 & 0x8000 ) + { + PopArgv(); + } +} + +// call of a DLL-procedure after CDecl (+StringID+type) + +void SbiRuntime::StepCALLC( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + OUString aName = pImg->GetString( static_cast<short>( nOp1 & 0x7FFF ) ); + SbxArray* pArgs = nullptr; + if( nOp1 & 0x8000 ) + { + pArgs = refArgv.get(); + } + DllCall( aName, aLibName, pArgs, static_cast<SbxDataType>(nOp2), true ); + aLibName.clear(); + if( nOp1 & 0x8000 ) + { + PopArgv(); + } +} + + +// beginning of a statement (+Line+Col) + +void SbiRuntime::StepSTMNT( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + // If the Expr-Stack at the beginning of a statement contains a variable, + // some fool has called X as a function, although it's a variable! + bool bFatalExpr = false; + OUString sUnknownMethodName; + if( nExprLvl > 1 ) + { + bFatalExpr = true; + } + else if( nExprLvl ) + { + SbxVariable* p = refExprStk->Get32( 0 ); + if( p->GetRefCount() > 1 && + refLocals.is() && refLocals->Find( p->GetName(), p->GetClass() ) ) + { + sUnknownMethodName = p->GetName(); + bFatalExpr = true; + } + } + + ClearExprStack(); + + aRefSaved.clear(); + + // We have to cancel hard here because line and column + // would be wrong later otherwise! + if( bFatalExpr) + { + StarBASIC::FatalError( ERRCODE_BASIC_NO_METHOD, sUnknownMethodName ); + return; + } + pStmnt = pCode - 9; + sal_uInt16 nOld = nLine; + nLine = static_cast<short>( nOp1 ); + + // #29955 & 0xFF, to filter out for-loop-level + nCol1 = static_cast<short>( nOp2 & 0xFF ); + + // find the next STMNT-command to set the final column + // of this statement + + nCol2 = 0xffff; + sal_uInt16 n1, n2; + const sal_uInt8* p = pMod->FindNextStmnt( pCode, n1, n2 ); + if( p ) + { + if( n1 == nOp1 ) + { + // #29955 & 0xFF, to filter out for-loop-level + nCol2 = (n2 & 0xFF) - 1; + } + } + + // #29955 correct for-loop-level, #67452 NOT in the error-handler + if( !bInError ) + { + // (there's a difference here in case of a jump out of a loop) + sal_uInt16 nExspectedForLevel = static_cast<sal_uInt16>( nOp2 / 0x100 ); + if( !pGosubStk.empty() ) + { + nExspectedForLevel = nExspectedForLevel + pGosubStk.back().nStartForLvl; + } + + // if the actual for-level is too small it'd jump out + // of a loop -> corrected + while( nForLvl > nExspectedForLevel ) + { + PopFor(); + } + } + + // 16.10.96: #31460 new concept for StepInto/Over/Out + // see explanation at _ImplGetBreakCallLevel + if( pInst->nCallLvl <= pInst->nBreakCallLvl ) + { + StarBASIC* pStepBasic = GetCurrentBasic( &rBasic ); + BasicDebugFlags nNewFlags = pStepBasic->StepPoint( nLine, nCol1, nCol2 ); + + pInst->CalcBreakCallLevel( nNewFlags ); + } + + // break points only at STMNT-commands in a new line! + else if( ( nOp1 != nOld ) + && ( nFlags & BasicDebugFlags::Break ) + && pMod->IsBP( static_cast<sal_uInt16>( nOp1 ) ) ) + { + StarBASIC* pBreakBasic = GetCurrentBasic( &rBasic ); + BasicDebugFlags nNewFlags = pBreakBasic->BreakPoint( nLine, nCol1, nCol2 ); + + pInst->CalcBreakCallLevel( nNewFlags ); + } +} + +// (+StreamMode+Flags) +// Stack: block length +// channel number +// file name + +void SbiRuntime::StepOPEN( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + SbxVariableRef pName = PopVar(); + SbxVariableRef pChan = PopVar(); + SbxVariableRef pLen = PopVar(); + short nBlkLen = pLen->GetInteger(); + short nChan = pChan->GetInteger(); + OString aName(OUStringToOString(pName->GetOUString(), osl_getThreadTextEncoding())); + pIosys->Open( nChan, aName, static_cast<StreamMode>( nOp1 ), + static_cast<SbiStreamFlags>( nOp2 ), nBlkLen ); + Error( pIosys->GetError() ); +} + +// create object (+StringID+StringID) + +void SbiRuntime::StepCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + OUString aClass( pImg->GetString( static_cast<short>( nOp2 ) ) ); + SbxObject *pObj = SbxBase::CreateObject( aClass ); + if( !pObj ) + { + Error( ERRCODE_BASIC_INVALID_OBJECT ); + } + else + { + OUString aName( pImg->GetString( static_cast<short>( nOp1 ) ) ); + pObj->SetName( aName ); + // the object must be able to call the BASIC + pObj->SetParent( &rBasic ); + SbxVariable* pNew = new SbxVariable; + pNew->PutObject( pObj ); + PushVar( pNew ); + } +} + +void SbiRuntime::StepDCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + StepDCREATE_IMPL( nOp1, nOp2 ); +} + +void SbiRuntime::StepDCREATE_REDIMP( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + StepDCREATE_IMPL( nOp1, nOp2 ); +} + +// #56204 create object array (+StringID+StringID), DCREATE == Dim-Create +void SbiRuntime::StepDCREATE_IMPL( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + SbxVariableRef refVar = PopVar(); + + DimImpl( refVar ); + + // fill the array with instances of the requested class + SbxBase* pObj = refVar->GetObject(); + if (!pObj) + { + StarBASIC::Error( ERRCODE_BASIC_INVALID_OBJECT ); + return; + } + + SbxDimArray* pArray = dynamic_cast<SbxDimArray*>(pObj); + if (!pArray) + return; + + const sal_Int32 nDims = pArray->GetDims32(); + sal_Int32 nTotalSize = nDims > 0 ? 1 : 0; + + // must be a one-dimensional array + sal_Int32 nLower, nUpper; + for( sal_Int32 i = 0 ; i < nDims ; ++i ) + { + pArray->GetDim32( i+1, nLower, nUpper ); + const sal_Int32 nSize = nUpper - nLower + 1; + nTotalSize *= nSize; + } + + // Optimization: pre-allocate underlying container + if (nTotalSize > 0) + pArray->SbxArray::GetRef32(nTotalSize - 1); + + // First, fill those parts of the array that are preserved + bool bWasError = false; + const bool bRestored = implRestorePreservedArray(pArray, refRedimpArray, &bWasError); + if (bWasError) + nTotalSize = 0; // on error, don't create objects + + // create objects and insert them into the array + OUString aClass( pImg->GetString( static_cast<short>( nOp2 ) ) ); + OUString aName; + for( sal_Int32 i = 0 ; i < nTotalSize ; ++i ) + { + if (!bRestored || !pArray->SbxArray::GetRef32(i)) // For those left unset after preserve + { + SbxObject* pClassObj = SbxBase::CreateObject(aClass); + if (!pClassObj) + { + Error(ERRCODE_BASIC_INVALID_OBJECT); + break; + } + else + { + if (aName.isEmpty()) + aName = pImg->GetString(static_cast<short>(nOp1)); + pClassObj->SetName(aName); + // the object must be able to call the basic + pClassObj->SetParent(&rBasic); + pArray->SbxArray::Put32(pClassObj, i); + } + } + } +} + +void SbiRuntime::StepTCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + OUString aName( pImg->GetString( static_cast<short>( nOp1 ) ) ); + OUString aClass( pImg->GetString( static_cast<short>( nOp2 ) ) ); + + SbxObject* pCopyObj = createUserTypeImpl( aClass ); + if( pCopyObj ) + { + pCopyObj->SetName( aName ); + } + SbxVariable* pNew = new SbxVariable; + pNew->PutObject( pCopyObj ); + pNew->SetDeclareClassName( aClass ); + PushVar( pNew ); +} + +void SbiRuntime::implHandleSbxFlags( SbxVariable* pVar, SbxDataType t, sal_uInt32 nOp2 ) +{ + bool bWithEvents = ((t & 0xff) == SbxOBJECT && (nOp2 & SBX_TYPE_WITH_EVENTS_FLAG) != 0); + if( bWithEvents ) + { + pVar->SetFlag( SbxFlagBits::WithEvents ); + } + bool bDimAsNew = ((nOp2 & SBX_TYPE_DIM_AS_NEW_FLAG) != 0); + if( bDimAsNew ) + { + pVar->SetFlag( SbxFlagBits::DimAsNew ); + } + bool bFixedString = ((t & 0xff) == SbxSTRING && (nOp2 & SBX_FIXED_LEN_STRING_FLAG) != 0); + if( bFixedString ) + { + sal_uInt16 nCount = static_cast<sal_uInt16>( nOp2 >> 17 ); // len = all bits above 0x10000 + OUStringBuffer aBuf; + comphelper::string::padToLength(aBuf, nCount); + pVar->PutString(aBuf.makeStringAndClear()); + } + + bool bVarToDim = ((nOp2 & SBX_TYPE_VAR_TO_DIM_FLAG) != 0); + if( bVarToDim ) + { + pVar->SetFlag( SbxFlagBits::VarToDim ); + } +} + +// establishing a local variable (+StringID+type) + +void SbiRuntime::StepLOCAL( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + if( !refLocals.is() ) + { + refLocals = new SbxArray; + } + OUString aName( pImg->GetString( static_cast<short>( nOp1 ) ) ); + if( refLocals->Find( aName, SbxClassType::DontCare ) == nullptr ) + { + SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff); + SbxVariable* p = new SbxVariable( t ); + p->SetName( aName ); + implHandleSbxFlags( p, t, nOp2 ); + refLocals->Put32( p, refLocals->Count32() ); + } +} + +// establishing a module-global variable (+StringID+type) + +void SbiRuntime::StepPUBLIC_Impl( sal_uInt32 nOp1, sal_uInt32 nOp2, bool bUsedForClassModule ) +{ + OUString aName( pImg->GetString( static_cast<short>( nOp1 ) ) ); + SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff); + bool bFlag = pMod->IsSet( SbxFlagBits::NoModify ); + pMod->SetFlag( SbxFlagBits::NoModify ); + SbxVariableRef p = pMod->Find( aName, SbxClassType::Property ); + if( p.is() ) + { + pMod->Remove (p.get()); + } + SbProperty* pProp = pMod->GetProperty( aName, t ); + if( !bUsedForClassModule ) + { + pProp->SetFlag( SbxFlagBits::Private ); + } + if( !bFlag ) + { + pMod->ResetFlag( SbxFlagBits::NoModify ); + } + if( pProp ) + { + pProp->SetFlag( SbxFlagBits::DontStore ); + // from 2.7.1996: HACK because of 'reference can't be saved' + pProp->SetFlag( SbxFlagBits::NoModify); + + implHandleSbxFlags( pProp, t, nOp2 ); + } +} + +void SbiRuntime::StepPUBLIC( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + StepPUBLIC_Impl( nOp1, nOp2, false ); +} + +void SbiRuntime::StepPUBLIC_P( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + // Creates module variable that isn't reinitialised when + // between invocations ( for VBASupport & document basic only ) + if( pMod->pImage->bFirstInit ) + { + bool bUsedForClassModule = pImg->IsFlag( SbiImageFlags::CLASSMODULE ); + StepPUBLIC_Impl( nOp1, nOp2, bUsedForClassModule ); + } +} + +// establishing a global variable (+StringID+type) + +void SbiRuntime::StepGLOBAL( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + if( pImg->IsFlag( SbiImageFlags::CLASSMODULE ) ) + { + StepPUBLIC_Impl( nOp1, nOp2, true ); + } + OUString aName( pImg->GetString( static_cast<short>( nOp1 ) ) ); + SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff); + + // Store module scope variables at module scope + // in non vba mode these are stored at the library level :/ + // not sure if this really should not be enabled for ALL basic + SbxObject* pStorage = &rBasic; + if ( SbiRuntime::isVBAEnabled() ) + { + pStorage = pMod; + pMod->AddVarName( aName ); + } + + bool bFlag = pStorage->IsSet( SbxFlagBits::NoModify ); + rBasic.SetFlag( SbxFlagBits::NoModify ); + SbxVariableRef p = pStorage->Find( aName, SbxClassType::Property ); + if( p.is() ) + { + pStorage->Remove (p.get()); + } + p = pStorage->Make( aName, SbxClassType::Property, t ); + if( !bFlag ) + { + pStorage->ResetFlag( SbxFlagBits::NoModify ); + } + if( p.is() ) + { + p->SetFlag( SbxFlagBits::DontStore ); + // from 2.7.1996: HACK because of 'reference can't be saved' + p->SetFlag( SbxFlagBits::NoModify); + } +} + + +// Creates global variable that isn't reinitialised when +// basic is restarted, P=PERSIST (+StringID+Typ) + +void SbiRuntime::StepGLOBAL_P( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + if( pMod->pImage->bFirstInit ) + { + StepGLOBAL( nOp1, nOp2 ); + } +} + + +// Searches for global variable, behavior depends on the fact +// if the variable is initialised for the first time + +void SbiRuntime::StepFIND_G( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + if( pMod->pImage->bFirstInit ) + { + // Behave like always during first init + StepFIND( nOp1, nOp2 ); + } + else + { + // Return dummy variable + SbxDataType t = static_cast<SbxDataType>(nOp2); + OUString aName( pImg->GetString( static_cast<short>( nOp1 & 0x7FFF ) ) ); + + SbxVariable* pDummyVar = new SbxVariable( t ); + pDummyVar->SetName( aName ); + PushVar( pDummyVar ); + } +} + + +SbxVariable* SbiRuntime::StepSTATIC_Impl( + OUString const & aName, SbxDataType t, sal_uInt32 nOp2 ) +{ + SbxVariable* p = nullptr; + if ( pMeth ) + { + SbxArray* pStatics = pMeth->GetStatics(); + if( pStatics && ( pStatics->Find( aName, SbxClassType::DontCare ) == nullptr ) ) + { + p = new SbxVariable( t ); + if( t != SbxVARIANT ) + { + p->SetFlag( SbxFlagBits::Fixed ); + } + p->SetName( aName ); + implHandleSbxFlags( p, t, nOp2 ); + pStatics->Put32( p, pStatics->Count32() ); + } + } + return p; +} +// establishing a static variable (+StringID+type) +void SbiRuntime::StepSTATIC( sal_uInt32 nOp1, sal_uInt32 nOp2 ) +{ + OUString aName( pImg->GetString( static_cast<short>( nOp1 ) ) ); + SbxDataType t = static_cast<SbxDataType>(nOp2 & 0xffff); + StepSTATIC_Impl( aName, t, nOp2 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/stdobj.cxx b/basic/source/runtime/stdobj.cxx new file mode 100644 index 000000000..8a9513e81 --- /dev/null +++ b/basic/source/runtime/stdobj.cxx @@ -0,0 +1,898 @@ +/* -*- 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 <runtime.hxx> +#include <stdobj.hxx> +#include <sbstdobj.hxx> +#include <rtlproto.hxx> +#include <sbintern.hxx> +// The nArgs-field of a table entry is encrypted as follows: +// At the moment it is assumed that properties don't need any +// parameters! + +// previously ARGSMASK_ was 0x007F ( e.g. up to 127 args ) however 63 should be +// enough, if not we need to increase the size of nArgs member in the Methods +// struct below. +// note: the limitation of 63 args is only for RTL functions defined here and +// does NOT impose a limit on User defined procedures ). This changes is to +// allow us space for a flag to blacklist some functions in vba mode + +#define ARGSMASK_ 0x003F // 63 Arguments +#define COMPTMASK_ 0x00C0 // COMPATIBILITY mask +#define COMPATONLY_ 0x0080 // procedure is visible in vba mode only +#define NORMONLY_ 0x0040 // procedure is visible in normal mode only + +#define RWMASK_ 0x0F00 // mask for R/W-bits +#define TYPEMASK_ 0xF000 // mask for the entry's type + +#define OPT_ 0x0400 // parameter is optional +#define CONST_ 0x0800 // property is const +#define METHOD_ 0x3000 +#define PROPERTY_ 0x4000 +#define OBJECT_ 0x8000 + // combination of bits above: +#define FUNCTION_ 0x1100 +#define LFUNCTION_ 0x1300 // mask for function which also works as Lvalue +#define SUB_ 0x2100 +#define ROPROP_ 0x4100 // mask Read Only-Property +#define RWPROP_ 0x4300 // mask Read/Write-Property +#define CPROP_ 0x4900 // mask for constant + +namespace { + +struct Methods { + const char* pName; + SbxDataType eType; + short nArgs; + RtlCall pFunc; + sal_uInt16 nHash; +}; + +} + +static Methods aMethods[] = { + +{ "Abs", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Abs),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "Array", SbxOBJECT, FUNCTION_, RTLNAME(Array),0 }, +{ "Asc", SbxLONG, 1 | FUNCTION_, RTLNAME(Asc),0 }, + { "string", SbxSTRING, 0,nullptr,0 }, +{ "AscW", SbxLONG, 1 | FUNCTION_ | COMPATONLY_, RTLNAME(Asc),0}, + { "string", SbxSTRING, 0,nullptr,0 }, +{ "Atn", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Atn),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "ATTR_ARCHIVE", SbxINTEGER, CPROP_, RTLNAME(ATTR_ARCHIVE),0 }, +{ "ATTR_DIRECTORY", SbxINTEGER, CPROP_, RTLNAME(ATTR_DIRECTORY),0 }, +{ "ATTR_HIDDEN", SbxINTEGER, CPROP_, RTLNAME(ATTR_HIDDEN),0 }, +{ "ATTR_NORMAL", SbxINTEGER, CPROP_, RTLNAME(ATTR_NORMAL),0 }, +{ "ATTR_READONLY", SbxINTEGER, CPROP_, RTLNAME(ATTR_READONLY),0 }, +{ "ATTR_SYSTEM", SbxINTEGER, CPROP_, RTLNAME(ATTR_SYSTEM),0 }, +{ "ATTR_VOLUME", SbxINTEGER, CPROP_, RTLNAME(ATTR_VOLUME),0 }, + +{ "Beep", SbxNULL, FUNCTION_, RTLNAME(Beep),0 }, +{ "Blue", SbxINTEGER, 1 | FUNCTION_ | NORMONLY_, RTLNAME(Blue),0 }, + { "RGB-Value", SbxLONG, 0,nullptr,0 }, + +{ "CallByName", SbxVARIANT, 3 | FUNCTION_, RTLNAME(CallByName),0 }, + { "Object", SbxOBJECT, 0,nullptr,0 }, + { "ProcedureName",SbxSTRING, 0,nullptr,0 }, + { "CallType", SbxINTEGER, 0,nullptr,0 }, +{ "CBool", SbxBOOL, 1 | FUNCTION_, RTLNAME(CBool),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CByte", SbxBYTE, 1 | FUNCTION_, RTLNAME(CByte),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CCur", SbxCURRENCY, 1 | FUNCTION_, RTLNAME(CCur),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CDate", SbxDATE, 1 | FUNCTION_, RTLNAME(CDate),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CDateFromUnoDate", SbxDATE, 1 | FUNCTION_, RTLNAME(CDateFromUnoDate),0 }, + { "UnoDate", SbxOBJECT, 0,nullptr,0 }, +{ "CDateToUnoDate", SbxOBJECT, 1 | FUNCTION_, RTLNAME(CDateToUnoDate),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, +{ "CDateFromUnoTime", SbxDATE, 1 | FUNCTION_, RTLNAME(CDateFromUnoTime),0 }, + { "UnoTime", SbxOBJECT, 0,nullptr,0 }, +{ "CDateToUnoTime", SbxOBJECT, 1 | FUNCTION_, RTLNAME(CDateToUnoTime),0 }, + { "Time", SbxDATE, 0,nullptr,0 }, +{ "CDateFromUnoDateTime", SbxDATE, 1 | FUNCTION_, RTLNAME(CDateFromUnoDateTime),0 }, + { "UnoDateTime", SbxOBJECT, 0,nullptr,0 }, +{ "CDateToUnoDateTime", SbxOBJECT, 1 | FUNCTION_, RTLNAME(CDateToUnoDateTime),0 }, + { "DateTime", SbxDATE, 0,nullptr,0 }, +{ "CDateFromIso", SbxDATE, 1 | FUNCTION_, RTLNAME(CDateFromIso),0 }, + { "IsoDate", SbxSTRING, 0,nullptr,0 }, +{ "CDateToIso", SbxSTRING, 1 | FUNCTION_, RTLNAME(CDateToIso),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, +{ "CDec", SbxDECIMAL, 1 | FUNCTION_, RTLNAME(CDec),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CDbl", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(CDbl),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CF_BITMAP", SbxINTEGER, CPROP_, RTLNAME(CF_BITMAP),0 }, +{ "CF_METAFILEPICT",SbxINTEGER, CPROP_, RTLNAME(CF_METAFILEPICT),0 }, +{ "CF_TEXT", SbxINTEGER, CPROP_, RTLNAME(CF_TEXT),0 }, +{ "ChDir", SbxNULL, 1 | FUNCTION_, RTLNAME(ChDir),0 }, + { "string", SbxSTRING, 0,nullptr,0 }, +{ "ChDrive", SbxNULL, 1 | FUNCTION_, RTLNAME(ChDrive),0 }, + { "string", SbxSTRING, 0,nullptr,0 }, + +{ "Choose", SbxVARIANT, 2 | FUNCTION_, RTLNAME(Choose),0 }, + { "Index", SbxINTEGER, 0,nullptr,0 }, + { "Expression", SbxVARIANT, 0,nullptr,0 }, + +{ "Chr", SbxSTRING, 1 | FUNCTION_, RTLNAME(Chr),0 }, + { "string", SbxINTEGER, 0,nullptr,0 }, +{ "ChrW", SbxSTRING, 1 | FUNCTION_ | COMPATONLY_, RTLNAME(ChrW),0}, + { "string", SbxINTEGER, 0,nullptr,0 }, + +{ "CInt", SbxINTEGER, 1 | FUNCTION_, RTLNAME(CInt),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CLEAR_ALLTABS", SbxINTEGER, CPROP_, RTLNAME(CLEAR_ALLTABS),0 }, +{ "CLEAR_TAB", SbxINTEGER, CPROP_, RTLNAME(CLEAR_TAB),0 }, +{ "CLng", SbxLONG, 1 | FUNCTION_, RTLNAME(CLng),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CompatibilityMode", SbxBOOL, 1 | FUNCTION_, RTLNAME(CompatibilityMode),0}, + { "bEnable", SbxBOOL, 0,nullptr,0 }, +{ "ConvertFromUrl", SbxSTRING, 1 | FUNCTION_, RTLNAME(ConvertFromUrl),0 }, + { "Url", SbxSTRING, 0,nullptr,0 }, +{ "ConvertToUrl", SbxSTRING, 1 | FUNCTION_, RTLNAME(ConvertToUrl),0 }, + { "SystemPath", SbxSTRING, 0,nullptr,0 }, +{ "Cos", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Cos),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "CreateObject", SbxOBJECT, 1 | FUNCTION_, RTLNAME( CreateObject ),0 }, + { "class", SbxSTRING, 0,nullptr,0 }, +{ "CreateUnoListener",SbxOBJECT, 1 | FUNCTION_, RTLNAME( CreateUnoListener ),0 }, + { "prefix", SbxSTRING, 0,nullptr,0 }, + { "typename", SbxSTRING, 0,nullptr,0 }, +{ "CreateUnoDialog",SbxOBJECT, 2 | FUNCTION_, RTLNAME( CreateUnoDialog ),0 }, + { "dialoglibrary",SbxOBJECT, 0,nullptr,0 }, + { "dialogname", SbxSTRING, 0,nullptr,0 }, +{ "CreateUnoService",SbxOBJECT, 1 | FUNCTION_, RTLNAME( CreateUnoService ),0 }, + { "servicename", SbxSTRING, 0,nullptr,0 }, +{ "CreateUnoServiceWithArguments",SbxOBJECT, 2 | FUNCTION_, RTLNAME( CreateUnoServiceWithArguments ),0 }, + { "servicename", SbxSTRING, 0,nullptr,0 }, + { "arguments", SbxARRAY, 0,nullptr,0 }, +{ "CreateUnoStruct",SbxOBJECT, 1 | FUNCTION_, RTLNAME( CreateUnoStruct ),0 }, + { "classname", SbxSTRING, 0,nullptr,0 }, +{ "CreateUnoValue", SbxOBJECT, 2 | FUNCTION_, RTLNAME( CreateUnoValue ),0 }, + { "type", SbxSTRING, 0,nullptr,0 }, + { "value", SbxVARIANT, 0,nullptr,0 }, +{ "CreatePropertySet",SbxOBJECT, 1 | FUNCTION_, RTLNAME( CreatePropertySet ),0 }, + { "values", SbxARRAY, 0,nullptr,0 }, +{ "CSng", SbxSINGLE, 1 | FUNCTION_, RTLNAME(CSng),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CStr", SbxSTRING, 1 | FUNCTION_, RTLNAME(CStr),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CurDir", SbxSTRING, 1 | FUNCTION_, RTLNAME(CurDir),0 }, + { "string", SbxSTRING, 0,nullptr,0 }, +{ "CVar", SbxVARIANT, 1 | FUNCTION_, RTLNAME(CVar),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "CVErr", SbxVARIANT, 1 | FUNCTION_, RTLNAME(CVErr),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, +{ "DDB", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, RTLNAME(DDB),0 }, + { "Cost", SbxDOUBLE, 0, nullptr,0 }, + { "Salvage", SbxDOUBLE, 0, nullptr,0 }, + { "Life", SbxDOUBLE, 0, nullptr,0 }, + { "Period", SbxDOUBLE, 0, nullptr,0 }, + { "Factor", SbxVARIANT, OPT_, nullptr,0 }, +{ "Date", SbxDATE, LFUNCTION_,RTLNAME(Date),0 }, +{ "DateAdd", SbxDATE, 3 | FUNCTION_, RTLNAME(DateAdd),0 }, + { "Interval", SbxSTRING, 0,nullptr,0 }, + { "Number", SbxLONG, 0,nullptr,0 }, + { "Date", SbxDATE, 0,nullptr,0 }, +{ "DateDiff", SbxDOUBLE, 5 | FUNCTION_, RTLNAME(DateDiff),0 }, + { "Interval", SbxSTRING, 0,nullptr,0 }, + { "Date1", SbxDATE, 0,nullptr,0 }, + { "Date2", SbxDATE, 0,nullptr,0 }, + { "Firstdayofweek" , SbxINTEGER, OPT_,nullptr,0 }, + { "Firstweekofyear", SbxINTEGER, OPT_,nullptr,0 }, +{ "DatePart", SbxLONG, 4 | FUNCTION_, RTLNAME(DatePart),0 }, + { "Interval", SbxSTRING, 0,nullptr,0 }, + { "Date", SbxDATE, 0,nullptr,0 }, + { "Firstdayofweek" , SbxINTEGER, OPT_, nullptr,0 }, + { "Firstweekofyear", SbxINTEGER, OPT_, nullptr,0 }, +{ "DateSerial", SbxDATE, 3 | FUNCTION_, RTLNAME(DateSerial),0 }, + { "Year", SbxINTEGER, 0,nullptr,0 }, + { "Month", SbxINTEGER, 0,nullptr,0 }, + { "Day", SbxINTEGER, 0,nullptr,0 }, +{ "DateValue", SbxDATE, 1 | FUNCTION_, RTLNAME(DateValue),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, +{ "Day", SbxINTEGER, 1 | FUNCTION_, RTLNAME(Day),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, +{ "Ddeexecute", SbxNULL, 2 | FUNCTION_, RTLNAME(DDEExecute),0 }, + { "Channel", SbxLONG, 0,nullptr,0 }, + { "Command", SbxSTRING, 0,nullptr,0 }, +{ "Ddeinitiate", SbxINTEGER, 2 | FUNCTION_, RTLNAME(DDEInitiate),0 }, + { "Application", SbxSTRING, 0,nullptr,0 }, + { "Topic", SbxSTRING, 0,nullptr,0 }, +{ "Ddepoke", SbxNULL, 3 | FUNCTION_, RTLNAME(DDEPoke),0 }, + { "Channel", SbxLONG, 0,nullptr,0 }, + { "Item", SbxSTRING, 0,nullptr,0 }, + { "Data", SbxSTRING, 0,nullptr,0 }, +{ "Dderequest", SbxSTRING, 2 | FUNCTION_, RTLNAME(DDERequest),0 }, + { "Channel", SbxLONG, 0,nullptr,0 }, + { "Item", SbxSTRING, 0,nullptr,0 }, +{ "Ddeterminate", SbxNULL, 1 | FUNCTION_, RTLNAME(DDETerminate),0 }, + { "Channel", SbxLONG, 0,nullptr,0 }, +{ "Ddeterminateall", SbxNULL, FUNCTION_, RTLNAME(DDETerminateAll),0 }, +{ "DimArray", SbxOBJECT, FUNCTION_, RTLNAME(DimArray),0 }, +{ "Dir", SbxSTRING, 2 | FUNCTION_, RTLNAME(Dir),0 }, + { "FileSpec", SbxSTRING, OPT_, nullptr,0 }, + { "attrmask", SbxINTEGER, OPT_, nullptr,0 }, +{ "DoEvents", SbxINTEGER, FUNCTION_, RTLNAME(DoEvents),0 }, +{ "DumpAllObjects", SbxEMPTY, 2 | SUB_, RTLNAME(DumpAllObjects),0 }, + { "FileSpec", SbxSTRING, 0,nullptr,0 }, + { "DumpAll", SbxINTEGER, OPT_, nullptr,0 }, + +{ "Empty", SbxVARIANT, CPROP_, RTLNAME(Empty),0 }, +{ "EqualUnoObjects",SbxBOOL, 2 | FUNCTION_, RTLNAME(EqualUnoObjects),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "EnableReschedule", SbxNULL, 1 | FUNCTION_, RTLNAME(EnableReschedule),0}, + { "bEnable", SbxBOOL, 0,nullptr,0 }, +{ "Environ", SbxSTRING, 1 | FUNCTION_, RTLNAME(Environ),0 }, + { "Environmentstring",SbxSTRING, 0,nullptr,0 }, +{ "EOF", SbxBOOL, 1 | FUNCTION_, RTLNAME(EOF),0 }, + { "Channel", SbxINTEGER, 0,nullptr,0 }, +{ "Erl", SbxLONG, ROPROP_, RTLNAME( Erl ),0 }, +{ "Err", SbxVARIANT, RWPROP_, RTLNAME( Err ),0 }, +{ "Error", SbxSTRING, 1 | FUNCTION_, RTLNAME( Error ),0 }, + { "code", SbxLONG, 0,nullptr,0 }, +{ "Exp", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Exp),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, + +{ "False", SbxBOOL, CPROP_, RTLNAME(False),0 }, +{ "FileAttr", SbxINTEGER, 2 | FUNCTION_, RTLNAME(FileAttr),0 }, + { "Channel", SbxINTEGER, 0,nullptr,0 }, + { "Attributes", SbxINTEGER, 0,nullptr,0 }, +{ "FileCopy", SbxNULL, 2 | FUNCTION_, RTLNAME(FileCopy),0 }, + { "Source", SbxSTRING, 0,nullptr,0 }, + { "Destination", SbxSTRING, 0,nullptr,0 }, +{ "FileDateTime", SbxSTRING, 1 | FUNCTION_, RTLNAME(FileDateTime),0 }, + { "filename", SbxSTRING, 0,nullptr,0 }, +{ "FileExists", SbxBOOL, 1 | FUNCTION_, RTLNAME(FileExists),0 }, + { "filename", SbxSTRING, 0,nullptr,0 }, +{ "FileLen", SbxLONG, 1 | FUNCTION_, RTLNAME(FileLen),0 }, + { "filename", SbxSTRING, 0,nullptr,0 }, +{ "FindObject", SbxOBJECT, 1 | FUNCTION_, RTLNAME(FindObject),0 }, + { "Name", SbxSTRING, 0,nullptr,0 }, +{ "FindPropertyObject", SbxOBJECT, 2 | FUNCTION_, RTLNAME(FindPropertyObject),0 }, + { "Object", SbxOBJECT, 0,nullptr,0 }, + { "Name", SbxSTRING, 0,nullptr,0 }, +{ "Fix", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Fix),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "Format", SbxSTRING, 2 | FUNCTION_, RTLNAME(Format),0 }, + { "expression", SbxVARIANT, 0,nullptr,0 }, + { "format", SbxSTRING, OPT_, nullptr,0 }, +{ "FormatDateTime", SbxSTRING, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(FormatDateTime),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, + { "NamedFormat", SbxINTEGER, OPT_, nullptr,0 }, +{ "FormatNumber", SbxSTRING, 5 | FUNCTION_ | COMPATONLY_, RTLNAME(FormatNumber), 0 }, + { "expression", SbxDOUBLE, 0, nullptr, 0 }, + { "numDigitsAfterDecimal", SbxINTEGER, OPT_, nullptr, 0 }, + { "includeLeadingDigit", SbxINTEGER, OPT_, nullptr, 0 }, // vbTriState + { "useParensForNegativeNumbers", SbxINTEGER, OPT_, nullptr, 0 }, // vbTriState + { "groupDigits", SbxINTEGER, OPT_, nullptr, 0 }, // vbTriState +{ "Frac", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Frac),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "FRAMEANCHORCHAR", SbxINTEGER, CPROP_, RTLNAME(FRAMEANCHORCHAR),0 }, +{ "FRAMEANCHORPAGE", SbxINTEGER, CPROP_, RTLNAME(FRAMEANCHORPAGE),0 }, +{ "FRAMEANCHORPARA", SbxINTEGER, CPROP_, RTLNAME(FRAMEANCHORPARA),0 }, +{ "FreeFile", SbxINTEGER, FUNCTION_, RTLNAME(FreeFile),0 }, +{ "FreeLibrary", SbxNULL, 1 | FUNCTION_, RTLNAME(FreeLibrary),0 }, + { "Modulename", SbxSTRING, 0,nullptr,0 }, + +{ "FV", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, RTLNAME(FV),0 }, + { "Rate", SbxDOUBLE, 0, nullptr,0 }, + { "NPer", SbxDOUBLE, 0, nullptr,0 }, + { "Pmt", SbxDOUBLE, 0, nullptr,0 }, + { "PV", SbxVARIANT, OPT_, nullptr,0 }, + { "Due", SbxVARIANT, OPT_, nullptr,0 }, +{ "Get", SbxNULL, 3 | FUNCTION_, RTLNAME(Get),0 }, + { "filenumber", SbxINTEGER, 0,nullptr,0 }, + { "recordnumber", SbxLONG, 0,nullptr,0 }, + { "variablename", SbxVARIANT, 0,nullptr,0 }, +{ "GetAttr", SbxINTEGER, 1 | FUNCTION_, RTLNAME(GetAttr),0 }, + { "filename", SbxSTRING, 0,nullptr,0 }, +{ "GetDefaultContext", SbxOBJECT, 0 | FUNCTION_, RTLNAME(GetDefaultContext),0 }, +{ "GetDialogZoomFactorX", SbxDOUBLE, FUNCTION_,RTLNAME(GetDialogZoomFactorX),0 }, +{ "GetDialogZoomFactorY", SbxDOUBLE, FUNCTION_,RTLNAME(GetDialogZoomFactorY),0 }, +{ "GetGUIType", SbxINTEGER, FUNCTION_,RTLNAME(GetGUIType),0 }, +{ "GetGUIVersion", SbxLONG, FUNCTION_,RTLNAME(GetGUIVersion),0 }, +{ "GetPathSeparator", SbxSTRING, FUNCTION_,RTLNAME(GetPathSeparator),0 }, +{ "GetProcessServiceManager", SbxOBJECT, 0 | FUNCTION_, RTLNAME(GetProcessServiceManager),0 }, +{ "GetSolarVersion", SbxLONG, FUNCTION_,RTLNAME(GetSolarVersion),0 }, +{ "GetSystemTicks", SbxLONG, FUNCTION_,RTLNAME(GetSystemTicks),0 }, +{ "GetSystemType", SbxINTEGER, FUNCTION_,RTLNAME(GetSystemType),0 }, +{ "GlobalScope", SbxOBJECT, FUNCTION_,RTLNAME(GlobalScope),0 }, +{ "Green", SbxINTEGER, 1 | FUNCTION_ | NORMONLY_, RTLNAME(Green),0 }, + { "RGB-Value", SbxLONG, 0,nullptr,0 }, + +{ "HasUnoInterfaces", SbxBOOL, 1 | FUNCTION_, RTLNAME(HasUnoInterfaces),0}, + { "InterfaceName",SbxSTRING, 0,nullptr,0 }, +{ "Hex", SbxSTRING, 1 | FUNCTION_, RTLNAME(Hex),0 }, + { "number", SbxLONG, 0,nullptr,0 }, +{ "Hour", SbxINTEGER, 1 | FUNCTION_, RTLNAME(Hour),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, + +{ "IDABORT", SbxINTEGER, CPROP_, RTLNAME(IDABORT),0 }, +{ "IDCANCEL", SbxINTEGER, CPROP_, RTLNAME(IDCANCEL),0 }, +{ "IDNO", SbxINTEGER, CPROP_, RTLNAME(IDNO),0 }, +{ "IDOK", SbxINTEGER, CPROP_, RTLNAME(IDOK),0 }, +{ "IDRETRY", SbxINTEGER, CPROP_, RTLNAME(IDRETRY),0 }, +{ "IDYES", SbxINTEGER, CPROP_, RTLNAME(IDYES),0 }, + +{ "Iif", SbxVARIANT, 3 | FUNCTION_, RTLNAME(Iif),0 }, + { "Bool", SbxBOOL, 0,nullptr,0 }, + { "Variant1", SbxVARIANT, 0,nullptr,0 }, + { "Variant2", SbxVARIANT, 0,nullptr,0 }, + +{ "Input", SbxSTRING, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(Input),0}, + { "Number", SbxLONG, 0,nullptr,0 }, + { "FileNumber", SbxLONG, 0,nullptr,0 }, +{ "InputBox", SbxSTRING, 5 | FUNCTION_, RTLNAME(InputBox),0 }, + { "Prompt", SbxSTRING, 0,nullptr,0 }, + { "Title", SbxSTRING, OPT_, nullptr,0 }, + { "Default", SbxSTRING, OPT_, nullptr,0 }, + { "XPosTwips", SbxLONG, OPT_, nullptr,0 }, + { "YPosTwips", SbxLONG, OPT_, nullptr,0 }, +{ "InStr", SbxLONG, 4 | FUNCTION_, RTLNAME(InStr),0 }, + { "Start", SbxSTRING, OPT_, nullptr,0 }, + { "String1", SbxSTRING, 0,nullptr,0 }, + { "String2", SbxSTRING, 0,nullptr,0 }, + { "Compare", SbxINTEGER, OPT_, nullptr,0 }, +{ "InStrRev", SbxLONG, 4 | FUNCTION_ | COMPATONLY_, RTLNAME(InStrRev),0}, + { "String1", SbxSTRING, 0,nullptr,0 }, + { "String2", SbxSTRING, 0,nullptr,0 }, + { "Start", SbxSTRING, OPT_, nullptr,0 }, + { "Compare", SbxINTEGER, OPT_, nullptr,0 }, +{ "Int", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Int),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "IPmt", SbxDOUBLE, 6 | FUNCTION_ | COMPATONLY_, RTLNAME(IPmt),0 }, + { "Rate", SbxDOUBLE, 0, nullptr,0 }, + { "Per", SbxDOUBLE, 0, nullptr,0 }, + { "NPer", SbxDOUBLE, 0, nullptr,0 }, + { "PV", SbxDOUBLE, 0, nullptr,0 }, + { "FV", SbxVARIANT, OPT_, nullptr,0 }, + { "Due", SbxVARIANT, OPT_, nullptr,0 }, +{ "IRR", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(IRR),0 }, + { "ValueArray", SbxARRAY, 0, nullptr,0 }, + { "Guess", SbxVARIANT, OPT_, nullptr,0 }, +{ "IsArray", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsArray),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "IsDate", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsDate),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "IsEmpty", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsEmpty),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "IsError", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsError),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "IsMissing", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsMissing),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "IsNull", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsNull),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "IsNumeric", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsNumeric),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "IsObject", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsObject),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "IsUnoStruct", SbxBOOL, 1 | FUNCTION_, RTLNAME(IsUnoStruct),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "Join", SbxSTRING, 2 | FUNCTION_, RTLNAME(Join),0 }, + { "list", SbxOBJECT, 0,nullptr,0 }, + { "delimiter", SbxSTRING, 0,nullptr,0 }, +{ "Kill", SbxNULL, 1 | FUNCTION_, RTLNAME(Kill),0 }, + { "filespec", SbxSTRING, 0,nullptr,0 }, +{ "LBound", SbxLONG, 1 | FUNCTION_, RTLNAME(LBound),0 }, + { "Variant", SbxVARIANT, 0,nullptr,0 }, +{ "LCase", SbxSTRING, 1 | FUNCTION_, RTLNAME(LCase),0 }, + { "string", SbxSTRING, 0,nullptr,0 }, +{ "Left", SbxSTRING, 2 | FUNCTION_, RTLNAME(Left),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, + { "Count", SbxLONG, 0,nullptr,0 }, +{ "Len", SbxLONG, 1 | FUNCTION_, RTLNAME(Len),0 }, + { "StringOrVariant", SbxVARIANT, 0,nullptr,0 }, +{ "LenB", SbxLONG, 1 | FUNCTION_, RTLNAME(Len),0 }, + { "StringOrVariant", SbxVARIANT, 0,nullptr,0 }, +{ "Load", SbxNULL, 1 | FUNCTION_, RTLNAME(Load),0 }, + { "object", SbxOBJECT, 0,nullptr,0 }, +{ "LoadPicture", SbxOBJECT, 1 | FUNCTION_, RTLNAME(LoadPicture),0 }, + { "string", SbxSTRING, 0,nullptr,0 }, +{ "Loc", SbxLONG, 1 | FUNCTION_, RTLNAME(Loc),0 }, + { "Channel", SbxINTEGER, 0,nullptr,0 }, +{ "Lof", SbxLONG, 1 | FUNCTION_, RTLNAME(Lof),0 }, + { "Channel", SbxINTEGER, 0,nullptr,0 }, +{ "Log", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Log),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "LTrim", SbxSTRING, 1 | FUNCTION_, RTLNAME(LTrim),0 }, + { "string", SbxSTRING, 0,nullptr,0 }, + +{ "MB_ABORTRETRYIGNORE", SbxINTEGER, CPROP_, RTLNAME(MB_ABORTRETRYIGNORE),0}, +{ "MB_APPLMODAL", SbxINTEGER, CPROP_, RTLNAME(MB_APPLMODAL),0 }, +{ "MB_DEFBUTTON1", SbxINTEGER, CPROP_, RTLNAME(MB_DEFBUTTON1),0 }, +{ "MB_DEFBUTTON2", SbxINTEGER, CPROP_, RTLNAME(MB_DEFBUTTON2),0 }, +{ "MB_DEFBUTTON3", SbxINTEGER, CPROP_, RTLNAME(MB_DEFBUTTON3),0 }, +{ "MB_ICONEXCLAMATION", SbxINTEGER, CPROP_, RTLNAME(MB_ICONEXCLAMATION),0}, +{ "MB_ICONINFORMATION", SbxINTEGER, CPROP_, RTLNAME(MB_ICONINFORMATION),0}, +{ "MB_ICONQUESTION",SbxINTEGER, CPROP_, RTLNAME(MB_ICONQUESTION),0 }, +{ "MB_ICONSTOP", SbxINTEGER, CPROP_, RTLNAME(MB_ICONSTOP),0 }, +{ "MB_OK", SbxINTEGER, CPROP_, RTLNAME(MB_OK),0 }, +{ "MB_OKCANCEL", SbxINTEGER, CPROP_, RTLNAME(MB_OKCANCEL),0 }, +{ "MB_RETRYCANCEL", SbxINTEGER, CPROP_, RTLNAME(MB_RETRYCANCEL),0 }, +{ "MB_SYSTEMMODAL", SbxINTEGER, CPROP_, RTLNAME(MB_SYSTEMMODAL),0 }, +{ "MB_YESNO", SbxINTEGER, CPROP_, RTLNAME(MB_YESNO),0 }, +{ "MB_YESNOCANCEL", SbxINTEGER, CPROP_, RTLNAME(MB_YESNOCANCEL),0 }, + +{ "Me", SbxOBJECT, 0 | FUNCTION_ | COMPATONLY_, RTLNAME(Me),0 }, +{ "Mid", SbxSTRING, 3 | LFUNCTION_,RTLNAME(Mid),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, + { "StartPos", SbxLONG, 0,nullptr,0 }, + { "Length", SbxLONG, OPT_, nullptr,0 }, +{ "Minute", SbxINTEGER, 1 | FUNCTION_, RTLNAME(Minute),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, +{ "MIRR", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(MIRR),0 }, + { "ValueArray", SbxARRAY, 0, nullptr,0 }, + { "FinanceRate", SbxDOUBLE, 0, nullptr,0 }, + { "ReinvestRate", SbxDOUBLE, 0, nullptr,0 }, +{ "MkDir", SbxNULL, 1 | FUNCTION_, RTLNAME(MkDir),0 }, + { "pathname", SbxSTRING, 0,nullptr,0 }, +{ "Month", SbxINTEGER, 1 | FUNCTION_, RTLNAME(Month),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, +{ "MonthName", SbxSTRING, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(MonthName),0 }, + { "Month", SbxINTEGER, 0,nullptr,0 }, + { "Abbreviate", SbxBOOL, OPT_, nullptr,0 }, +{ "MsgBox", SbxINTEGER, 5 | FUNCTION_, RTLNAME(MsgBox),0 }, + { "Prompt", SbxSTRING, 0,nullptr,0 }, + { "Buttons", SbxINTEGER, OPT_, nullptr,0 }, + { "Title", SbxSTRING, OPT_, nullptr,0 }, + { "Helpfile", SbxSTRING, OPT_, nullptr,0 }, + { "Context", SbxINTEGER, OPT_, nullptr,0 }, + +{ "Nothing", SbxOBJECT, CPROP_, RTLNAME(Nothing),0 }, +{ "Now", SbxDATE, FUNCTION_, RTLNAME(Now),0 }, +{ "NPer", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, RTLNAME(NPer),0 }, + { "Rate", SbxDOUBLE, 0, nullptr,0 }, + { "Pmt", SbxDOUBLE, 0, nullptr,0 }, + { "PV", SbxDOUBLE, 0, nullptr,0 }, + { "FV", SbxVARIANT, OPT_, nullptr,0 }, + { "Due", SbxVARIANT, OPT_, nullptr,0 }, +{ "NPV", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(NPV),0 }, + { "Rate", SbxDOUBLE, 0, nullptr,0 }, + { "ValueArray", SbxARRAY, 0, nullptr,0 }, +{ "Null", SbxNULL, CPROP_, RTLNAME(Null),0 }, + +{ "Oct", SbxSTRING, 1 | FUNCTION_, RTLNAME(Oct),0 }, + { "number", SbxLONG, 0,nullptr,0 }, + +{ "Partition", SbxSTRING, 4 | FUNCTION_, RTLNAME(Partition),0 }, + { "number", SbxLONG, 0,nullptr,0 }, + { "start", SbxLONG, 0,nullptr,0 }, + { "stop", SbxLONG, 0,nullptr,0 }, + { "interval", SbxLONG, 0,nullptr,0 }, +{ "Pi", SbxDOUBLE, CPROP_, RTLNAME(PI),0 }, + +{ "Pmt", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, RTLNAME(Pmt),0 }, + { "Rate", SbxDOUBLE, 0, nullptr,0 }, + { "NPer", SbxDOUBLE, 0, nullptr,0 }, + { "PV", SbxDOUBLE, 0, nullptr,0 }, + { "FV", SbxVARIANT, OPT_, nullptr,0 }, + { "Due", SbxVARIANT, OPT_, nullptr,0 }, + +{ "PPmt", SbxDOUBLE, 6 | FUNCTION_ | COMPATONLY_, RTLNAME(PPmt),0 }, + { "Rate", SbxDOUBLE, 0, nullptr,0 }, + { "Per", SbxDOUBLE, 0, nullptr,0 }, + { "NPer", SbxDOUBLE, 0, nullptr,0 }, + { "PV", SbxDOUBLE, 0, nullptr,0 }, + { "FV", SbxVARIANT, OPT_, nullptr,0 }, + { "Due", SbxVARIANT, OPT_, nullptr,0 }, + +{ "Put", SbxNULL, 3 | FUNCTION_, RTLNAME(Put),0 }, + { "filenumber", SbxINTEGER, 0,nullptr,0 }, + { "recordnumber", SbxLONG, 0,nullptr,0 }, + { "variablename", SbxVARIANT, 0,nullptr,0 }, + +{ "PV", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, RTLNAME(PV),0 }, + { "Rate", SbxDOUBLE, 0, nullptr,0 }, + { "NPer", SbxDOUBLE, 0, nullptr,0 }, + { "Pmt", SbxDOUBLE, 0, nullptr,0 }, + { "FV", SbxVARIANT, OPT_, nullptr,0 }, + { "Due", SbxVARIANT, OPT_, nullptr,0 }, + +{ "QBColor", SbxLONG, 1 | FUNCTION_, RTLNAME(QBColor),0 }, + { "number", SbxINTEGER, 0,nullptr,0 }, + +{ "Randomize", SbxNULL, 1 | FUNCTION_, RTLNAME(Randomize),0 }, + { "Number", SbxDOUBLE, OPT_, nullptr,0 }, +{ "Rate", SbxDOUBLE, 6 | FUNCTION_ | COMPATONLY_, RTLNAME(Rate),0 }, + { "NPer", SbxDOUBLE, 0, nullptr,0 }, + { "Pmt", SbxDOUBLE, 0, nullptr,0 }, + { "PV", SbxDOUBLE, 0, nullptr,0 }, + { "FV", SbxVARIANT, OPT_, nullptr,0 }, + { "Due", SbxVARIANT, OPT_, nullptr,0 }, + { "Guess", SbxVARIANT, OPT_, nullptr,0 }, +{ "Red", SbxINTEGER, 1 | FUNCTION_ | NORMONLY_, RTLNAME(Red),0 }, + { "RGB-Value", SbxLONG, 0,nullptr,0 }, +{ "Reset", SbxNULL, 0 | FUNCTION_, RTLNAME(Reset),0 }, +{ "ResolvePath", SbxSTRING, 1 | FUNCTION_, RTLNAME(ResolvePath),0 }, + { "Path", SbxSTRING, 0,nullptr,0 }, +{ "RGB", SbxLONG, 3 | FUNCTION_, RTLNAME(RGB),0 }, + { "Red", SbxINTEGER, 0,nullptr,0 }, + { "Green", SbxINTEGER, 0,nullptr,0 }, + { "Blue", SbxINTEGER, 0,nullptr,0 }, +{ "Replace", SbxSTRING, 6 | FUNCTION_, RTLNAME(Replace),0 }, + { "Expression", SbxSTRING, 0,nullptr,0 }, + { "Find", SbxSTRING, 0,nullptr,0 }, + { "Replace", SbxSTRING, 0,nullptr,0 }, + { "Start", SbxINTEGER, OPT_, nullptr,0 }, + { "Count", SbxINTEGER, OPT_, nullptr,0 }, + { "Compare", SbxINTEGER, OPT_, nullptr,0 }, +{ "Right", SbxSTRING, 2 | FUNCTION_, RTLNAME(Right),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, + { "Count", SbxLONG, 0,nullptr,0 }, +{ "RmDir", SbxNULL, 1 | FUNCTION_, RTLNAME(RmDir),0 }, + { "pathname", SbxSTRING, 0,nullptr,0 }, +{ "Round", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(Round),0}, + { "Expression", SbxDOUBLE, 0,nullptr,0 }, + { "Numdecimalplaces", SbxINTEGER, OPT_, nullptr,0 }, +{ "Rnd", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Rnd),0 }, + { "Number", SbxDOUBLE, OPT_, nullptr,0 }, +{ "RTL", SbxOBJECT, 0 | FUNCTION_ | COMPATONLY_, RTLNAME(RTL),0}, +{ "RTrim", SbxSTRING, 1 | FUNCTION_, RTLNAME(RTrim),0 }, + { "string", SbxSTRING, 0,nullptr,0 }, + +{ "SavePicture", SbxNULL, 2 | FUNCTION_, RTLNAME(SavePicture),0 }, + { "object", SbxOBJECT, 0,nullptr,0 }, + { "string", SbxSTRING, 0,nullptr,0 }, +{ "Second", SbxINTEGER, 1 | FUNCTION_, RTLNAME(Second),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, +{ "Seek", SbxLONG, 1 | FUNCTION_, RTLNAME(Seek),0 }, + { "Channel", SbxINTEGER, 0,nullptr,0 }, +{ "SendKeys", SbxNULL, 2 | FUNCTION_, RTLNAME(SendKeys),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, + { "Wait", SbxBOOL, OPT_, nullptr,0 }, +{ "SetAttr", SbxNULL, 2 | FUNCTION_, RTLNAME(SetAttr),0 }, + { "File" , SbxSTRING, 0,nullptr,0 }, + { "Attributes", SbxINTEGER, 0,nullptr,0 }, +{ "SET_OFF", SbxINTEGER, CPROP_, RTLNAME(SET_OFF),0 }, +{ "SET_ON", SbxINTEGER, CPROP_, RTLNAME(SET_ON),0 }, +{ "SET_TAB", SbxINTEGER, CPROP_, RTLNAME(SET_TAB),0 }, +{ "Sgn", SbxINTEGER, 1 | FUNCTION_, RTLNAME(Sgn),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "Shell", SbxLONG, 2 | FUNCTION_, RTLNAME(Shell),0 }, + { "Commandstring",SbxSTRING, 0,nullptr,0 }, + { "WindowStyle", SbxINTEGER, OPT_, nullptr,0 }, +{ "Sin", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Sin),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "SLN", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(SLN),0 }, + { "Cost", SbxDOUBLE, 0,nullptr,0 }, + { "Double", SbxDOUBLE, 0,nullptr,0 }, + { "Life", SbxDOUBLE, 0,nullptr,0 }, +{ "SYD", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, RTLNAME(SYD),0 }, + { "Cost", SbxDOUBLE, 0,nullptr,0 }, + { "Salvage", SbxDOUBLE, 0,nullptr,0 }, + { "Life", SbxDOUBLE, 0,nullptr,0 }, + { "Period", SbxDOUBLE, 0,nullptr,0 }, +{ "Space", SbxSTRING, 1 | FUNCTION_, RTLNAME(Space),0 }, + { "string", SbxLONG, 0,nullptr,0 }, +{ "Spc", SbxSTRING, 1 | FUNCTION_, RTLNAME(Spc),0 }, + { "Count", SbxLONG, 0,nullptr,0 }, +{ "Split", SbxOBJECT, 3 | FUNCTION_, RTLNAME(Split),0 }, + { "expression", SbxSTRING, 0,nullptr,0 }, + { "delimiter", SbxSTRING, 0,nullptr,0 }, + { "count", SbxLONG, 0,nullptr,0 }, +{ "Sqr", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Sqr),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "Str", SbxSTRING, 1 | FUNCTION_, RTLNAME(Str),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "StrComp", SbxINTEGER, 3 | FUNCTION_, RTLNAME(StrComp),0 }, + { "String1", SbxSTRING, 0,nullptr,0 }, + { "String2", SbxSTRING, 0,nullptr,0 }, + { "Compare", SbxINTEGER, OPT_, nullptr,0 }, +{ "StrConv", SbxOBJECT, 3 | FUNCTION_, RTLNAME(StrConv),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, + { "Conversion", SbxSTRING, 0,nullptr,0 }, + { "LCID", SbxINTEGER, OPT_,nullptr,0 }, +{ "String", SbxSTRING, 2 | FUNCTION_, RTLNAME(String),0 }, + { "Count", SbxLONG, 0,nullptr,0 }, + { "Filler", SbxVARIANT, 0,nullptr,0 }, +{ "StrReverse", SbxSTRING, 1 | FUNCTION_ | COMPATONLY_, RTLNAME(StrReverse),0 }, + { "String1", SbxSTRING, 0,nullptr,0 }, +{ "Switch", SbxVARIANT, 2 | FUNCTION_, RTLNAME(Switch),0 }, + { "Expression", SbxVARIANT, 0,nullptr,0 }, + { "Value", SbxVARIANT, 0,nullptr,0 }, +{ "Tab", SbxSTRING, 1 | FUNCTION_, RTLNAME(Tab),0 }, + { "Count", SbxLONG, 0,nullptr,0 }, +{ "Tan", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Tan),0 }, + { "number", SbxDOUBLE, 0,nullptr,0 }, +{ "Time", SbxVARIANT, LFUNCTION_,RTLNAME(Time),0 }, +{ "Timer", SbxDATE, FUNCTION_, RTLNAME(Timer),0 }, +{ "TimeSerial", SbxDATE, 3 | FUNCTION_, RTLNAME(TimeSerial),0 }, + { "Hour", SbxLONG, 0,nullptr,0 }, + { "Minute", SbxLONG, 0,nullptr,0 }, + { "Second", SbxLONG, 0,nullptr,0 }, +{ "TimeValue", SbxDATE, 1 | FUNCTION_, RTLNAME(TimeValue),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, +{ "TOGGLE", SbxINTEGER, CPROP_, RTLNAME(TOGGLE),0 }, +{ "Trim", SbxSTRING, 1 | FUNCTION_, RTLNAME(Trim),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, +{ "True", SbxBOOL, CPROP_, RTLNAME(True),0 }, +{ "TwipsPerPixelX", SbxLONG, FUNCTION_, RTLNAME(TwipsPerPixelX),0 }, +{ "TwipsPerPixelY", SbxLONG, FUNCTION_, RTLNAME(TwipsPerPixelY),0 }, + +{ "TYP_AUTHORFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_AUTHORFLD),0 }, +{ "TYP_CHAPTERFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_CHAPTERFLD),0 }, +{ "TYP_CONDTXTFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_CONDTXTFLD),0 }, +{ "TYP_DATEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DATEFLD),0 }, +{ "TYP_DBFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DBFLD),0 }, +{ "TYP_DBNAMEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DBNAMEFLD),0 }, +{ "TYP_DBNEXTSETFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DBNEXTSETFLD),0 }, +{ "TYP_DBNUMSETFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DBNUMSETFLD),0 }, +{ "TYP_DBSETNUMBERFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DBSETNUMBERFLD),0 }, +{ "TYP_DDEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DDEFLD),0 }, +{ "TYP_DOCINFOFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DOCINFOFLD),0 }, +{ "TYP_DOCSTATFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_DOCSTATFLD),0 }, +{ "TYP_EXTUSERFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_EXTUSERFLD),0 }, +{ "TYP_FILENAMEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_FILENAMEFLD),0 }, +{ "TYP_FIXDATEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_FIXDATEFLD),0 }, +{ "TYP_FIXTIMEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_FIXTIMEFLD),0 }, +{ "TYP_FORMELFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_FORMELFLD),0 }, +{ "TYP_GETFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_GETFLD),0 }, +{ "TYP_GETREFFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_GETREFFLD),0 }, +{ "TYP_GETREFPAGEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_GETREFPAGEFLD),0 }, +{ "TYP_HIDDENPARAFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_HIDDENPARAFLD),0 }, +{ "TYP_HIDDENTXTFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_HIDDENTXTFLD),0 }, +{ "TYP_INPUTFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_INPUTFLD),0 }, +{ "TYP_INTERNETFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_INTERNETFLD),0 }, +{ "TYP_JUMPEDITFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_JUMPEDITFLD),0 }, +{ "TYP_MACROFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_MACROFLD),0 }, +{ "TYP_NEXTPAGEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_NEXTPAGEFLD),0 }, +{ "TYP_PAGENUMBERFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_PAGENUMBERFLD),0 }, +{ "TYP_POSTITFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_POSTITFLD),0 }, +{ "TYP_PREVPAGEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_PREVPAGEFLD),0 }, +{ "TYP_SEQFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_SEQFLD),0 }, +{ "TYP_SETFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_SETFLD),0 }, +{ "TYP_SETINPFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_SETINPFLD),0 }, +{ "TYP_SETREFFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_SETREFFLD),0 }, +{ "TYP_SETREFPAGEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_SETREFPAGEFLD),0 }, +{ "TYP_TEMPLNAMEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_TEMPLNAMEFLD),0}, +{ "TYP_TIMEFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_TIMEFLD),0 }, +{ "TYP_USERFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_USERFLD),0 }, +{ "TYP_USRINPFLD", SbxINTEGER, CPROP_, RTLNAME(TYP_USRINPFLD),0 }, + +{ "TypeLen", SbxINTEGER, 1 | FUNCTION_, RTLNAME(TypeLen),0 }, + { "Var", SbxVARIANT, 0,nullptr,0 }, +{ "TypeName", SbxSTRING, 1 | FUNCTION_, RTLNAME(TypeName),0 }, + { "Var", SbxVARIANT, 0,nullptr,0 }, + +{ "UBound", SbxLONG, 1 | FUNCTION_, RTLNAME(UBound),0 }, + { "Var", SbxVARIANT, 0,nullptr,0 }, +{ "UCase", SbxSTRING, 1 | FUNCTION_, RTLNAME(UCase),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, +{ "Unload", SbxNULL, 1 | FUNCTION_, RTLNAME(Unload),0 }, + { "Dialog", SbxOBJECT, 0,nullptr,0 }, + +{ "Val", SbxDOUBLE, 1 | FUNCTION_, RTLNAME(Val),0 }, + { "String", SbxSTRING, 0,nullptr,0 }, +{ "VarType", SbxINTEGER, 1 | FUNCTION_, RTLNAME(VarType),0 }, + { "Var", SbxVARIANT, 0,nullptr,0 }, +{ "V_EMPTY", SbxINTEGER, CPROP_, RTLNAME(V_EMPTY),0 }, +{ "V_NULL", SbxINTEGER, CPROP_, RTLNAME(V_NULL),0 }, +{ "V_INTEGER", SbxINTEGER, CPROP_, RTLNAME(V_INTEGER),0 }, +{ "V_LONG", SbxINTEGER, CPROP_, RTLNAME(V_LONG),0 }, +{ "V_SINGLE", SbxINTEGER, CPROP_, RTLNAME(V_SINGLE),0 }, +{ "V_DOUBLE", SbxINTEGER, CPROP_, RTLNAME(V_DOUBLE),0 }, +{ "V_CURRENCY", SbxINTEGER, CPROP_, RTLNAME(V_CURRENCY),0 }, +{ "V_DATE", SbxINTEGER, CPROP_, RTLNAME(V_DATE),0 }, +{ "V_STRING", SbxINTEGER, CPROP_, RTLNAME(V_STRING),0 }, + +{ "Wait", SbxNULL, 1 | FUNCTION_, RTLNAME(Wait),0 }, + { "Milliseconds", SbxLONG, 0,nullptr,0 }, +{ "FuncCaller", SbxVARIANT, FUNCTION_, RTLNAME(FuncCaller),0 }, +//#i64882# +{ "WaitUntil", SbxNULL, 1 | FUNCTION_, RTLNAME(WaitUntil),0 }, + { "Date", SbxDOUBLE, 0,nullptr,0 }, +{ "Weekday", SbxINTEGER, 2 | FUNCTION_, RTLNAME(Weekday),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, + { "Firstdayofweek", SbxINTEGER, OPT_, nullptr,0 }, +{ "WeekdayName", SbxSTRING, 3 | FUNCTION_ | COMPATONLY_, RTLNAME(WeekdayName),0 }, + { "Weekday", SbxINTEGER, 0,nullptr,0 }, + { "Abbreviate", SbxBOOL, OPT_, nullptr,0 }, + { "Firstdayofweek", SbxINTEGER, OPT_, nullptr,0 }, +{ "Year", SbxINTEGER, 1 | FUNCTION_, RTLNAME(Year),0 }, + { "Date", SbxDATE, 0,nullptr,0 }, + +{ nullptr, SbxNULL, -1,nullptr,0 }}; // end of the table + +SbiStdObject::SbiStdObject( const OUString& r, StarBASIC* pb ) : SbxObject( r ) +{ + // do we have to initialize the hashcodes? + Methods* p = aMethods; + if( !p->nHash ) + while( p->nArgs != -1 ) + { + OUString aName_ = OUString::createFromAscii( p->pName ); + p->nHash = SbxVariable::MakeHashCode( aName_ ); + p += ( p->nArgs & ARGSMASK_ ) + 1; + } + + // #i92642: Remove default properties + Remove( "Name", SbxClassType::DontCare ); + Remove( "Parent", SbxClassType::DontCare ); + + SetParent( pb ); + + pStdFactory.reset( new SbStdFactory ); + SbxBase::AddFactory( pStdFactory.get() ); + + Insert( new SbStdClipboard ); +} + +SbiStdObject::~SbiStdObject() +{ + SbxBase::RemoveFactory( pStdFactory.get() ); + pStdFactory.reset(); +} + +// Finding an element: +// It runs linearly through the method table here until an +// adequate method is has been found. Because of the bits in +// the nArgs-field the adequate instance of an SbxObjElement +// is created then. If the method/property hasn't been found, +// return NULL without error code, so that a whole chain of +// objects can be asked for the method/property. + +SbxVariable* SbiStdObject::Find( const OUString& rName, SbxClassType t ) +{ + // entered already? + SbxVariable* pVar = SbxObject::Find( rName, t ); + if( !pVar ) + { + // else search one + sal_uInt16 nHash_ = SbxVariable::MakeHashCode( rName ); + Methods* p = aMethods; + bool bFound = false; + short nIndex = 0; + sal_uInt16 nSrchMask = TYPEMASK_; + switch( t ) + { + case SbxClassType::Method: nSrchMask = METHOD_; break; + case SbxClassType::Property: nSrchMask = PROPERTY_; break; + case SbxClassType::Object: nSrchMask = OBJECT_; break; + default: break; + } + while( p->nArgs != -1 ) + { + if( ( p->nArgs & nSrchMask ) + && ( p->nHash == nHash_ ) + && ( rName.equalsIgnoreAsciiCaseAscii( p->pName ) ) ) + { + bFound = true; + if( p->nArgs & COMPTMASK_ ) + { + bool bCompatibility = false; + SbiInstance* pInst = GetSbData()->pInst; + if (pInst) + { + bCompatibility = pInst->IsCompatibility(); + } + else + { + // No instance running => compiling a source on module level. + const SbModule* pModule = GetSbData()->pCompMod; + if (pModule) + bCompatibility = pModule->IsVBACompat(); + } + if ((bCompatibility && (NORMONLY_ & p->nArgs)) || (!bCompatibility && (COMPATONLY_ & p->nArgs))) + bFound = false; + } + break; + } + nIndex += ( p->nArgs & ARGSMASK_ ) + 1; + p = aMethods + nIndex; + } + + if( bFound ) + { + // isolate Args-fields: + SbxFlagBits nAccess = static_cast<SbxFlagBits>(( p->nArgs & RWMASK_ ) >> 8); + short nType = ( p->nArgs & TYPEMASK_ ); + if( p->nArgs & CONST_ ) + nAccess |= SbxFlagBits::Const; + OUString aName_ = OUString::createFromAscii( p->pName ); + SbxClassType eCT = SbxClassType::Object; + if( nType & PROPERTY_ ) + { + eCT = SbxClassType::Property; + } + else if( nType & METHOD_ ) + { + eCT = SbxClassType::Method; + } + pVar = Make( aName_, eCT, p->eType, ( p->nArgs & FUNCTION_ ) == FUNCTION_ ); + pVar->SetUserData( nIndex + 1 ); + pVar->SetFlags( nAccess ); + } + } + return pVar; +} + +// SetModified must be pinched off at the RTL +void SbiStdObject::SetModified( bool ) +{ +} + + +void SbiStdObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) + +{ + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + if( !pHint ) + return; + + SbxVariable* pVar = pHint->GetVar(); + SbxArray* pPar_ = pVar->GetParameters(); + const sal_uInt16 nCallId = static_cast<sal_uInt16>(pVar->GetUserData()); + if( nCallId ) + { + const SfxHintId t = pHint->GetId(); + if( t == SfxHintId::BasicInfoWanted ) + pVar->SetInfo( GetInfo( static_cast<short>(pVar->GetUserData()) ) ); + else + { + bool bWrite = false; + if( t == SfxHintId::BasicDataChanged ) + bWrite = true; + if( t == SfxHintId::BasicDataWanted || bWrite ) + { + RtlCall p = aMethods[ nCallId-1 ].pFunc; + SbxArrayRef rPar( pPar_ ); + if( !pPar_ ) + { + rPar = pPar_ = new SbxArray; + pPar_->Put32( pVar, 0 ); + } + p( static_cast<StarBASIC*>(GetParent()), *pPar_, bWrite ); + return; + } + } + } + SbxObject::Notify( rBC, rHint ); +} + +// building the info-structure for single elements +// if nIdx = 0, don't create anything (Std-Props!) + +SbxInfo* SbiStdObject::GetInfo( short nIdx ) +{ + if( !nIdx ) + return nullptr; + Methods* p = &aMethods[ --nIdx ]; + SbxInfo* pInfo_ = new SbxInfo; + short nPar = p->nArgs & ARGSMASK_; + for( short i = 0; i < nPar; i++ ) + { + p++; + OUString aName_ = OUString::createFromAscii( p->pName ); + SbxFlagBits nFlags_ = static_cast<SbxFlagBits>(( p->nArgs >> 8 ) & 0x03); + if( p->nArgs & OPT_ ) + { + nFlags_ |= SbxFlagBits::Optional; + } + pInfo_->AddParam( aName_, p->eType, nFlags_ ); + } + return pInfo_; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/stdobj1.cxx b/basic/source/runtime/stdobj1.cxx new file mode 100644 index 000000000..20268bfbf --- /dev/null +++ b/basic/source/runtime/stdobj1.cxx @@ -0,0 +1,422 @@ +/* -*- 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 <basic/sberrors.hxx> +#include <basic/sbstar.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> +#include <sbstdobj.hxx> + +#define ATTR_IMP_TYPE 1 +#define ATTR_IMP_WIDTH 2 +#define ATTR_IMP_HEIGHT 3 +#define ATTR_IMP_BOLD 4 +#define ATTR_IMP_ITALIC 5 +#define ATTR_IMP_STRIKETHROUGH 6 +#define ATTR_IMP_UNDERLINE 7 +#define ATTR_IMP_SIZE 9 +#define ATTR_IMP_NAME 10 + +#define METH_CLEAR 20 +#define METH_GETDATA 21 +#define METH_GETFORMAT 22 +#define METH_GETTEXT 23 +#define METH_SETDATA 24 +#define METH_SETTEXT 25 + + +SbStdFactory::SbStdFactory() +{ +} + +SbxObject* SbStdFactory::CreateObject( const OUString& rClassName ) +{ + if( rClassName.equalsIgnoreAsciiCase("Picture") ) + return new SbStdPicture; + else if( rClassName.equalsIgnoreAsciiCase("Font") ) + return new SbStdFont; + else + return nullptr; +} + + +void SbStdPicture::PropType( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + { + StarBASIC::Error( ERRCODE_BASIC_PROP_READONLY ); + return; + } + + GraphicType eType = aGraphic.GetType(); + sal_Int16 nType = 0; + + if( eType == GraphicType::Bitmap ) + nType = 1; + else if( eType != GraphicType::NONE ) + nType = 2; + + pVar->PutInteger( nType ); +} + + +void SbStdPicture::PropWidth( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + { + StarBASIC::Error( ERRCODE_BASIC_PROP_READONLY ); + return; + } + + Size aSize = OutputDevice::LogicToLogic(aGraphic.GetPrefSize(), aGraphic.GetPrefMapMode(), MapMode(MapUnit::MapTwip)); + pVar->PutInteger( static_cast<sal_Int16>(aSize.Width()) ); +} + +void SbStdPicture::PropHeight( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + { + StarBASIC::Error( ERRCODE_BASIC_PROP_READONLY ); + return; + } + + Size aSize = OutputDevice::LogicToLogic(aGraphic.GetPrefSize(), aGraphic.GetPrefMapMode(), MapMode(MapUnit::MapTwip)); + pVar->PutInteger( static_cast<sal_Int16>(aSize.Height()) ); +} + + +SbStdPicture::SbStdPicture() : + SbxObject( "Picture" ) +{ + // Properties + SbxVariable* p = Make( "Type", SbxClassType::Property, SbxVARIANT ); + p->SetFlags( SbxFlagBits::Read | SbxFlagBits::DontStore ); + p->SetUserData( ATTR_IMP_TYPE ); + p = Make( "Width", SbxClassType::Property, SbxVARIANT ); + p->SetFlags( SbxFlagBits::Read | SbxFlagBits::DontStore ); + p->SetUserData( ATTR_IMP_WIDTH ); + p = Make( "Height", SbxClassType::Property, SbxVARIANT ); + p->SetFlags( SbxFlagBits::Read | SbxFlagBits::DontStore ); + p->SetUserData( ATTR_IMP_HEIGHT ); +} + +SbStdPicture::~SbStdPicture() +{ +} + + +void SbStdPicture::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) + +{ + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + + if( !pHint ) + return; + + if( pHint->GetId() == SfxHintId::BasicInfoWanted ) + { + SbxObject::Notify( rBC, rHint ); + return; + } + + SbxVariable* pVar = pHint->GetVar(); + const sal_uInt32 nWhich = pVar->GetUserData(); + bool bWrite = pHint->GetId() == SfxHintId::BasicDataChanged; + + // Properties + switch( nWhich ) + { + case ATTR_IMP_TYPE: PropType( pVar, bWrite ); return; + case ATTR_IMP_WIDTH: PropWidth( pVar, bWrite ); return; + case ATTR_IMP_HEIGHT: PropHeight( pVar, bWrite ); return; + } + + SbxObject::Notify( rBC, rHint ); +} + + +void SbStdFont::PropBold( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + SetBold( pVar->GetBool() ); + else + pVar->PutBool( IsBold() ); +} + +void SbStdFont::PropItalic( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + SetItalic( pVar->GetBool() ); + else + pVar->PutBool( IsItalic() ); +} + +void SbStdFont::PropStrikeThrough( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + SetStrikeThrough( pVar->GetBool() ); + else + pVar->PutBool( IsStrikeThrough() ); +} + +void SbStdFont::PropUnderline( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + SetUnderline( pVar->GetBool() ); + else + pVar->PutBool( IsUnderline() ); +} + +void SbStdFont::PropSize( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + SetSize( static_cast<sal_uInt16>(pVar->GetInteger()) ); + else + pVar->PutInteger( static_cast<sal_Int16>(GetSize()) ); +} + +void SbStdFont::PropName( SbxVariable* pVar, bool bWrite ) +{ + if( bWrite ) + { + aName = pVar->GetOUString(); + } + else + { + pVar->PutString( aName ); + } +} + + +SbStdFont::SbStdFont() + : SbxObject( "Font" ) + , bBold(false) + , bItalic(false) + , bStrikeThrough(false) + , bUnderline(false) + , nSize(0) +{ + // Properties + SbxVariable* p = Make( "Bold", SbxClassType::Property, SbxVARIANT ); + p->SetFlags( SbxFlagBits::ReadWrite | SbxFlagBits::DontStore ); + p->SetUserData( ATTR_IMP_BOLD ); + p = Make( "Italic", SbxClassType::Property, SbxVARIANT ); + p->SetFlags( SbxFlagBits::ReadWrite | SbxFlagBits::DontStore ); + p->SetUserData( ATTR_IMP_ITALIC ); + p = Make( "StrikeThrough", SbxClassType::Property, SbxVARIANT ); + p->SetFlags( SbxFlagBits::ReadWrite | SbxFlagBits::DontStore ); + p->SetUserData( ATTR_IMP_STRIKETHROUGH ); + p = Make( "Underline", SbxClassType::Property, SbxVARIANT ); + p->SetFlags( SbxFlagBits::ReadWrite | SbxFlagBits::DontStore ); + p->SetUserData( ATTR_IMP_UNDERLINE ); + p = Make( "Size", SbxClassType::Property, SbxVARIANT ); + p->SetFlags( SbxFlagBits::ReadWrite | SbxFlagBits::DontStore ); + p->SetUserData( ATTR_IMP_SIZE ); + + // handle name property yourself + p = Find( "Name", SbxClassType::Property ); + assert(p && "No Name Property"); + p->SetUserData( ATTR_IMP_NAME ); +} + +SbStdFont::~SbStdFont() +{ +} + +void SbStdFont::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + + if( !pHint ) + return; + + if( pHint->GetId() == SfxHintId::BasicInfoWanted ) + { + SbxObject::Notify( rBC, rHint ); + return; + } + + SbxVariable* pVar = pHint->GetVar(); + const sal_uInt32 nWhich = pVar->GetUserData(); + bool bWrite = pHint->GetId() == SfxHintId::BasicDataChanged; + + // Properties + switch( nWhich ) + { + case ATTR_IMP_BOLD: PropBold( pVar, bWrite ); return; + case ATTR_IMP_ITALIC: PropItalic( pVar, bWrite ); return; + case ATTR_IMP_STRIKETHROUGH:PropStrikeThrough( pVar, bWrite ); return; + case ATTR_IMP_UNDERLINE: PropUnderline( pVar, bWrite ); return; + case ATTR_IMP_SIZE: PropSize( pVar, bWrite ); return; + case ATTR_IMP_NAME: PropName( pVar, bWrite ); return; + } + + SbxObject::Notify( rBC, rHint ); +} + + +void SbStdClipboard::MethClear( SbxArray const * pPar_ ) +{ + if( pPar_ && (pPar_->Count32() > 1) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS ); + return; + } + +} + +void SbStdClipboard::MethGetData( SbxArray* pPar_ ) +{ + if( !pPar_ || (pPar_->Count32() != 2) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS ); + return; + } + + sal_Int16 nFormat = pPar_->Get32(1)->GetInteger(); + if( nFormat <= 0 || nFormat > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + +} + +void SbStdClipboard::MethGetFormat( SbxVariable* pVar, SbxArray* pPar_ ) +{ + if( !pPar_ || (pPar_->Count32() != 2) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS ); + return; + } + + sal_Int16 nFormat = pPar_->Get32(1)->GetInteger(); + if( nFormat <= 0 || nFormat > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + + pVar->PutBool( false ); +} + +void SbStdClipboard::MethGetText( SbxVariable* pVar, SbxArray const * pPar_ ) +{ + if( pPar_ && (pPar_->Count32() > 1) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS ); + return; + } + + pVar->PutString( OUString() ); +} + +void SbStdClipboard::MethSetData( SbxArray* pPar_ ) +{ + if( !pPar_ || (pPar_->Count32() != 3) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS ); + return; + } + + sal_Int16 nFormat = pPar_->Get32(2)->GetInteger(); + if( nFormat <= 0 || nFormat > 3 ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); + return; + } + +} + +void SbStdClipboard::MethSetText( SbxArray const * pPar_ ) +{ + if( !pPar_ || (pPar_->Count32() != 2) ) + { + StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS ); + return; + } + +} + + +SbStdClipboard::SbStdClipboard() : + SbxObject( "Clipboard" ) +{ + SbxVariable* p = Find( "Name", SbxClassType::Property ); + assert(p && "No Name Property"); + p->SetUserData( ATTR_IMP_NAME ); + + // register methods + p = Make( "Clear", SbxClassType::Method, SbxEMPTY ); + p->SetFlag( SbxFlagBits::DontStore ); + p->SetUserData( METH_CLEAR ); + p = Make( "GetData", SbxClassType::Method, SbxEMPTY ); + p->SetFlag( SbxFlagBits::DontStore ); + p->SetUserData( METH_GETDATA ); + p = Make( "GetFormat", SbxClassType::Method, SbxEMPTY ); + p->SetFlag( SbxFlagBits::DontStore ); + p->SetUserData( METH_GETFORMAT ); + p = Make( "GetText", SbxClassType::Method, SbxEMPTY ); + p->SetFlag( SbxFlagBits::DontStore ); + p->SetUserData( METH_GETTEXT ); + p = Make( "SetData", SbxClassType::Method, SbxEMPTY ); + p->SetFlag( SbxFlagBits::DontStore ); + p->SetUserData( METH_SETDATA ); + p = Make( "SetText", SbxClassType::Method, SbxEMPTY ); + p->SetFlag( SbxFlagBits::DontStore ); + p->SetUserData( METH_SETTEXT ); +} + +SbStdClipboard::~SbStdClipboard() +{ +} + +void SbStdClipboard::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint); + + if( !pHint ) + return; + + if( pHint->GetId() == SfxHintId::BasicInfoWanted ) + { + SbxObject::Notify( rBC, rHint ); + return; + } + + SbxVariable* pVar = pHint->GetVar(); + SbxArray* pPar_ = pVar->GetParameters(); + const sal_uInt32 nWhich = pVar->GetUserData(); + + // Methods + switch( nWhich ) + { + case METH_CLEAR: MethClear( pPar_ ); return; + case METH_GETDATA: MethGetData( pPar_ ); return; + case METH_GETFORMAT: MethGetFormat( pVar, pPar_ ); return; + case METH_GETTEXT: MethGetText( pVar, pPar_ ); return; + case METH_SETDATA: MethSetData( pPar_ ); return; + case METH_SETTEXT: MethSetText( pPar_ ); return; + } + + SbxObject::Notify( rBC, rHint ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/runtime/wnt-x86.asm b/basic/source/runtime/wnt-x86.asm new file mode 100644 index 000000000..08bd70fc2 --- /dev/null +++ b/basic/source/runtime/wnt-x86.asm @@ -0,0 +1,47 @@ +; +; 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 . +; + +.386 + +PUBLIC _DllMgr_call32@12 +PUBLIC _DllMgr_callFp@12 + +_TEXT SEGMENT +_DllMgr_call32@12: +_DllMgr_callFp@12: + push ebp + mov ebp, esp + push esi + push edi + mov ecx, [ebp+16] + jecxz $1 + sub esp, ecx + mov edi, esp + mov esi, [ebp+12] + shr ecx, 2 + rep movsd +$1: call DWORD PTR [ebp+8] + ; for extra safety, do not trust esp after call (in case the Basic Declare + ; signature is wrong): + mov edi, [ebp-8] + mov esi, [ebp-4] + mov esp, ebp + pop ebp + ret 12 +_TEXT ENDS +END |