summaryrefslogtreecommitdiffstats
path: root/basic/source/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'basic/source/runtime')
-rw-r--r--basic/source/runtime/basrdll.cxx132
-rw-r--r--basic/source/runtime/comenumwrapper.cxx65
-rw-r--r--basic/source/runtime/comenumwrapper.hxx45
-rw-r--r--basic/source/runtime/ddectrl.cxx198
-rw-r--r--basic/source/runtime/ddectrl.hxx54
-rw-r--r--basic/source/runtime/dllmgr-none.cxx113
-rw-r--r--basic/source/runtime/dllmgr-x64.cxx764
-rw-r--r--basic/source/runtime/dllmgr-x86.cxx739
-rw-r--r--basic/source/runtime/dllmgr.hxx54
-rw-r--r--basic/source/runtime/inputbox.cxx142
-rw-r--r--basic/source/runtime/iosys.cxx842
-rw-r--r--basic/source/runtime/methods.cxx4745
-rw-r--r--basic/source/runtime/methods1.cxx2992
-rw-r--r--basic/source/runtime/props.cxx171
-rw-r--r--basic/source/runtime/runtime.cxx4766
-rw-r--r--basic/source/runtime/stdobj.cxx1097
-rw-r--r--basic/source/runtime/stdobj1.cxx427
-rw-r--r--basic/source/runtime/wnt-x86.asm47
18 files changed, 17393 insertions, 0 deletions
diff --git a/basic/source/runtime/basrdll.cxx b/basic/source/runtime/basrdll.cxx
new file mode 100644
index 0000000000..853863b4fc
--- /dev/null
+++ b/basic/source/runtime/basrdll.cxx
@@ -0,0 +1,132 @@
+/* -*- 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 <mutex>
+
+#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;
+
+ SbxAppData aSbxAppData;
+
+ BasicDLLImpl()
+ : bDebugMode(false)
+ , bBreakEnabled(true)
+ { }
+
+ static BasicDLLImpl* BASIC_DLL;
+ static std::mutex& getMutex()
+ {
+ static std::mutex aMutex;
+ return aMutex;
+ }
+};
+
+BasicDLLImpl* BasicDLLImpl::BASIC_DLL = nullptr;
+}
+
+BasicDLL::BasicDLL()
+{
+ std::scoped_lock aGuard(BasicDLLImpl::getMutex());
+ if (!BasicDLLImpl::BASIC_DLL)
+ BasicDLLImpl::BASIC_DLL = new BasicDLLImpl;
+ m_xImpl = BasicDLLImpl::BASIC_DLL;
+}
+
+BasicDLL::~BasicDLL()
+{
+ std::scoped_lock aGuard(BasicDLLImpl::getMutex());
+ const bool bLastRef = m_xImpl->GetRefCount() == 1;
+ if (bLastRef) {
+ BasicDLLImpl::BASIC_DLL->aSbxAppData.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->aSbxAppData;
+}
+
+bool IsSbxData_Impl()
+{
+ return BasicDLLImpl::BASIC_DLL;
+}
+
+/* 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 0000000000..b7881a1b9a
--- /dev/null
+++ b/basic/source/runtime/comenumwrapper.cxx
@@ -0,0 +1,65 @@
+/* -*- 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{ uno::Any(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 0000000000..38dd57f274
--- /dev/null
+++ b/basic/source/runtime/comenumwrapper.hxx
@@ -0,0 +1,45 @@
+/* -*- 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>
+#include <utility>
+
+class ComEnumerationWrapper : public ::cppu::WeakImplHelper<css::container::XEnumeration>
+{
+ css::uno::Reference<css::script::XInvocation> m_xInvocation;
+ sal_Int32 m_nCurInd;
+
+public:
+ explicit ComEnumerationWrapper(css::uno::Reference<css::script::XInvocation> xInvocation)
+ : m_xInvocation(std::move(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 0000000000..41e5c53d84
--- /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 <comphelper/errcode.hxx>
+#include <svl/svdde.hxx>
+#include "ddectrl.hxx"
+#include <basic/sberrors.hxx>
+
+#define DDE_FIRSTERR 0x4000
+#define DDE_LASTERR 0x4011
+
+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;
+ }
+ tools::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;
+ auto pConv = std::make_unique<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 0000000000..d341bbcb8e
--- /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 <comphelper/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 0000000000..4c7f700a9e
--- /dev/null
+++ b/basic/source/runtime/dllmgr-none.cxx
@@ -0,0 +1,113 @@
+/* -*- 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"
+
+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->Count() != 2)
+ return ERRCODE_BASIC_BAD_ARGUMENT;
+ SbxVariable* pOut = pArgs->Get(1);
+ if (!pOut)
+ return ERRCODE_BASIC_BAD_ARGUMENT;
+ if (pOut->IsCurrency())
+ {
+ pOut->PutCurrency(nValue);
+ return ERRCODE_NONE;
+ }
+ if (!pOut->IsObject())
+ return ERRCODE_BASIC_BAD_ARGUMENT;
+
+ // 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->Count() != 2)
+ return ERRCODE_BASIC_BAD_ARGUMENT;
+ SbxVariable* pLow = pProps->Get(0);
+ SbxVariable* pHigh = pProps->Get(1);
+ if (!pLow || !pLow->IsLong() ||
+ !pHigh || !pHigh->IsLong())
+ return ERRCODE_BASIC_BAD_ARGUMENT;
+ pLow->PutLong(nValue & 0xffffffff);
+ pHigh->PutLong(nValue >> 32);
+ return ERRCODE_NONE;
+}
+
+ErrCode builtin_kernel32(std::u16string_view aFuncName, SbxArray *pArgs,
+ SbxVariable &rRetVal)
+{
+ sal_Int64 nNanoSecsPerSec = 1000.0*1000*1000;
+ if (aFuncName == u"QueryPerformanceFrequency")
+ return returnInt64InOutArg(pArgs, rRetVal, nNanoSecsPerSec);
+
+ else if (aFuncName == u"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(
+ std::u16string_view aFuncName, std::u16string_view aDllName,
+ SbxArray *pArgs, SbxVariable &rRetVal,
+ SAL_UNUSED_PARAMETER bool /* bCDecl */)
+{
+ if (aDllName == u"kernel32")
+ return builtin_kernel32(aFuncName, pArgs, rRetVal);
+ else
+ return ERRCODE_BASIC_NOT_IMPLEMENTED;
+}
+
+void SbiDllMgr::FreeDll(SAL_UNUSED_PARAMETER OUString const &) {}
+
+SbiDllMgr::SbiDllMgr() = default;
+
+#if defined(_WIN32) && !defined(_ARM64_)
+SbiDllMgr::~SbiDllMgr() = default;
+#endif
+
+/* 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 0000000000..0a3d334ce4
--- /dev/null
+++ b/basic/source/runtime/dllmgr-x64.cxx
@@ -0,0 +1,764 @@
+/* -*- 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 <string_view>
+#include <vector>
+
+#include <basic/sbx.hxx>
+#include <basic/sbxvar.hxx>
+#include <comphelper/string.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>
+#include <o3tl/string_view.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->Count(); ++i)
+ {
+ n = std::max(n, alignment(props->Get(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->GetDims();
+ std::vector< sal_Int32 > low(dims);
+ for (sal_Int32 i = 0; i < dims; ++i) {
+ sal_Int32 up;
+ arr->GetDim(i + 1, low[i], up);
+ }
+ return alignment(arr->Get(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->Count(); ++i)
+ {
+ ErrCode e = marshal(false, props->Get(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->GetDims();
+ std::vector< sal_Int32 > low(dims);
+ std::vector< sal_Int32 > up(dims);
+ for (sal_Int32 i = 0; i < dims; ++i) {
+ arr->GetDim(i + 1, low[i], up[i]);
+ }
+ for (std::vector< sal_Int32 > idx = low;;) {
+ ErrCode e = marshal(false, arr->Get(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->Count(); ++i)
+ {
+ data = unmarshal(props->Get(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->GetDims();
+ std::vector< sal_Int32 > low(dims);
+ std::vector< sal_Int32 > up(dims);
+ for (sal_Int32 i = 0; i < dims; ++i) {
+ arr->GetDim(i + 1, low[i], up[i]);
+ }
+ for (std::vector< sal_Int32 > idx = low;;) {
+ data = unmarshal(arr->Get(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(
+ std::u16string_view dll, ProcData const & proc, SbxArray * arguments,
+ SbxVariable & result)
+{
+ if (arguments && arguments->Count() > 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 =
+ o3tl::equalsIgnoreAsciiCase(dll, u"KERNEL32.DLL") &&
+ (proc.name == "GetLogicalDriveStringsA");
+ for (sal_uInt32 i = 1; i < (arguments == nullptr ? 0 : arguments->Count()); ++i)
+ {
+ ErrCode e = marshal(true, arguments->Get(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_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 p = reinterpret_cast<sal_Int64 (*)(...)>(proc.proc);
+ auto const st = reinterpret_cast<double *>(stack.data());
+ iRetVal
+ = p(st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7], st[8], st[9], st[10],
+ st[11], st[12], st[13], st[14], st[15], st[16], st[17], st[18], st[19]);
+ break;
+ }
+ case SbxSINGLE:
+ case SbxDOUBLE:
+ {
+ auto p = reinterpret_cast<double (*)(...)>(proc.proc);
+ auto const st = reinterpret_cast<double*>(stack.data());
+ dRetVal
+ = p(st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7], st[8], st[9], st[10],
+ st[11], st[12], st[13], st[14], st[15], st[16], st[17], st[18], st[19]);
+ 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->Count()); ++i)
+ {
+ arguments->Get(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 = o3tl::toInt32(name.subView(1)); //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(
+ std::u16string_view function, std::u16string_view library,
+ SbxArray * arguments, SbxVariable & result, bool cdeclConvention)
+{
+ if (cdeclConvention) {
+ return ERRCODE_BASIC_NOT_IMPLEMENTED;
+ }
+ OUString dllName(fullDllName(OUString(library)));
+ Dll * dll = impl_->getDll(dllName);
+ if (dll == nullptr) {
+ return ERRCODE_BASIC_BAD_DLL_LOAD;
+ }
+ ProcData proc;
+ ErrCode e = dll->getProc(OUString(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 0000000000..7ab84d7f8b
--- /dev/null
+++ b/basic/source/runtime/dllmgr-x86.cxx
@@ -0,0 +1,739 @@
+/* -*- 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 <comphelper/string.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>
+#include <o3tl/string_view.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->Count(); ++i)
+ {
+ n = std::max(n, alignment(props->Get(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->GetDims();
+ std::vector< sal_Int32 > low(dims);
+ for (sal_Int32 i = 0; i < dims; ++i) {
+ sal_Int32 up;
+ arr->GetDim(i + 1, low[i], up);
+ }
+ return alignment(arr->Get(&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->Count(); ++i)
+ {
+ ErrCode e = marshal(false, props->Get(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->GetDims();
+ std::vector< sal_Int32 > low(dims);
+ std::vector< sal_Int32 > up(dims);
+ for (sal_Int32 i = 0; i < dims; ++i) {
+ arr->GetDim(i + 1, low[i], up[i]);
+ }
+ for (std::vector< sal_Int32 > idx = low;;) {
+ ErrCode e = marshal(false, arr->Get(&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->Count(); ++i)
+ {
+ data = unmarshal(props->Get(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->GetDims();
+ std::vector< sal_Int32 > low(dims);
+ std::vector< sal_Int32 > up(dims);
+ for (sal_Int32 i = 0; i < dims; ++i) {
+ arr->GetDim(i + 1, low[i], up[i]);
+ }
+ for (std::vector< sal_Int32 > idx = low;;) {
+ data = unmarshal(arr->Get(&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->Count()); ++i)
+ {
+ ErrCode e = marshal(true, arguments->Get(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->Count()); ++i)
+ {
+ arguments->Get(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 = o3tl::toInt32(name.subView(1)); //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(
+ std::u16string_view function, std::u16string_view library,
+ SbxArray * arguments, SbxVariable & result, bool cdeclConvention)
+{
+ if (cdeclConvention) {
+ return ERRCODE_BASIC_NOT_IMPLEMENTED;
+ }
+ OUString dllName(fullDllName(OUString(library)));
+ Dll * dll = impl_->getDll(dllName);
+ if (dll == 0) {
+ return ERRCODE_BASIC_BAD_DLL_LOAD;
+ }
+ ProcData proc;
+ ErrCode e = dll->getProc(OUString(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 0000000000..a280e89b64
--- /dev/null
+++ b/basic/source/runtime/dllmgr.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 <sal/config.h>
+#include <comphelper/errcode.hxx>
+#include <memory>
+
+class SbxArray;
+class SbxVariable;
+
+class SbiDllMgr {
+public:
+ SbiDllMgr(const SbiDllMgr&) = delete;
+ const SbiDllMgr& operator=(const SbiDllMgr&) = delete;
+
+ SbiDllMgr();
+
+#if defined(_WIN32) && !defined(_ARM64_)
+ ~SbiDllMgr();
+#endif
+
+ ErrCode Call(
+ std::u16string_view function, std::u16string_view library,
+ SbxArray * arguments, SbxVariable & result, bool cdeclConvention);
+
+ void FreeDll(OUString const & library);
+
+private:
+#if defined(_WIN32) && !defined(_ARM64_)
+ 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 0000000000..2e154d0e00
--- /dev/null
+++ b/basic/source/runtime/inputbox.cxx
@@ -0,0 +1,142 @@
+/* -*- 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/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.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( tools::Long nXTwips, tools::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, tools::Long nXTwips, tools::Long nYTwips );
+ OUString const & GetText() const { return m_aText; }
+};
+
+}
+
+SvRTLInputBox::SvRTLInputBox(weld::Window* pParent, const OUString& rPrompt,
+ const OUString& rTitle, const OUString& rDefault,
+ tools::Long nXTwips, tools::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(tools::Long nXTwips, tools::Long nYTwips)
+{
+ if( nXTwips != -1 && nYTwips != -1 )
+ {
+ Point aDlgPosApp( nXTwips, nYTwips );
+ OutputDevice* pDefaultDevice = Application::GetDefaultDevice();
+ pDefaultDevice->Push(vcl::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.Count();
+ 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.Get(1)->GetOUString();
+ if (nArgCount > 2 && !rPar.Get(2)->IsErr())
+ aTitle = rPar.Get(2)->GetOUString();
+ if (nArgCount > 3 && !rPar.Get(3)->IsErr())
+ aDefault = rPar.Get(3)->GetOUString();
+ if ( nArgCount > 4 )
+ {
+ if ( nArgCount != 6 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ nX = rPar.Get(4)->GetLong();
+ nY = rPar.Get(5)->GetLong();
+ }
+ SvRTLInputBox aDlg(Application::GetDefDialogParent(), rPrompt, aTitle, aDefault, nX, nY);
+ aDlg.run();
+ rPar.Get(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 0000000000..be056aaf61
--- /dev/null
+++ b/basic/source/runtime/iosys.cxx
@@ -0,0 +1,842 @@
+/* -*- 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 <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>
+#include <iosys.hxx>
+
+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;
+
+
+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 const bool bRetVal = [] {
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+ if( !xContext.is() )
+ {
+ // No service manager at all
+ return false;
+ }
+ else
+ {
+ Reference< XUniversalContentBroker > xManager = UniversalContentBroker::create(xContext);
+
+ if ( !( xManager->queryContentProvider( "file:///" ).is() ) )
+ {
+ // No UCB
+ return false;
+ }
+ }
+ return true;
+ }();
+ 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
+( std::string_view 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 += "\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, std::string_view 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() )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(Application::GetDefDialogParent(), 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 += "\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(std::u16string_view 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(std::u16string_view 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;
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(Application::GetDefDialogParent(), 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 0000000000..ad3b638805
--- /dev/null
+++ b/basic/source/runtime/methods.cxx
@@ -0,0 +1,4745 @@
+/* -*- 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/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/sound.hxx>
+#include <vcl/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 <unotools/wincodepage.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/VbStrConv.hpp>
+#include <ooo/vba/VbTriState.hpp>
+#include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp>
+#include <memory>
+#include <random>
+#include <string_view>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+// include search util
+#include <com/sun/star/i18n/Transliteration.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <i18nutil/searchopt.hxx>
+#include <unotools/textsearch.hxx>
+#include <svl/numformat.hxx>
+
+#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 <vcl/TypeSerializer.hxx>
+
+using namespace comphelper;
+using namespace osl;
+using namespace com::sun::star;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+
+static sal_Int32 GetDayDiff(const Date& rDate) { return rDate - Date(1899'12'30); }
+
+#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 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)
+{
+ if( rPar.Count() < 2 )
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ OUString aClass(rPar.Get(1)->GetOUString());
+ SbxObjectRef p = SbxBase::CreateObject( aClass );
+ if( !p.is() )
+ return StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD );
+
+ // Convenience: enter BASIC as parent
+ p->SetParent( pBasic );
+ rPar.Get(0)->PutObject(p.get());
+}
+
+// Error( n )
+
+void SbRtl_Error(StarBASIC * pBasic, SbxArray & rPar, bool)
+{
+ if( !pBasic )
+ return StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
+
+ OUString aErrorMsg;
+ ErrCode nErr = ERRCODE_NONE;
+ sal_Int32 nCode = 0;
+ if (rPar.Count() == 1)
+ {
+ nErr = StarBASIC::GetErrBasic();
+ aErrorMsg = StarBASIC::GetErrorMsg();
+ }
+ else
+ {
+ nCode = rPar.Get(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.Count() > 1)
+ {
+ uno::Reference< ooo::vba::XErrObject > xErrObj( SbxErrObject::getUnoErrObject() );
+ if ( xErrObj.is() && xErrObj->getNumber() == nCode && !xErrObj->getDescription().isEmpty() )
+ {
+ tmpErrMsg = xErrObj->getDescription();
+ }
+ }
+ rPar.Get(0)->PutString(tmpErrMsg);
+}
+
+// Sinus
+
+void SbRtl_Sin(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariableRef pArg = rPar.Get(1);
+ rPar.Get(0)->PutDouble(sin(pArg->GetDouble()));
+}
+
+
+void SbRtl_Cos(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariableRef pArg = rPar.Get(1);
+ rPar.Get(0)->PutDouble(cos(pArg->GetDouble()));
+}
+
+
+void SbRtl_Atn(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariableRef pArg = rPar.Get(1);
+ rPar.Get(0)->PutDouble(atan(pArg->GetDouble()));
+}
+
+
+void SbRtl_Abs(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariableRef pArg = rPar.Get(1);
+ rPar.Get(0)->PutDouble(fabs(pArg->GetDouble()));
+}
+
+
+void SbRtl_Asc(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariableRef pArg = rPar.Get(1);
+ OUString aStr( pArg->GetOUString() );
+ if ( aStr.isEmpty())
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ rPar.Get(0)->PutEmpty();
+ return;
+ }
+ sal_Unicode aCh = aStr[0];
+ rPar.Get(0)->PutLong(aCh);
+}
+
+static void implChr( SbxArray& rPar, bool bChrW )
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariableRef pArg = rPar.Get(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.Get(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 ? OUString(o3tl::toU(expression)) : OUString("???"))
+ << "\" (" << (function ? OUString(o3tl::toU(function)) : OUString("???")) << ") at "
+ << (file ? OUString(o3tl::toU(file)) : OUString("???")) << ":" << line);
+}
+
+}
+
+#endif
+
+void SbRtl_CurDir(StarBASIC *, SbxArray & rPar, bool)
+{
+ // #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.Count() == 2)
+ {
+ OUString aDrive = rPar.Get(1)->GetOUString();
+ if ( aDrive.getLength() != 1 )
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ auto c = rtl::toAsciiUpperCase(aDrive[0]);
+ if ( !rtl::isAsciiUpperCase( c ) )
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ 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 )
+ return StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE );
+
+ rPar.Get(0)->PutString(OUString(o3tl::toU(pBuffer)));
+
+#else
+
+ const int PATH_INCR = 250;
+
+ int nSize = PATH_INCR;
+ std::unique_ptr<char[]> pMem;
+ while( true )
+ {
+ pMem.reset(new char[nSize]);
+ if( !pMem )
+ return StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY );
+
+ if( getcwd( pMem.get(), nSize-1 ) != nullptr )
+ {
+ rPar.Get(0)->PutString(OUString::createFromAscii(pMem.get()));
+ return;
+ }
+ if( errno != ERANGE )
+ return StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
+
+ nSize += PATH_INCR;
+ };
+
+#endif
+}
+
+void SbRtl_ChDir(StarBASIC * pBasic, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutEmpty();
+ if (rPar.Count() == 2)
+ {
+ // VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.)
+ if( SbiRuntime::isVBAEnabled() )
+ {
+ ::basic::vba::registerCurrentDirectory(getDocumentModel(pBasic),
+ rPar.Get(1)->GetOUString());
+ }
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+}
+
+void SbRtl_ChDrive(StarBASIC *, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutEmpty();
+ if (rPar.Count() != 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.Get(0)->PutEmpty();
+ if (rPar.Count() == 3)
+ {
+ OUString aSource = rPar.Get(1)->GetOUString();
+ OUString aDest = rPar.Get(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.Get(0)->PutEmpty();
+ if (rPar.Count() == 2)
+ {
+ OUString aFileSpec = rPar.Get(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.Get(0)->PutEmpty();
+ if (rPar.Count() == 2)
+ {
+ OUString aPath = rPar.Get(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->Insert(pResult.get(), pPar->Count());
+ pPar->Insert(pParam.get(), pPar->Count());
+ SbRtl_CurDir( pBasic, *pPar, bWrite );
+
+ OUString sCurPathURL;
+ File::getFileURLFromSystemPath(pPar->Get(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 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
+ }
+
+ Directory aDir( aDirPath );
+ nRet = aDir.open();
+ if( nRet != FileBase::E_None )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
+ }
+ aDir.close();
+
+ comphelper::DirectoryHelper::deleteDirRecursively(aDirPath);
+}
+
+
+void SbRtl_RmDir(StarBASIC *, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutEmpty();
+ if (rPar.Count() == 2)
+ {
+ OUString aPath = rPar.Get(1)->GetOUString();
+ if( hasUno() )
+ {
+ const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
+ if( xSFI.is() )
+ {
+ try
+ {
+ if( !xSFI->isFolder( aPath ) )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
+ }
+ SbiInstance* pInst = GetSbData()->pInst;
+ bool bCompatibility = ( pInst && pInst->IsCompatibility() );
+ if( bCompatibility )
+ {
+ Sequence< OUString > aContent = xSFI->getFolderContents( aPath, true );
+ if( aContent.hasElements() )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR );
+ }
+ }
+
+ 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.Get(0)->PutEmpty();
+ StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
+}
+
+void SbRtl_Exp(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ double aDouble = rPar.Get(1)->GetDouble();
+ aDouble = exp( aDouble );
+ checkArithmeticOverflow( aDouble );
+ rPar.Get(0)->PutDouble(aDouble);
+}
+
+void SbRtl_FileLen(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ SbxVariableRef pArg = rPar.Get(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.Get(0)->PutLong(nLen);
+}
+
+
+
+void SbRtl_Hex(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ SbxVariableRef pArg = rPar.Get(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());
+ rPar.Get(0)->PutString(OUString::number(nVal, 16).toAsciiUpperCase());
+}
+
+void SbRtl_FuncCaller(StarBASIC *, SbxArray & rPar, bool)
+{
+ if ( SbiRuntime::isVBAEnabled() && GetSbData()->pInst && GetSbData()->pInst->pRun )
+ {
+ if ( GetSbData()->pInst->pRun->GetExternalCaller() )
+ *rPar.Get(0) = *GetSbData()->pInst->pRun->GetExternalCaller();
+ else
+ {
+ SbxVariableRef pVar = new SbxVariable(SbxVARIANT);
+ *rPar.Get(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.Count() - 1;
+ if ( nArgCount < 2 )
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ else
+ {
+ sal_Int32 nStartPos = 1;
+ sal_Int32 nFirstStringPos = 1;
+
+ if ( nArgCount >= 3 )
+ {
+ nStartPos = rPar.Get(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.Get(4)->GetInteger();
+ }
+ sal_Int32 nPos;
+ const OUString& rToken = rPar.Get(nFirstStringPos + 1)->GetOUString();
+
+ // #97545 Always find empty string
+ if( rToken.isEmpty() )
+ {
+ nPos = nStartPos;
+ }
+ else
+ {
+ const OUString& rStr1 = rPar.Get(nFirstStringPos)->GetOUString();
+ const sal_Int32 nrStr1Len = rStr1.getLength();
+ if (nStartPos > nrStr1Len)
+ {
+ // Start position is greater than the string being searched
+ nPos = 0;
+ }
+ else
+ {
+ if( !bTextMode )
+ {
+ nPos = rStr1.indexOf( rToken, nStartPos - 1 ) + 1;
+ }
+ else
+ {
+ // tdf#139840 - case-insensitive operation for non-ASCII characters
+ i18nutil::SearchOptions2 aSearchOptions;
+ aSearchOptions.searchString = rToken;
+ aSearchOptions.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
+ aSearchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
+ utl::TextSearch textSearch(aSearchOptions);
+
+ sal_Int32 nStart = nStartPos - 1;
+ sal_Int32 nEnd = nrStr1Len;
+ nPos = textSearch.SearchForward(rStr1, &nStart, &nEnd) ? nStart + 1 : 0;
+ }
+ }
+ }
+ rPar.Get(0)->PutLong(nPos);
+ }
+}
+
+
+// InstrRev(string1, string2[, start[, compare]])
+
+void SbRtl_InStrRev(StarBASIC *, SbxArray & rPar, bool)
+{
+ const sal_uInt32 nArgCount = rPar.Count() - 1;
+ if ( nArgCount < 2 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ const OUString aStr1 = rPar.Get(1)->GetOUString();
+ const OUString aToken = rPar.Get(2)->GetOUString();
+
+ sal_Int32 nStartPos = -1;
+ if ( nArgCount >= 3 )
+ {
+ nStartPos = rPar.Get(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.Get(4)->GetInteger();
+ }
+ const 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
+ {
+ // tdf#143332 - case-insensitive operation for non-ASCII characters
+ i18nutil::SearchOptions2 aSearchOptions;
+ aSearchOptions.searchString = aToken;
+ aSearchOptions.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
+ aSearchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
+ utl::TextSearch textSearch(aSearchOptions);
+
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = nStartPos;
+ nPos = textSearch.SearchBackward(aStr1, &nEnd, &nStart) ? nStart : 0;
+ }
+ }
+ }
+ rPar.Get(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.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariableRef pArg = rPar.Get(1);
+ double aDouble= pArg->GetDouble();
+ /*
+ floor( 2.8 ) = 2.0
+ floor( -2.8 ) = -3.0
+ */
+ aDouble = floor( aDouble );
+ rPar.Get(0)->PutDouble(aDouble);
+}
+
+
+void SbRtl_Fix(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariableRef pArg = rPar.Get(1);
+ double aDouble = pArg->GetDouble();
+ if ( aDouble >= 0.0 )
+ aDouble = floor( aDouble );
+ else
+ aDouble = ceil( aDouble );
+ rPar.Get(0)->PutDouble(aDouble);
+}
+
+
+void SbRtl_LCase(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ const CharClass& rCharClass = GetCharClass();
+ OUString aStr(rPar.Get(1)->GetOUString());
+ aStr = rCharClass.lowercase(aStr);
+ rPar.Get(0)->PutString(aStr);
+}
+
+void SbRtl_Left(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 3)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ OUString aStr(rPar.Get(1)->GetOUString());
+ sal_Int32 nResultLen = rPar.Get(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.Get(0)->PutString(aStr);
+ }
+}
+
+void SbRtl_Log(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ double aArg = rPar.Get(1)->GetDouble();
+ if ( aArg > 0 )
+ {
+ double d = log( aArg );
+ checkArithmeticOverflow( d );
+ rPar.Get(0)->PutDouble(d);
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ }
+}
+
+void SbRtl_LTrim(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ OUString aStr(comphelper::string::stripStart(rPar.Get(1)->GetOUString(), ' '));
+ rPar.Get(0)->PutString(aStr);
+}
+
+
+// Mid( String, nStart, nLength )
+
+void SbRtl_Mid(StarBASIC *, SbxArray & rPar, bool bWrite)
+{
+ int nArgCount = rPar.Count() - 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.Get(1)->GetOUString();
+ sal_Int32 nStartPos = rPar.Get(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.Get(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 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ nStartPos = nArgLen;
+ }
+
+ OUString aReplaceStr = rPar.Get(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.Get(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.Get(0)->PutString(aResultStr);
+ }
+ }
+ }
+}
+
+void SbRtl_Oct(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ SbxVariableRef pArg = rPar.Get(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());
+ rPar.Get(0)->PutString(OUString::number(nVal, 8));
+ }
+}
+
+// Replace(expression, find, replace[, start[, count[, compare]]])
+
+void SbRtl_Replace(StarBASIC *, SbxArray & rPar, bool)
+{
+ const sal_uInt32 nArgCount = rPar.Count() - 1;
+ if ( nArgCount < 3 || nArgCount > 6 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ sal_Int32 lStartPos = 1;
+ if (nArgCount >= 4)
+ {
+ if (rPar.Get(4)->GetType() != SbxEMPTY)
+ {
+ lStartPos = rPar.Get(4)->GetLong();
+ }
+ if (lStartPos < 1)
+ {
+ return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
+ }
+ }
+ --lStartPos; // Make it 0-based
+
+ sal_Int32 lCount = -1;
+ if (nArgCount >= 5)
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ {
+ lCount = rPar.Get(5)->GetLong();
+ }
+ if (lCount < -1)
+ {
+ return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
+ }
+ }
+
+ bool bCaseInsensitive;
+ if (nArgCount == 6)
+ {
+ bCaseInsensitive = rPar.Get(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.Get(1)->GetOUString();
+ OUString aFindStr = rPar.Get(2)->GetOUString();
+ const OUString aReplaceStr = rPar.Get(3)->GetOUString();
+
+ OUString aSrcStr(aExpStr);
+ sal_Int32 nPrevPos = std::min(lStartPos, aSrcStr.getLength());
+ css::uno::Sequence<sal_Int32> aOffset;
+ if (bCaseInsensitive)
+ {
+ // tdf#132389: case-insensitive operation for non-ASCII characters
+ // tdf#142487: use css::i18n::Transliteration to correctly handle ß -> ss expansion
+ // tdf#132388: We can't use utl::TextSearch (css::i18n::XTextSearch), because each call to
+ // css::i18n::XTextSearch::SearchForward transliterates input string, making
+ // performance of repeated calls unacceptable
+ auto xTrans = css::i18n::Transliteration::create(comphelper::getProcessComponentContext());
+ xTrans->loadModule(css::i18n::TransliterationModules_IGNORE_CASE, {});
+ aFindStr = xTrans->transliterate(aFindStr, 0, aFindStr.getLength(), aOffset);
+ aSrcStr = xTrans->transliterate(aSrcStr, nPrevPos, aSrcStr.getLength() - nPrevPos, aOffset);
+ nPrevPos = std::distance(aOffset.begin(),
+ std::lower_bound(aOffset.begin(), aOffset.end(), nPrevPos));
+ }
+
+ auto getExpStrPos = [aOffset, nExpLen = aExpStr.getLength()](sal_Int32 nSrcStrPos) -> sal_Int32
+ {
+ assert(!aOffset.hasElements() || aOffset.getLength() >= nSrcStrPos);
+ if (!aOffset.hasElements())
+ return nSrcStrPos;
+ return aOffset.getLength() > nSrcStrPos ? aOffset[nSrcStrPos] : nExpLen;
+ };
+
+ // Note: the result starts from lStartPos, removing everything to the left. See i#94895.
+ OUStringBuffer sResult(aSrcStr.getLength() - nPrevPos);
+ sal_Int32 nCounts = 0;
+ while (lCount == -1 || lCount > nCounts)
+ {
+ sal_Int32 nPos = aSrcStr.indexOf(aFindStr, nPrevPos);
+ if (nPos < 0)
+ break;
+
+ lStartPos = getExpStrPos(nPrevPos);
+ sResult.append(aExpStr.getStr() + lStartPos, getExpStrPos(nPos) - lStartPos);
+ sResult.append(aReplaceStr);
+ nPrevPos = nPos + aFindStr.getLength();
+ nCounts++;
+ }
+ lStartPos = getExpStrPos(nPrevPos);
+ sResult.append(aExpStr.getStr() + lStartPos, aExpStr.getLength() - lStartPos);
+ rPar.Get(0)->PutString(sResult.makeStringAndClear());
+}
+
+void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 3)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ const OUString& rStr = rPar.Get(1)->GetOUString();
+ int nResultLen = rPar.Get(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.Get(0)->PutString(aResultStr);
+ }
+}
+
+void SbRtl_RTL(StarBASIC * pBasic, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutObject(pBasic->getRTL().get());
+}
+
+void SbRtl_RTrim(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ OUString aStr(comphelper::string::stripEnd(rPar.Get(1)->GetOUString(), ' '));
+ rPar.Get(0)->PutString(aStr);
+}
+
+void SbRtl_Sgn(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ double aDouble = rPar.Get(1)->GetDouble();
+ sal_Int16 nResult = 0;
+ if ( aDouble > 0 )
+ {
+ nResult = 1;
+ }
+ else if ( aDouble < 0 )
+ {
+ nResult = -1;
+ }
+ rPar.Get(0)->PutInteger(nResult);
+ }
+}
+
+void SbRtl_Space(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ const sal_Int32 nCount = rPar.Get(1)->GetLong();
+ OUStringBuffer aBuf(nCount);
+ string::padToLength(aBuf, nCount, ' ');
+ rPar.Get(0)->PutString(aBuf.makeStringAndClear());
+ }
+}
+
+void SbRtl_Sqr(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ double aDouble = rPar.Get(1)->GetDouble();
+ if ( aDouble >= 0 )
+ {
+ rPar.Get(0)->PutDouble(sqrt(aDouble));
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ }
+}
+
+void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ OUString aStr;
+ OUString aStrNew("");
+ SbxVariableRef pArg = rPar.Get(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.subView(iZeroSearch);
+ }
+ else
+ {
+ aStrNew = " " + aStr;
+ }
+ }
+ else
+ {
+ aStrNew = aStr;
+ }
+ rPar.Get(0)->PutString(aStrNew);
+ }
+}
+
+void SbRtl_StrComp(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 3)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ rPar.Get(0)->PutEmpty();
+ return;
+ }
+ const OUString& rStr1 = rPar.Get(1)->GetOUString();
+ const OUString& rStr2 = rPar.Get(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.Count() == 4)
+ bTextCompare = rPar.Get(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.Get(0)->PutInteger(sal::static_int_cast<sal_Int16>(nRetValue));
+}
+
+void SbRtl_String(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ sal_Unicode aFiller;
+ sal_Int32 lCount = rPar.Get(1)->GetLong();
+ if( lCount < 0 || lCount > 0xffff )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ if (rPar.Get(2)->GetType() == SbxINTEGER)
+ {
+ aFiller = static_cast<sal_Unicode>(rPar.Get(2)->GetInteger());
+ }
+ else
+ {
+ const OUString& rStr = rPar.Get(2)->GetOUString();
+ aFiller = rStr[0];
+ }
+ OUStringBuffer aBuf(lCount);
+ string::padToLength(aBuf, lCount, aFiller);
+ rPar.Get(0)->PutString(aBuf.makeStringAndClear());
+ }
+}
+
+void SbRtl_Tab(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ else
+ {
+ const sal_Int32 nCount = std::max(rPar.Get(1)->GetLong(), sal_Int32(0));
+ OUStringBuffer aStr(nCount);
+ comphelper::string::padToLength(aStr, nCount, '\t');
+ rPar.Get(0)->PutString(aStr.makeStringAndClear());
+ }
+}
+
+void SbRtl_Tan(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ SbxVariableRef pArg = rPar.Get(1);
+ rPar.Get(0)->PutDouble(tan(pArg->GetDouble()));
+ }
+}
+
+void SbRtl_UCase(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ const CharClass& rCharClass = GetCharClass();
+ OUString aStr(rPar.Get(1)->GetOUString());
+ aStr = rCharClass.uppercase( aStr );
+ rPar.Get(0)->PutString(aStr);
+ }
+}
+
+
+void SbRtl_Val(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ double nResult = 0.0;
+ char* pEndPtr;
+
+ OUString aStr(rPar.Get(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.Get(0)->PutDouble(nResult);
+ }
+}
+
+
+// Helper functions for date conversion
+sal_Int16 implGetDateDay( double aDate )
+{
+ aDate = floor( aDate );
+ Date aRefDate(1899'12'30);
+ aRefDate.AddDays( aDate );
+
+ sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetDay() );
+ return nRet;
+}
+
+sal_Int16 implGetDateMonth( double aDate )
+{
+ Date aRefDate(1899'12'30);
+ sal_Int32 nDays = static_cast<sal_Int32>(aDate);
+ 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.Count() != 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODate(rPar.Get(1))));
+}
+
+// Function to convert date from UNO date (com.sun.star.util.Date)
+void SbRtl_CDateFromUnoDate(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Date>::get()));
+ css::util::Date aUnoDate;
+ if(aAny >>= aUnoDate)
+ SbxDateFromUNODate(rPar.Get(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.Count() != 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ unoToSbxValue(rPar.Get(0), Any(SbxDateToUNOTime(rPar.Get(1))));
+}
+
+// Function to convert date from UNO time (com.sun.star.util.Time)
+void SbRtl_CDateFromUnoTime(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Time>::get()));
+ css::util::Time aUnoTime;
+ if(aAny >>= aUnoTime)
+ SbxDateFromUNOTime(rPar.Get(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.Count() != 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODateTime(rPar.Get(1))));
+}
+
+// Function to convert date from UNO date (com.sun.star.util.Date)
+void SbRtl_CDateFromUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::DateTime>::get()));
+ css::util::DateTime aUnoDT;
+ if(aAny >>= aUnoDT)
+ SbxDateFromUNODateTime(rPar.Get(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.Count() == 2)
+ {
+ double aDate = rPar.Get(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.Get(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.Count() == 2)
+ {
+ do
+ {
+ OUString aStr = rPar.Get(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;
+ std::u16string_view 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.subView( 0, nMonthPos );
+ aMonthStr = aStr.subView( nMonthPos, 2 );
+ aDayStr = aStr.subView( 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.subView( 0, nMonthSep );
+ aMonthStr = aStr.subView( nMonthSep + 1, 2 );
+ aDayStr = aStr.subView( 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 * o3tl::toInt32(aYearStr)),
+ static_cast<sal_Int16>(o3tl::toInt32(aMonthStr)), static_cast<sal_Int16>(o3tl::toInt32(aDayStr)),
+ bUseTwoDigitYear, SbDateCorrection::None, dDate ))
+ break;
+
+ rPar.Get(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.Count() < 4)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ sal_Int16 nYear = rPar.Get(1)->GetInteger();
+ sal_Int16 nMonth = rPar.Get(2)->GetInteger();
+ sal_Int16 nDay = rPar.Get(3)->GetInteger();
+
+ double dDate;
+ if( implDateSerial( nYear, nMonth, nDay, true, SbDateCorrection::RollOver, dDate ) )
+ {
+ rPar.Get(0)->PutDate(dDate);
+ }
+}
+
+void SbRtl_TimeSerial(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 4)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ sal_Int16 nHour = rPar.Get(1)->GetInteger();
+ if ( nHour == 24 )
+ {
+ nHour = 0; // because of UNO DateTimes, which go till 24 o'clock
+ }
+ sal_Int16 nMinute = rPar.Get(2)->GetInteger();
+ sal_Int16 nSecond = rPar.Get(3)->GetInteger();
+ if ((nHour < 0 || nHour > 23) ||
+ (nMinute < 0 || nMinute > 59 ) ||
+ (nSecond < 0 || nSecond > 59 ))
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ rPar.Get(0)->PutDate(implTimeSerial(nHour, nMinute, nSecond)); // JSM
+}
+
+void SbRtl_DateValue(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 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.Get(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.Get(0)->PutDate(fResult);
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
+ }
+ }
+}
+
+void SbRtl_TimeValue(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 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.Get(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.Get(0)->PutDate(fResult);
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
+ }
+ }
+}
+
+void SbRtl_Day(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ SbxVariableRef pArg = rPar.Get(1);
+ double aDate = pArg->GetDate();
+
+ sal_Int16 nDay = implGetDateDay( aDate );
+ rPar.Get(0)->PutInteger(nDay);
+ }
+}
+
+void SbRtl_Year(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ sal_Int16 nYear = implGetDateYear(rPar.Get(1)->GetDate());
+ rPar.Get(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.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ double nArg = rPar.Get(1)->GetDate();
+ sal_Int16 nHour = implGetHour( nArg );
+ rPar.Get(0)->PutInteger(nHour);
+ }
+}
+
+void SbRtl_Minute(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ double nArg = rPar.Get(1)->GetDate();
+ sal_Int16 nMin = implGetMinute( nArg );
+ rPar.Get(0)->PutInteger(nMin);
+ }
+}
+
+void SbRtl_Month(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ sal_Int16 nMonth = implGetDateMonth(rPar.Get(1)->GetDate());
+ rPar.Get(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.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ double nArg = rPar.Get(1)->GetDate();
+ sal_Int16 nSecond = implGetSecond( nArg );
+ rPar.Get(0)->PutInteger(nSecond);
+ }
+}
+
+double Now_Impl()
+{
+ DateTime aDateTime( DateTime::SYSTEM );
+ double aSerial = static_cast<double>(GetDayDiff( aDateTime ));
+ tools::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.Get(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.Get(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
+ tools::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) );
+ const 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 );
+ tools::Long nSeconds = aTime.GetHour();
+ nSeconds *= 3600;
+ nSeconds += aTime.GetMin() * 60;
+ nSeconds += aTime.GetSec();
+ rPar.Get(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.Get(0);
+ if( pMeth->IsString() )
+ {
+ OUString aRes;
+ const 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.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ rPar.Get(0)->PutBool((rPar.Get(1)->GetType() & SbxARRAY) != 0);
+ }
+}
+
+void SbRtl_IsObject(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ SbxVariable* pVar = rPar.Get(1);
+ bool bObject = pVar->IsObject();
+ SbxBase* pObj = (bObject ? pVar->GetObject() : nullptr);
+
+ if( auto pUnoClass = dynamic_cast<SbUnoClass*>( pObj) )
+ {
+ bObject = pUnoClass->getUnoClass().is();
+ }
+ rPar.Get(0)->PutBool(bObject);
+ }
+}
+
+void SbRtl_IsDate(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ // #46134 only string is converted, all other types result in sal_False
+ SbxVariableRef xArg = rPar.Get(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.Get(0)->PutBool(bDate);
+ }
+}
+
+void SbRtl_IsEmpty(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ SbxVariable* pVar = nullptr;
+ if( SbiRuntime::isVBAEnabled() )
+ {
+ pVar = getDefaultProp(rPar.Get(1));
+ }
+ if ( pVar )
+ {
+ pVar->Broadcast( SfxHintId::BasicDataWanted );
+ rPar.Get(0)->PutBool(pVar->IsEmpty());
+ }
+ else
+ {
+ rPar.Get(0)->PutBool(rPar.Get(1)->IsEmpty());
+ }
+ }
+}
+
+void SbRtl_IsError(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ SbxVariable* pVar = rPar.Get(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.Get(0)->PutBool(xError->hasError());
+ }
+ else
+ {
+ rPar.Get(0)->PutBool(rPar.Get(1)->IsErr());
+ }
+ }
+}
+
+void SbRtl_IsNull(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 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.Get(1);
+ bool bNull = rPar.Get(1)->IsNull();
+ if( !bNull && pArg->GetType() == SbxOBJECT )
+ {
+ SbxBase* pObj = pArg->GetObject();
+ if( !pObj )
+ {
+ bNull = true;
+ }
+ }
+ rPar.Get(0)->PutBool(bNull);
+ }
+}
+
+void SbRtl_IsNumeric(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ rPar.Get(0)->PutBool(rPar.Get(1)->IsNumericRTL());
+ }
+}
+
+
+void SbRtl_IsMissing(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ // #57915 Missing is reported by an error
+ rPar.Get(0)->PutBool(rPar.Get(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.moWildCard.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.moWildCard.emplace(aPureFileName);
+ }
+ return aPathStr;
+}
+
+static bool implCheckWildcard(std::u16string_view rName, SbiRTLData const& rRTLData)
+{
+ bool bMatch = true;
+
+ if (rRTLData.moWildCard)
+ {
+ bMatch = rRTLData.moWildCard->Matches(rName);
+ }
+ return bMatch;
+}
+
+
+static bool isRootDir( std::u16string_view 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.Count();
+ 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.Get(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.Get(0)->PutString(aNameOnlyStr);
+ return;
+ }
+
+ try
+ {
+ OUString aDirURLStr;
+ bool bFolder = xSFI->isFolder( aFileURLStr );
+
+ if( bFolder )
+ {
+ aDirURLStr = aFileURLStr;
+ }
+ else
+ {
+ rPar.Get(0)->PutString("");
+ }
+
+ SbAttributes nFlags = SbAttributes::NONE;
+ if ( nParCount > 2 )
+ {
+ rRTLData.nDirFlags = nFlags
+ = static_cast<SbAttributes>(rPar.Get(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.Get(0)->PutString(aPath);
+ }
+ }
+ else
+ {
+ // TODO: OSL
+ if ( nParCount >= 2 )
+ {
+ OUString aFileParam = rPar.Get(1)->GetOUString();
+
+ OUString aDirURL = implSetupWildcard(aFileParam, rRTLData);
+
+ SbAttributes nFlags = SbAttributes::NONE;
+ if ( nParCount > 2 )
+ {
+ rRTLData.nDirFlags = nFlags
+ = static_cast<SbAttributes>(rPar.Get(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.Get(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.Get(0)->PutString(aPath);
+ }
+ }
+}
+
+
+void SbRtl_GetAttr(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() == 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.Get(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.Get(0)->PutInteger(nFlags);
+
+ return;
+ }
+ #endif
+
+ if( hasUno() )
+ {
+ const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
+ if( xSFI.is() )
+ {
+ try
+ {
+ OUString aPath = getFullPath(rPar.Get(1)->GetOUString());
+ bool bExists = false;
+ try { bExists = xSFI->exists( aPath ); }
+ catch(const Exception & ) {}
+ if( !bExists )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
+ }
+
+ 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.Get(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.Get(0)->PutInteger(nFlags);
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+}
+
+
+void SbRtl_FileDateTime(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ OUString aPath = rPar.Get(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 ));
+ tools::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;
+
+ const 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.Get(0)->PutString(aRes);
+ }
+}
+
+
+void SbRtl_EOF(StarBASIC *, SbxArray & rPar, bool)
+{
+ // No changes for UCB
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ sal_Int16 nChannel = rPar.Get(1)->GetInteger();
+ SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
+ SbiStream* pSbStrm = pIO->GetStream( nChannel );
+ if ( !pSbStrm )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
+ }
+ 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.Get(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.Count() != 3)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ sal_Int16 nChannel = rPar.Get(1)->GetInteger();
+ SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
+ SbiStream* pSbStrm = pIO->GetStream( nChannel );
+ if ( !pSbStrm )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
+ }
+ sal_Int16 nRet;
+ if (rPar.Get(2)->GetInteger() == 1)
+ {
+ nRet = static_cast<sal_Int16>(pSbStrm->GetMode());
+ }
+ else
+ {
+ nRet = 0; // System file handle not supported
+ }
+ rPar.Get(0)->PutInteger(nRet);
+ }
+}
+void SbRtl_Loc(StarBASIC *, SbxArray & rPar, bool)
+{
+ // No changes for UCB
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ sal_Int16 nChannel = rPar.Get(1)->GetInteger();
+ SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
+ SbiStream* pSbStrm = pIO->GetStream( nChannel );
+ if ( !pSbStrm )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
+ }
+ 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.Get(0)->PutLong(static_cast<sal_Int32>(nPos));
+ }
+}
+
+void SbRtl_Lof(StarBASIC *, SbxArray & rPar, bool)
+{
+ // No changes for UCB
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ sal_Int16 nChannel = rPar.Get(1)->GetInteger();
+ SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
+ SbiStream* pSbStrm = pIO->GetStream( nChannel );
+ if ( !pSbStrm )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
+ }
+ SvStream* pSvStrm = pSbStrm->GetStrm();
+ sal_uInt64 const nLen = pSvStrm->TellEnd();
+ rPar.Get(0)->PutLong(static_cast<sal_Int32>(nLen));
+ }
+}
+
+
+void SbRtl_Seek(StarBASIC *, SbxArray & rPar, bool)
+{
+ // No changes for UCB
+ int nArgs = static_cast<int>(rPar.Count());
+ if ( nArgs < 2 || nArgs > 3 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ sal_Int16 nChannel = rPar.Get(1)->GetInteger();
+ SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
+ SbiStream* pSbStrm = pIO->GetStream( nChannel );
+ if ( !pSbStrm )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
+ }
+ 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.Get(0)->PutLong(static_cast<sal_Int32>(nPos));
+ }
+ else // Seek-Statement
+ {
+ sal_Int32 nPos = rPar.Get(2)->GetLong();
+ if ( nPos < 1 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ 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.Count();
+ if ( nArgCount < 2 || nArgCount > 3 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ OUString aResult;
+ if( nArgCount == 2 )
+ {
+ rPar.Get(1)->Format(aResult);
+ }
+ else
+ {
+ OUString aFmt(rPar.Get(2)->GetOUString());
+ rPar.Get(1)->Format(aResult, &aFmt);
+ }
+ rPar.Get(0)->PutString(aResult);
+ }
+}
+
+static bool IsMissing(SbxArray& rPar, const sal_uInt32 i)
+{
+ const sal_uInt32 nArgCount = rPar.Count();
+ if (nArgCount <= i)
+ return true;
+
+ SbxVariable* aPar = rPar.Get(i);
+ return (aPar->GetType() == SbxERROR && SbiRuntime::IsMissing(aPar, 1));
+}
+
+static sal_Int16 GetOptionalIntegerParamOrDefault(SbxArray& rPar, const sal_uInt32 i,
+ const sal_Int16 defaultValue)
+{
+ return IsMissing(rPar, i) ? defaultValue : rPar.Get(i)->GetInteger();
+}
+
+static OUString GetOptionalOUStringParamOrDefault(SbxArray& rPar, const sal_uInt32 i,
+ const OUString& defaultValue)
+{
+ return IsMissing(rPar, i) ? defaultValue : rPar.Get(i)->GetOUString();
+}
+
+static void lcl_FormatNumberPercent(SbxArray& rPar, bool isPercent)
+{
+ const sal_uInt32 nArgCount = rPar.Count();
+ if (nArgCount < 2 || nArgCount > 6)
+ {
+ return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
+ }
+
+ // 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.Get(2)->IsEmpty())
+ {
+ nNumDigitsAfterDecimal = rPar.Get(2)->GetInteger();
+ if (nNumDigitsAfterDecimal < -1)
+ {
+ return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
+ }
+ else if (nNumDigitsAfterDecimal > 255)
+ nNumDigitsAfterDecimal %= 256;
+ }
+ if (nNumDigitsAfterDecimal == -1)
+ nNumDigitsAfterDecimal = LocaleDataWrapper::getNumDigits();
+
+ bool bIncludeLeadingDigit = LocaleDataWrapper::isNumLeadingZero();
+ if (nArgCount > 3 && !rPar.Get(3)->IsEmpty())
+ {
+ switch (rPar.Get(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:
+ return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
+ }
+ }
+
+ bool bUseParensForNegativeNumbers = false;
+ if (nArgCount > 4 && !rPar.Get(4)->IsEmpty())
+ {
+ switch (rPar.Get(4)->GetInteger())
+ {
+ case ooo::vba::VbTriState::vbFalse:
+ case ooo::vba::VbTriState::vbUseDefault:
+ // do nothing
+ break;
+ case ooo::vba::VbTriState::vbTrue:
+ bUseParensForNegativeNumbers = true;
+ break;
+ default:
+ return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
+ }
+ }
+
+ bool bGroupDigits = false;
+ if (nArgCount > 5 && !rPar.Get(5)->IsEmpty())
+ {
+ switch (rPar.Get(5)->GetInteger())
+ {
+ case ooo::vba::VbTriState::vbFalse:
+ case ooo::vba::VbTriState::vbUseDefault:
+ // do nothing
+ break;
+ case ooo::vba::VbTriState::vbTrue:
+ bGroupDigits = true;
+ break;
+ default:
+ return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
+ }
+ }
+
+ double fVal = rPar.Get(1)->GetDouble();
+ if (isPercent)
+ fVal *= 100;
+ 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();
+ OUStringBuffer aResult;
+ rtl::math::doubleToUStringBuffer(aResult,
+ fVal, rtl_math_StringFormat_F, nNumDigitsAfterDecimal, decSep,
+ bGroupDigits ? localeData.getDigitGrouping().getConstArray() : nullptr,
+ localeData.getNumThousandSep().toChar());
+
+ if (!bIncludeLeadingDigit && aResult.getLength() > 1)
+ aResult.stripStart('0');
+
+ if (nNumDigitsAfterDecimal > 0)
+ {
+ const sal_Int32 nSepPos = aResult.indexOf(decSep);
+
+ // VBA allows up to 255 digits; rtl::math::doubleToUString outputs up to 15 digits
+ // for ~small numbers, so pad them as appropriate.
+ if (nSepPos >= 0)
+ comphelper::string::padToLength(aResult, nSepPos + nNumDigitsAfterDecimal + 1, '0');
+ }
+
+ if (bNegative)
+ {
+ if (bUseParensForNegativeNumbers)
+ aResult.insert(0, '(').append(')');
+ else
+ aResult.insert(0, '-');
+ }
+ if (isPercent)
+ aResult.append('%');
+ rPar.Get(0)->PutString(aResult.makeStringAndClear());
+}
+
+// https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatnumber-function
+void SbRtl_FormatNumber(StarBASIC*, SbxArray& rPar, bool)
+{
+ return lcl_FormatNumberPercent(rPar, false);
+}
+
+// https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatpercent-function
+void SbRtl_FormatPercent(StarBASIC*, SbxArray& rPar, bool)
+{
+ return lcl_FormatNumberPercent(rPar, true);
+}
+
+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));
+ }
+ }
+};
+
+RandomNumberGenerator& theRandomNumberGenerator()
+{
+ static RandomNumberGenerator theGenerator;
+ return theGenerator;
+}
+
+}
+
+void SbRtl_Randomize(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() > 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ if (rPar.Count() == 2)
+ {
+ int nSeed = static_cast<int>(rPar.Get(1)->GetInteger());
+ theRandomNumberGenerator().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.Count() > 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ std::uniform_real_distribution<double> dist(0.0, 1.0);
+ double const tmp(dist(theRandomNumberGenerator().global_rng));
+ rPar.Get(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.Count();
+ if ( nArgCount < 2 || nArgCount > 5 )
+ {
+ rPar.Get(0)->PutLong(0);
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ oslProcessOption nOptions = osl_Process_SEARCHPATH | osl_Process_DETACHED;
+
+ OUString aCmdLine = rPar.Get(1)->GetOUString();
+ // attach additional parameters - everything must be parsed anyway
+ if( nArgCount >= 4 )
+ {
+ OUString tmp = rPar.Get(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.Get(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.Get(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.Get(0)->PutLong(0);
+ }
+ }
+}
+
+void SbRtl_VarType(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ SbxDataType eType = rPar.Get(1)->GetType();
+ rPar.Get(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 = std::size( 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.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ SbxDataType eType = rPar.Get(1)->GetType();
+ bool bIsArray = ( ( eType & SbxARRAY ) != 0 );
+
+ OUString aRetStr;
+ if ( SbiRuntime::isVBAEnabled() && eType == SbxOBJECT )
+ {
+ aRetStr = getObjectTypeName(rPar.Get(1));
+ }
+ else
+ {
+ aRetStr = getBasicTypeName( eType );
+ }
+ if( bIsArray )
+ {
+ aRetStr += "()";
+ }
+ rPar.Get(0)->PutString(aRetStr);
+ }
+}
+
+void SbRtl_Len(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ const OUString& rStr = rPar.Get(1)->GetOUString();
+ rPar.Get(0)->PutLong(rStr.getLength());
+ }
+}
+
+void SbRtl_DDEInitiate(StarBASIC *, SbxArray & rPar, bool)
+{
+ int nArgs = static_cast<int>(rPar.Count());
+ if ( nArgs != 3 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ const OUString& rApp = rPar.Get(1)->GetOUString();
+ const OUString& rTopic = rPar.Get(2)->GetOUString();
+
+ SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
+ size_t nChannel;
+ ErrCode nDdeErr = pDDE->Initiate( rApp, rTopic, nChannel );
+ if( nDdeErr )
+ {
+ StarBASIC::Error( nDdeErr );
+ }
+ else
+ {
+ rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nChannel));
+ }
+}
+
+void SbRtl_DDETerminate(StarBASIC *, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutEmpty();
+ int nArgs = static_cast<int>(rPar.Count());
+ if ( nArgs != 2 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ size_t nChannel = rPar.Get(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.Get(0)->PutEmpty();
+ int nArgs = static_cast<int>(rPar.Count());
+ if ( nArgs != 1 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ 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.Count());
+ if ( nArgs != 3 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ size_t nChannel = rPar.Get(1)->GetInteger();
+ const OUString& rItem = rPar.Get(2)->GetOUString();
+ SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
+ OUString aResult;
+ ErrCode nDdeErr = pDDE->Request( nChannel, rItem, aResult );
+ if( nDdeErr )
+ {
+ StarBASIC::Error( nDdeErr );
+ }
+ else
+ {
+ rPar.Get(0)->PutString(aResult);
+ }
+}
+
+void SbRtl_DDEExecute(StarBASIC *, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutEmpty();
+ int nArgs = static_cast<int>(rPar.Count());
+ if ( nArgs != 3 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ size_t nChannel = rPar.Get(1)->GetInteger();
+ const OUString& rCommand = rPar.Get(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.Get(0)->PutEmpty();
+ int nArgs = static_cast<int>(rPar.Count());
+ if ( nArgs != 4 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ size_t nChannel = rPar.Get(1)->GetInteger();
+ const OUString& rItem = rPar.Get(2)->GetOUString();
+ const OUString& rData = rPar.Get(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.Count() != 1)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
+ short nChannel = 1;
+ while( nChannel < CHANNELS )
+ {
+ SbiStream* pStrm = pIO->GetStream( nChannel );
+ if( !pStrm )
+ {
+ rPar.Get(0)->PutInteger(nChannel);
+ return;
+ }
+ nChannel++;
+ }
+ StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES );
+}
+
+void SbRtl_LBound(StarBASIC *, SbxArray & rPar, bool)
+{
+ const sal_uInt32 nParCount = rPar.Count();
+ if ( nParCount != 3 && nParCount != 2 )
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxBase* pParObj = rPar.Get(1)->GetObject();
+ SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
+ if( !pArr )
+ return StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
+
+ sal_Int32 nLower, nUpper;
+ short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
+ if (!pArr->GetDim(nDim, nLower, nUpper))
+ return StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
+ rPar.Get(0)->PutLong(nLower);
+}
+
+void SbRtl_UBound(StarBASIC *, SbxArray & rPar, bool)
+{
+ const sal_uInt32 nParCount = rPar.Count();
+ if ( nParCount != 3 && nParCount != 2 )
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxBase* pParObj = rPar.Get(1)->GetObject();
+ SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
+ if( !pArr )
+ return StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
+
+ sal_Int32 nLower, nUpper;
+ short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
+ if (!pArr->GetDim(nDim, nLower, nUpper))
+ return StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
+ rPar.Get(0)->PutLong(nUpper);
+}
+
+void SbRtl_RGB(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 4)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ sal_Int32 nRed = rPar.Get(1)->GetInteger() & 0xFF;
+ sal_Int32 nGreen = rPar.Get(2)->GetInteger() & 0xFF;
+ sal_Int32 nBlue = rPar.Get(3)->GetInteger() & 0xFF;
+ sal_Int32 nRGB;
+
+ SbiInstance* pInst = GetSbData()->pInst;
+ bool bCompatibility = ( pInst && pInst->IsCompatibility() );
+ // See discussion in tdf#145725, here's the quotation from a link indicated in the bugtracker
+ // which explains why we need to manage RGB differently according to VB compatibility
+ // "In other words, the individual color components are stored in the opposite order one would expect.
+ // VB stores the red color component in the low-order byte of the long integer's low-order word,
+ // the green color in the high-order byte of the low-order word, and the blue color in the low-order byte of the high-order word"
+ if( bCompatibility )
+ {
+ nRGB = (nBlue << 16) | (nGreen << 8) | nRed;
+ }
+ else
+ {
+ nRGB = (nRed << 16) | (nGreen << 8) | nBlue;
+ }
+ rPar.Get(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.Count() != 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ sal_Int16 nCol = rPar.Get(1)->GetInteger();
+ if( nCol < 0 || nCol > 15 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ sal_Int32 nRGB = pRGB[ nCol ];
+ rPar.Get(0)->PutLong(nRGB);
+}
+
+static std::vector<sal_uInt8> byteArray2Vec(SbxArray* pArr)
+{
+ std::vector<sal_uInt8> result;
+ if (pArr)
+ {
+ const sal_uInt32 nCount = pArr->Count();
+ result.reserve(nCount + 1); // to avoid reallocation when padding in vbFromUnicode
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ result.push_back(pArr->Get(i)->GetByte());
+ }
+ return result;
+}
+
+// Makes sure to get the byte array if passed, or the string converted to the bytes using
+// StringToByteArray in basic/source/sbx/sbxstr.cxx
+static std::vector<sal_uInt8> getByteArray(SbxValue& val)
+{
+ if (val.GetFullType() == SbxOBJECT)
+ if (auto pObj = val.GetObject())
+ if (pObj->GetType() == (SbxARRAY | SbxBYTE))
+ if (auto pArr = dynamic_cast<SbxArray*>(pObj))
+ return byteArray2Vec(pArr);
+
+ // Convert to string
+ tools::SvRef<SbxValue> pStringValue(new SbxValue(SbxSTRING));
+ *pStringValue = val;
+
+ // Convert string to byte array
+ tools::SvRef<SbxValue> pValue(new SbxValue(SbxOBJECT));
+ pValue->PutObject(new SbxArray(SbxBYTE));
+ *pValue = *pStringValue; // Does the magic of conversion of strings to byte arrays
+ return byteArray2Vec(dynamic_cast<SbxArray*>(pValue->GetObject()));
+}
+
+// StrConv(string, conversion, LCID)
+void SbRtl_StrConv(StarBASIC *, SbxArray & rPar, bool)
+{
+ const sal_uInt32 nArgCount = rPar.Count() - 1;
+ if( nArgCount < 2 || nArgCount > 3 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ sal_Int32 nConversion = rPar.Get(2)->GetLong();
+ LanguageType nLanguage = LANGUAGE_SYSTEM;
+ if (nArgCount == 3)
+ {
+ sal_Int32 lcid = rPar.Get(3)->GetLong();
+ nLanguage = LanguageType(lcid);
+ }
+
+ if (nConversion == ooo::vba::VbStrConv::vbUnicode) // This mode does not combine
+ {
+ // Assume that the passed byte array is encoded in the defined encoding, convert to
+ // UTF-16 and store as string. Passed strings are converted to byte array first.
+ auto inArray = getByteArray(*rPar.Get(1));
+ std::string_view s(reinterpret_cast<char*>(inArray.data()), inArray.size() / sizeof(char));
+ const auto encoding = utl_getWinTextEncodingFromLangStr(LanguageTag(nLanguage).getBcp47());
+ OUString aOUStr = OStringToOUString(s, encoding);
+ rPar.Get(0)->PutString(aOUStr);
+ return;
+ }
+
+ if (nConversion == ooo::vba::VbStrConv::vbFromUnicode) // This mode does not combine
+ {
+ // Assume that the passed byte array is UTF-16-encoded (system-endian), convert to specified
+ // encoding and store as byte array. Passed strings are converted to byte array first.
+ auto inArray = getByteArray(*rPar.Get(1));
+ while (inArray.size() % sizeof(sal_Unicode))
+ inArray.push_back('\0');
+ std::u16string_view s(reinterpret_cast<sal_Unicode*>(inArray.data()),
+ inArray.size() / sizeof(sal_Unicode));
+ const auto encoding = utl_getWinTextEncodingFromLangStr(LanguageTag(nLanguage).getBcp47());
+ OString aOStr = OUStringToOString(s, encoding);
+ const sal_Int32 lb = IsBaseIndexOne() ? 1 : 0;
+ const sal_Int32 ub = lb + aOStr.getLength() - 1;
+ SbxDimArray* pArray = new SbxDimArray(SbxBYTE);
+ pArray->unoAddDim(lb, ub);
+
+ for (sal_Int32 i = 0; i < aOStr.getLength(); ++i)
+ {
+ SbxVariable* pNew = new SbxVariable(SbxBYTE);
+ pNew->PutByte(aOStr[i]);
+ pArray->Put(pNew, i);
+ }
+
+ SbxVariable* retVar = rPar.Get(0);
+ SbxFlagBits nFlags = retVar->GetFlags();
+ retVar->ResetFlag(SbxFlagBits::Fixed);
+ retVar->PutObject(pArray);
+ retVar->SetFlags(nFlags);
+ retVar->SetParameters(nullptr);
+ return;
+ }
+
+ std::vector<TransliterationFlags> aTranslitSet;
+ auto check = [&nConversion, &aTranslitSet](sal_Int32 conv, TransliterationFlags flag)
+ {
+ if ((nConversion & conv) != conv)
+ return false;
+
+ aTranslitSet.push_back(flag);
+ nConversion &= ~conv;
+ return true;
+ };
+
+ // Check mutually exclusive bits together
+
+ if (!check(ooo::vba::VbStrConv::vbProperCase, TransliterationFlags::TITLE_CASE))
+ if (!check(ooo::vba::VbStrConv::vbUpperCase, TransliterationFlags::LOWERCASE_UPPERCASE))
+ check(ooo::vba::VbStrConv::vbLowerCase, TransliterationFlags::UPPERCASE_LOWERCASE);
+
+ if (!check(ooo::vba::VbStrConv::vbWide, TransliterationFlags::HALFWIDTH_FULLWIDTH))
+ check(ooo::vba::VbStrConv::vbNarrow, TransliterationFlags::FULLWIDTH_HALFWIDTH);
+
+ if (!check(ooo::vba::VbStrConv::vbKatakana, TransliterationFlags::HIRAGANA_KATAKANA))
+ check(ooo::vba::VbStrConv::vbHiragana, TransliterationFlags::KATAKANA_HIRAGANA);
+
+ if (nConversion) // unknown / incorrectly combined bits
+ return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
+
+ OUString aStr = rPar.Get(1)->GetOUString();
+ if (!aStr.isEmpty() && !aTranslitSet.empty())
+ {
+ uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
+
+ for (auto transliterationFlag : aTranslitSet)
+ {
+ if (transliterationFlag == TransliterationFlags::TITLE_CASE)
+ {
+ // TransliterationWrapper only handles the first character of the passed string
+ // when handling TITLE_CASE; see Transliteration_titlecase::transliterateImpl in
+ // i18npool/source/transliteration/transliteration_body.cxx
+ CharClass aCharClass{ xContext, LanguageTag(nLanguage) };
+ aStr = aCharClass.titlecase(aCharClass.lowercase(aStr));
+ }
+ else
+ {
+ utl::TransliterationWrapper aWrapper(xContext, transliterationFlag);
+ aStr = aWrapper.transliterate(aStr, nLanguage, 0, aStr.getLength(), nullptr);
+ }
+ }
+ }
+
+ rPar.Get(0)->PutString(aStr);
+}
+
+
+void SbRtl_Beep(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 1)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ Sound::Beep();
+}
+
+void SbRtl_Load(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+
+ SbxBase* pObj = rPar.Get(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.Get(0)->PutEmpty();
+ if (rPar.Count() != 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+
+ SbxBase* pObj = rPar.Get(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.Count() != 2)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ OUString aFileURL = getFullPath(rPar.Get(1)->GetOUString());
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aFileURL, StreamMode::READ ));
+ if( pStream )
+ {
+ Bitmap aBmp;
+ ReadDIB(aBmp, *pStream, true);
+ BitmapEx aBitmapEx(aBmp);
+ Graphic aGraphic(aBitmapEx);
+
+ SbxObjectRef xRef = new SbStdPicture;
+ static_cast<SbStdPicture*>(xRef.get())->SetGraphic( aGraphic );
+ rPar.Get(0)->PutObject(xRef.get());
+ }
+}
+
+void SbRtl_SavePicture(StarBASIC *, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutEmpty();
+ if (rPar.Count() != 3)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ SbxBase* pObj = rPar.Get(1)->GetObject();
+ if (SbStdPicture *pPicture = dynamic_cast<SbStdPicture*>(pObj))
+ {
+ SvFileStream aOStream(rPar.Get(2)->GetOUString(), StreamMode::WRITE | StreamMode::TRUNC);
+ const Graphic& aGraphic = pPicture->GetGraphic();
+ TypeSerializer aSerializer(aOStream);
+ aSerializer.writeGraphic(aGraphic);
+ }
+}
+
+void SbRtl_MsgBox(StarBASIC *, SbxArray & rPar, bool)
+{
+ const sal_uInt32 nArgCount = rPar.Count();
+ if( nArgCount < 2 || nArgCount > 6 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ // tdf#147529 - check for missing parameters
+ if (IsMissing(rPar, 1))
+ {
+ return StarBASIC::Error(ERRCODE_BASIC_NOT_OPTIONAL);
+ }
+
+ // tdf#151012 - initialize optional parameters with their default values (number of buttons)
+ WinBits nType = static_cast<WinBits>(GetOptionalIntegerParamOrDefault(rPar, 2, 0)); // MB_OK
+ 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.Get(1)->GetOUString();
+ // tdf#151012 - initialize optional parameters with their default values (title of dialog box)
+ OUString aTitle = GetOptionalOUStringParamOrDefault(rPar, 3, Application::GetDisplayName());
+
+ WinBits nDialogType = nType & (16+32+64);
+
+ SolarMutexGuard aSolarGuard;
+ weld::Widget* pParent = Application::GetDefDialogParent();
+
+ 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, GetpApp()));
+
+ 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.Get(0)->PutInteger(nRet);
+}
+
+void SbRtl_SetAttr(StarBASIC *, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutEmpty();
+ if (rPar.Count() == 3)
+ {
+ OUString aStr = rPar.Get(1)->GetOUString();
+ SbAttributes nFlags = static_cast<SbAttributes>(rPar.Get(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.Count();
+ 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.Get(1)->GetOUString(),
+ StreamMode::WRITE | StreamMode::TRUNC );
+ p->Dump(aStrm, rPar.Get(2)->GetBool());
+ aStrm.Close();
+ if( aStrm.GetError() != ERRCODE_NONE )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_IO_ERROR );
+ }
+ }
+}
+
+
+void SbRtl_FileExists(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() == 2)
+ {
+ OUString aStr = rPar.Get(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.Get(0)->PutBool(bExists);
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+}
+
+void SbRtl_Partition(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 5)
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ sal_Int32 nNumber = rPar.Get(1)->GetLong();
+ sal_Int32 nStart = rPar.Get(2)->GetLong();
+ sal_Int32 nStop = rPar.Get(3)->GetLong();
+ sal_Int32 nInterval = rPar.Get(4)->GetLong();
+
+ if( nStart < 0 || nStop <= nStart || nInterval < 1 )
+ {
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+
+ // 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 + ":");
+ 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.Get(0)->PutString(aRetStr.makeStringAndClear());
+}
+
+#endif
+
+sal_Int16 implGetDateYear( double aDate )
+{
+ Date aRefDate(1899'12'30);
+ sal_Int32 nDays = static_cast<sal_Int32>(aDate);
+ 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 &&
+#if HAVE_FEATURE_SCRIPTING
+ (bUseTwoDigitYear || SbiRuntime::isVBAEnabled())
+#else
+ bUseTwoDigitYear
+#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);
+ }
+ }
+ }
+
+ rdRet = GetDayDiff(aCurDate);
+ 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 0000000000..b4f7dfb5fd
--- /dev/null
+++ b/basic/source/runtime/methods1.cxx
@@ -0,0 +1,2992 @@
+/* -*- 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 <rtl/math.hxx>
+#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 <rtl/math.h>
+#include <svl/numformat.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 <officecfg/Office/Scripting.hxx>
+
+#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 bNeedsReload = true;
+
+ css::lang::Locale aLocale = Application::GetSettings().GetLanguageTag().getLocale();
+ bNeedsReload = bNeedsReload ||
+ ( aLocale.Language != aLastLocale.Language ||
+ aLocale.Country != aLastLocale.Country ||
+ aLocale.Variant != aLastLocale.Variant );
+ if( bNeedsReload )
+ {
+ bNeedsReload = false;
+ 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.Count();
+ if ( nParCount < 4 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ // 1. parameter is object
+ SbxBase* pObjVar = rPar.Get(1)->GetObject();
+ SbxObject* pObj = nullptr;
+ if( pObjVar )
+ pObj = dynamic_cast<SbxObject*>( pObjVar );
+ if( !pObj )
+ if (auto pSbxVar = dynamic_cast<const SbxVariable*>( pObjVar))
+ pObj = dynamic_cast<SbxObject*>( pSbxVar->GetObject() );
+ if( !pObj )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_PARAMETER );
+ return;
+ }
+
+ // 2. parameter is ProcName
+ OUString aNameStr = rPar.Get(2)->GetOUString();
+
+ // 3. parameter is CallType
+ sal_Int16 nCallType = rPar.Get(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.Get(0);
+ refVar->Put( aVals );
+ }
+ break;
+ case vbLet:
+ case vbSet:
+ {
+ if ( nParCount != 5 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ SbxVariableRef pValVar = rPar.Get(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.Get(i + 4);
+ xArray->Put(pPar, i + 1);
+ }
+ }
+
+ // Call method
+ SbxVariableRef refVar = rPar.Get(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.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ bVal = pSbxVariable->GetBool();
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ rPar.Get(0)->PutBool(bVal);
+}
+
+void SbRtl_CByte(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ sal_uInt8 nByte = 0;
+ if (rPar.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ nByte = pSbxVariable->GetByte();
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ rPar.Get(0)->PutByte(nByte);
+}
+
+void SbRtl_CCur(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_Int64 nCur = 0;
+ if (rPar.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ nCur = pSbxVariable->GetCurrency();
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ rPar.Get(0)->PutCurrency(nCur);
+}
+
+void SbRtl_CDec(StarBASIC *, SbxArray & rPar, bool)
+{
+#ifdef _WIN32
+ SbxDecimal* pDec = nullptr;
+ if (rPar.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ pDec = pSbxVariable->GetDecimal();
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ rPar.Get(0)->PutDecimal(pDec);
+#else
+ rPar.Get(0)->PutEmpty();
+ StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
+#endif
+}
+
+void SbRtl_CDate(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ double nVal = 0.0;
+ if (rPar.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ nVal = pSbxVariable->GetDate();
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ rPar.Get(0)->PutDate(nVal);
+}
+
+void SbRtl_CDbl(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ double nVal = 0.0;
+ if (rPar.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(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.Get(0)->PutDouble(nVal);
+}
+
+void SbRtl_CInt(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ sal_Int16 nVal = 0;
+ if (rPar.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ nVal = pSbxVariable->GetInteger();
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ rPar.Get(0)->PutInteger(nVal);
+}
+
+void SbRtl_CLng(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ sal_Int32 nVal = 0;
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ nVal = pSbxVariable->GetLong();
+ rPar.Get(0)->PutLong(nVal);
+}
+
+void SbRtl_CSng(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ float nVal = float(0.0);
+ if (rPar.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(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.Get(0)->PutSingle(nVal);
+}
+
+void SbRtl_CStr(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ OUString aString = pSbxVariable->GetOUString();
+ rPar.Get(0)->PutString(aString);
+}
+
+void SbRtl_CVar(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ SbxValues aVals( SbxVARIANT );
+ if (rPar.Count() == 2)
+ {
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ pSbxVariable->Get( aVals );
+ }
+ else
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ rPar.Get(0)->Put(aVals);
+}
+
+void SbRtl_CVErr(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ sal_Int16 nErrCode = pSbxVariable->GetInteger();
+ rPar.Get(0)->PutErr(nErrCode);
+}
+
+void SbRtl_Iif(StarBASIC *, SbxArray & rPar, bool) // JSM
+{
+ if (rPar.Count() != 4)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ if (rPar.Get(1)->GetBool())
+ {
+ *rPar.Get(0) = *rPar.Get(2);
+ }
+ else
+ {
+ *rPar.Get(0) = *rPar.Get(3);
+ }
+
+}
+
+void SbRtl_GetSystemType(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 1)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ // Removed for SRC595
+ rPar.Get(0)->PutInteger(-1);
+}
+
+void SbRtl_GetGUIType(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 1)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ // 17.7.2000 Make simple solution for testtool / fat office
+#if defined(_WIN32)
+ rPar.Get(0)->PutInteger(1);
+#elif defined(UNX)
+ rPar.Get(0)->PutInteger(4);
+#else
+ rPar.Get(0)->PutInteger(-1);
+#endif
+}
+
+void SbRtl_Red(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ sal_Int32 nRGB = rPar.Get(1)->GetLong();
+ nRGB &= 0x00FF0000;
+ nRGB >>= 16;
+ rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
+
+}
+
+void SbRtl_Green(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ sal_Int32 nRGB = rPar.Get(1)->GetLong();
+ nRGB &= 0x0000FF00;
+ nRGB >>= 8;
+ rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
+}
+
+void SbRtl_Blue(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ sal_Int32 nRGB = rPar.Get(1)->GetLong();
+ nRGB &= 0x000000FF;
+ rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
+ }
+}
+
+
+void SbRtl_Switch(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nCount = rPar.Count();
+ if( !(nCount & 0x0001 ))
+ {
+ // number of arguments must be odd
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ sal_uInt32 nCurExpr = 1;
+ while( nCurExpr < (nCount-1) )
+ {
+ if (rPar.Get(nCurExpr)->GetBool())
+ {
+ (*rPar.Get(0)) = *(rPar.Get(nCurExpr + 1));
+ return;
+ }
+ nCurExpr += 2;
+ }
+ rPar.Get(0)->PutNull();
+}
+
+//i#64882# Common wait impl for existing Wait and new WaitUntil
+// rtl functions
+void Wait_Impl( bool bDurationBased, SbxArray& rPar )
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ tools::Long nWait = 0;
+ if ( bDurationBased )
+ {
+ double dWait = rPar.Get(1)->GetDouble();
+ double dNow = Now_Impl();
+ double dSecs = ( dWait - dNow ) * 24.0 * 3600.0;
+ nWait = static_cast<tools::Long>( dSecs * 1000 ); // wait in thousands of sec
+ }
+ else
+ {
+ nWait = rPar.Get(1)->GetLong();
+ }
+
+ if( nWait < 0 )
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ Timer aTimer("basic Wait_Impl");
+ aTimer.SetTimeout( nWait );
+ aTimer.Start();
+ while ( aTimer.IsActive() && !Application::IsQuit())
+ {
+ 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.Get(0)->PutInteger(0);
+ Application::Reschedule( true );
+}
+
+void SbRtl_GetGUIVersion(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 1)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ // Removed for SRC595
+ rPar.Get(0)->PutLong(-1);
+}
+
+void SbRtl_Choose(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ sal_Int16 nIndex = rPar.Get(1)->GetInteger();
+ sal_uInt32 nCount = rPar.Count();
+ nCount--;
+ if( nCount == 1 || nIndex > sal::static_int_cast<sal_Int16>(nCount-1) || nIndex < 1 )
+ {
+ rPar.Get(0)->PutNull();
+ return;
+ }
+ (*rPar.Get(0)) = *(rPar.Get(nIndex + 1));
+}
+
+
+void SbRtl_Trim(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() < 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ OUString aStr(comphelper::string::strip(rPar.Get(1)->GetOUString(), ' '));
+ rPar.Get(0)->PutString(aStr);
+ }
+}
+
+void SbRtl_GetSolarVersion(StarBASIC *, SbxArray & rPar, bool)
+{
+ rPar.Get(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.Get(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.Get(0)->PutLong(nResult);
+}
+
+
+void SbRtl_FreeLibrary(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ GetSbData()->pInst->GetDllMgr()->FreeDll(rPar.Get(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.Count() - 1;
+ bool bIncIndex = IsBaseIndexOne();
+ if( nArraySize )
+ {
+ if ( bIncIndex )
+ {
+ pArray->AddDim(1, sal::static_int_cast<sal_Int32>(nArraySize));
+ }
+ else
+ {
+ pArray->AddDim(0, sal::static_int_cast<sal_Int32>(nArraySize) - 1);
+ }
+ }
+ else
+ {
+ pArray->unoAddDim(0, -1);
+ }
+
+ // insert parameters into the array
+ for( sal_uInt32 i = 0 ; i < nArraySize ; i++ )
+ {
+ SbxVariable* pVar = rPar.Get(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->Put(pNew, aIdx);
+ }
+
+ // return array
+ SbxVariableRef refVar = rPar.Get(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.Count() - 1;
+ if( nArrayDims > 0 )
+ {
+ for( sal_uInt32 i = 0; i < nArrayDims ; i++ )
+ {
+ sal_Int32 ub = rPar.Get(i + 1)->GetLong();
+ if( ub < 0 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
+ ub = 0;
+ }
+ pArray->AddDim(0, ub);
+ }
+ }
+ else
+ {
+ pArray->unoAddDim(0, -1);
+ }
+ SbxVariableRef refVar = rPar.Get(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.Count() < 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+
+ OUString aNameStr = rPar.Get(1)->GetOUString();
+
+ SbxBase* pFind = StarBASIC::FindSBXInCurrentScope( aNameStr );
+ SbxObject* pFindObj = nullptr;
+ if( pFind )
+ {
+ pFindObj = dynamic_cast<SbxObject*>( pFind );
+ }
+ SbxVariableRef refVar = rPar.Get(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.Count() < 3)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+
+ SbxBase* pObjVar = rPar.Get(1)->GetObject();
+ SbxObject* pObj = nullptr;
+ if( pObjVar )
+ {
+ pObj = dynamic_cast<SbxObject*>( pObjVar );
+ }
+ if( !pObj )
+ if (auto pSbxVar = dynamic_cast<const SbxVariable*>( pObjVar))
+ pObj = dynamic_cast<SbxObject*>( pSbxVar->GetObject() );
+
+ OUString aNameStr = rPar.Get(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.Get(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.GetDim(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.Get(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.Count() != 4)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ sal_Int16 nFileNo = rPar.Get(1)->GetInteger();
+ SbxVariable* pVar2 = rPar.Get(2);
+ SbxDataType eType2 = pVar2->GetType();
+ bool bHasRecordNo = (eType2 != SbxEMPTY && eType2 != SbxERROR);
+ tools::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)) )
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
+
+ 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.Get(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->GetDims();
+ 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.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ OUString aResult;
+ // should be ANSI but that's not possible under Win16 in the DLL
+ OString aByteStr(OUStringToOString(rPar.Get(1)->GetOUString(),
+ osl_getThreadTextEncoding()));
+ const char* pEnvStr = getenv(aByteStr.getStr());
+ if ( pEnvStr )
+ {
+ aResult = OUString(pEnvStr, strlen(pEnvStr), osl_getThreadTextEncoding());
+ }
+ rPar.Get(0)->PutString(aResult);
+}
+
+static double GetDialogZoomFactor( bool bX, tools::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.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ rPar.Get(0)->PutDouble(GetDialogZoomFactor(true, rPar.Get(1)->GetLong()));
+}
+
+void SbRtl_GetDialogZoomFactorY(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ rPar.Get(0)->PutDouble(GetDialogZoomFactor(false, rPar.Get(1)->GetLong()));
+}
+
+
+void SbRtl_EnableReschedule(StarBASIC *, SbxArray & rPar, bool)
+{
+ rPar.Get(0)->PutEmpty();
+ if (rPar.Count() != 2)
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ if( GetSbData()->pInst )
+ GetSbData()->pInst->EnableReschedule(rPar.Get(1)->GetBool());
+}
+
+void SbRtl_GetSystemTicks(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 1)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ rPar.Get(0)->PutLong(tools::Time::GetSystemTicks());
+}
+
+void SbRtl_GetPathSeparator(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 1)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ rPar.Get(0)->PutString(OUString(SAL_PATHDELIMITER));
+}
+
+void SbRtl_ResolvePath(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ OUString aStr = rPar.Get(1)->GetOUString();
+ rPar.Get(0)->PutString(aStr);
+}
+
+void SbRtl_TypeLen(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxDataType eType = rPar.Get(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.Get(1)->GetOUString().getLength());
+ break;
+
+ default:
+ nLen = 0;
+ break;
+ }
+ rPar.Get(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.Get(0);
+ refVar->PutObject( p );
+}
+
+// Helper functions to convert Url from/to system paths
+void SbRtl_ConvertToUrl(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ OUString aStr = rPar.Get(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.Get(0)->PutString(aFileURL);
+
+}
+
+void SbRtl_ConvertFromUrl(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ OUString aStr = rPar.Get(1)->GetOUString();
+ OUString aSysPath;
+ ::osl::File::getSystemPathFromFileURL( aStr, aSysPath );
+ if( aSysPath.isEmpty() )
+ {
+ aSysPath = aStr;
+ }
+ rPar.Get(0)->PutString(aSysPath);
+}
+
+
+// Provide DefaultContext
+void SbRtl_GetDefaultContext(StarBASIC *, SbxArray & rPar, bool)
+{
+ RTL_Impl_GetDefaultContext( rPar );
+}
+
+void SbRtl_Join(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nParCount = rPar.Count();
+ if ( nParCount != 3 && nParCount != 2 )
+ return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbxBase* pParObj = rPar.Get(1)->GetObject();
+ SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
+ if( !pArr )
+ return StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
+
+ if (pArr->GetDims() != 1)
+ return StarBASIC::Error( ERRCODE_BASIC_WRONG_DIMS ); // Syntax Error?!
+
+ OUString aDelim;
+ if( nParCount == 3 )
+ {
+ aDelim = rPar.Get(2)->GetOUString();
+ }
+ else
+ {
+ aDelim = " ";
+ }
+ OUStringBuffer aRetStr(32);
+ sal_Int32 nLower, nUpper;
+ pArr->GetDim(1, nLower, nUpper);
+ sal_Int32 aIdx[1];
+ for (aIdx[0] = nLower; aIdx[0] <= nUpper; ++aIdx[0])
+ {
+ OUString aStr = pArr->Get(aIdx)->GetOUString();
+ aRetStr.append(aStr);
+ if (aIdx[0] != nUpper)
+ {
+ aRetStr.append(aDelim);
+ }
+ }
+ rPar.Get(0)->PutString(aRetStr.makeStringAndClear());
+
+}
+
+
+void SbRtl_Split(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nParCount = rPar.Count();
+ if ( nParCount < 2 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ OUString aExpression = rPar.Get(1)->GetOUString();
+ sal_Int32 nArraySize = 0;
+ std::vector< OUString > vRet;
+ if( !aExpression.isEmpty() )
+ {
+ OUString aDelim;
+ if( nParCount >= 3 )
+ {
+ aDelim = rPar.Get(2)->GetOUString();
+ }
+ else
+ {
+ aDelim = " ";
+ }
+
+ sal_Int32 nCount = -1;
+ if( nParCount == 4 )
+ {
+ nCount = rPar.Get(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;
+ }
+ }
+
+ // tdf#123025 - split returns an array of substrings
+ SbxDimArray* pArray = new SbxDimArray( SbxSTRING );
+ pArray->unoAddDim(0, nArraySize - 1);
+
+ // insert parameter(s) into the array
+ const bool bIsVBAInterOp = SbiRuntime::isVBAEnabled();
+ for(sal_Int32 i = 0 ; i < nArraySize ; i++ )
+ {
+ // tdf#123025 - split returns an array of substrings
+ SbxVariableRef xVar = new SbxVariable( SbxSTRING );
+ xVar->PutString( vRet[i] );
+ // tdf#144924 - allow the assignment of different data types to the individual elements
+ if (!bIsVBAInterOp)
+ {
+ xVar->ResetFlag(SbxFlagBits::Fixed);
+ }
+ pArray->Put(xVar.get(), &i);
+ }
+
+ // return array
+ SbxVariableRef refVar = rPar.Get(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.Count();
+ 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.Get(1)->GetInteger();
+ if( nVal < 1 || nVal > nMonthCount )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ bool bAbbreviate = false;
+ if( nParCount == 3 )
+ bAbbreviate = rPar.Get(2)->GetBool();
+
+ const CalendarItem2* pCalendarItems = aMonthSeq.getConstArray();
+ const CalendarItem2& rItem = pCalendarItems[nVal - 1];
+
+ OUString aRetStr = ( bAbbreviate ? rItem.AbbrevName : rItem.FullName );
+ rPar.Get(0)->PutString(aRetStr);
+}
+
+// WeekdayName(weekday, abbreviate, firstdayofweek)
+void SbRtl_WeekdayName(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nParCount = rPar.Count();
+ 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.Get(1)->GetInteger();
+ sal_Int16 nFirstDay = 0;
+ if( nParCount == 4 )
+ {
+ nFirstDay = rPar.Get(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.Get(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.Get(0)->PutString(aRetStr);
+}
+
+void SbRtl_Weekday(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nParCount = rPar.Count();
+ if ( nParCount < 2 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ double aDate = rPar.Get(1)->GetDate();
+
+ bool bFirstDay = false;
+ sal_Int16 nFirstDay = 0;
+ if ( nParCount > 2 )
+ {
+ nFirstDay = rPar.Get(2)->GetInteger();
+ bFirstDay = true;
+ }
+ sal_Int16 nDay = implGetWeekDay( aDate, bFirstDay, nFirstDay );
+ rPar.Get(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
+ };
+ auto const pred = [&rStringCode](const IntervalInfo &aInterval) {
+ return rStringCode.equalsIgnoreAsciiCaseAscii(aInterval.mStringCode);
+ };
+
+ auto intervalIter = std::find_if(std::begin(aIntervalTable), std::end(aIntervalTable), pred);
+ if(intervalIter != std::end(aIntervalTable)) {
+ return intervalIter;
+ }
+ 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.Count();
+ if( nParCount != 4 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ OUString aStringCode = rPar.Get(1)->GetOUString();
+ IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
+ if( !pInfo )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ sal_Int32 lNumber = rPar.Get(2)->GetLong();
+ double dDate = rPar.Get(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.Get(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.Count();
+ if( nParCount < 4 || nParCount > 6 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ OUString aStringCode = rPar.Get(1)->GetOUString();
+ IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
+ if( !pInfo )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ double dDate1 = rPar.Get(2)->GetDate();
+ double dDate2 = rPar.Get(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.Get(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.Get(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.Count();
+ if( nParCount < 3 || nParCount > 5 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ OUString aStringCode = rPar.Get(1)->GetOUString();
+ IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
+ if( !pInfo )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ double dDate = rPar.Get(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.Get(3)->GetInteger();
+ bFirstDay = true;
+ }
+ nRet = implGetWeekDay( dDate, bFirstDay, nFirstDay );
+ break;
+ }
+ case INTERVAL_WW:
+ {
+ sal_Int16 nFirstDay = 1; // Default
+ if( nParCount >= 4 )
+ nFirstDay = rPar.Get(3)->GetInteger();
+
+ sal_Int16 nFirstWeek = 1; // Default
+ if( nParCount == 5 )
+ nFirstWeek = rPar.Get(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.Get(0)->PutLong(nRet);
+}
+
+// FormatDateTime(Date[,NamedFormat])
+void SbRtl_FormatDateTime(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nParCount = rPar.Count();
+ if( nParCount < 2 || nParCount > 3 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ double dDate = rPar.Get(1)->GetDate();
+ sal_Int16 nNamedFormat = 0;
+ if( nParCount > 2 )
+ {
+ nNamedFormat = rPar.Get(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 );
+ const 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.Get(0)->PutString(aRetStr);
+}
+
+void SbRtl_Frac(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nParCount = rPar.Count();
+ if( nParCount != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ double dVal = pSbxVariable->GetDouble();
+ if(dVal >= 0)
+ rPar.Get(0)->PutDouble(dVal - ::rtl::math::approxFloor(dVal));
+ else
+ rPar.Get(0)->PutDouble(dVal - ::rtl::math::approxCeil(dVal));
+}
+
+void SbRtl_Round(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nParCount = rPar.Count();
+ if( nParCount != 2 && nParCount != 3 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ double dVal = pSbxVariable->GetDouble();
+ double dRes = 0.0;
+ if( dVal != 0.0 )
+ {
+ sal_Int16 numdecimalplaces = 0;
+ if( nParCount == 3 )
+ {
+ numdecimalplaces = rPar.Get(2)->GetInteger();
+ if( numdecimalplaces < 0 || numdecimalplaces > 22 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ }
+
+ dRes = rtl_math_round(dVal, numdecimalplaces, rtl_math_RoundingMode_HalfEven);
+ }
+ rPar.Get(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.Count() - 1;
+
+ if ( nArgCount < 4 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ // retrieve non-optional params
+
+ Sequence< Any > aParams
+ {
+ Any(rPar.Get(1)->GetDouble()),
+ Any(rPar.Get(2)->GetDouble()),
+ Any(rPar.Get(3)->GetDouble()),
+ Any(rPar.Get(4)->GetDouble())
+ };
+
+ CallFunctionAccessFunction(aParams, "SYD", rPar.Get(0));
+}
+
+void SbRtl_SLN(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 3 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ // retrieve non-optional params
+
+ Sequence< Any > aParams
+ {
+ Any(rPar.Get(1)->GetDouble()),
+ Any(rPar.Get(2)->GetDouble()),
+ Any(rPar.Get(3)->GetDouble())
+ };
+
+ CallFunctionAccessFunction(aParams, "SLN", rPar.Get(0));
+}
+
+void SbRtl_Pmt(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 3 || nArgCount > 5 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ // retrieve non-optional params
+
+ double rate = rPar.Get(1)->GetDouble();
+ double nper = rPar.Get(2)->GetDouble();
+ double pmt = rPar.Get(3)->GetDouble();
+
+ // set default values for Optional args
+ double fv = 0;
+ double type = 0;
+
+ // fv
+ if ( nArgCount >= 4 )
+ {
+ if (rPar.Get(4)->GetType() != SbxEMPTY)
+ fv = rPar.Get(4)->GetDouble();
+ }
+ // type
+ if ( nArgCount >= 5 )
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ type = rPar.Get(5)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ Any(rate),
+ Any(nper),
+ Any(pmt),
+ Any(fv),
+ Any(type)
+ };
+
+ CallFunctionAccessFunction(aParams, "Pmt", rPar.Get(0));
+}
+
+void SbRtl_PPmt(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 4 || nArgCount > 6 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ // retrieve non-optional params
+
+ double rate = rPar.Get(1)->GetDouble();
+ double per = rPar.Get(2)->GetDouble();
+ double nper = rPar.Get(3)->GetDouble();
+ double pv = rPar.Get(4)->GetDouble();
+
+ // set default values for Optional args
+ double fv = 0;
+ double type = 0;
+
+ // fv
+ if ( nArgCount >= 5 )
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ fv = rPar.Get(5)->GetDouble();
+ }
+ // type
+ if ( nArgCount >= 6 )
+ {
+ if (rPar.Get(6)->GetType() != SbxEMPTY)
+ type = rPar.Get(6)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ Any(rate),
+ Any(per),
+ Any(nper),
+ Any(pv),
+ Any(fv),
+ Any(type)
+ };
+
+ CallFunctionAccessFunction(aParams, "PPmt", rPar.Get(0));
+}
+
+void SbRtl_PV(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 3 || nArgCount > 5 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ // retrieve non-optional params
+
+ double rate = rPar.Get(1)->GetDouble();
+ double nper = rPar.Get(2)->GetDouble();
+ double pmt = rPar.Get(3)->GetDouble();
+
+ // set default values for Optional args
+ double fv = 0;
+ double type = 0;
+
+ // fv
+ if ( nArgCount >= 4 )
+ {
+ if (rPar.Get(4)->GetType() != SbxEMPTY)
+ fv = rPar.Get(4)->GetDouble();
+ }
+ // type
+ if ( nArgCount >= 5 )
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ type = rPar.Get(5)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ Any(rate),
+ Any(nper),
+ Any(pmt),
+ Any(fv),
+ Any(type)
+ };
+
+ CallFunctionAccessFunction(aParams, "PV", rPar.Get(0));
+}
+
+void SbRtl_NPV(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 1 || nArgCount > 2 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ Any aValues = sbxToUnoValue(rPar.Get(2),
+ cppu::UnoType<Sequence<double>>::get() );
+
+ // convert for calc functions
+ Sequence< Sequence< double > > sValues(1);
+ aValues >>= sValues.getArray()[ 0 ];
+ aValues <<= sValues;
+
+ Sequence< Any > aParams
+ {
+ Any(rPar.Get(1)->GetDouble()),
+ aValues
+ };
+
+ CallFunctionAccessFunction(aParams, "NPV", rPar.Get(0));
+}
+
+void SbRtl_NPer(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 3 || nArgCount > 5 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ // retrieve non-optional params
+
+ double rate = rPar.Get(1)->GetDouble();
+ double pmt = rPar.Get(2)->GetDouble();
+ double pv = rPar.Get(3)->GetDouble();
+
+ // set default values for Optional args
+ double fv = 0;
+ double type = 0;
+
+ // fv
+ if ( nArgCount >= 4 )
+ {
+ if (rPar.Get(4)->GetType() != SbxEMPTY)
+ fv = rPar.Get(4)->GetDouble();
+ }
+ // type
+ if ( nArgCount >= 5 )
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ type = rPar.Get(5)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ Any(rate),
+ Any(pmt),
+ Any(pv),
+ Any(fv),
+ Any(type)
+ };
+
+ CallFunctionAccessFunction(aParams, "NPer", rPar.Get(0));
+}
+
+void SbRtl_MIRR(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 3 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ // retrieve non-optional params
+
+ Any aValues = sbxToUnoValue(rPar.Get(1),
+ cppu::UnoType<Sequence<double>>::get() );
+
+ // convert for calc functions
+ Sequence< Sequence< double > > sValues(1);
+ aValues >>= sValues.getArray()[ 0 ];
+ aValues <<= sValues;
+
+ Sequence< Any > aParams
+ {
+ aValues,
+ Any(rPar.Get(2)->GetDouble()),
+ Any(rPar.Get(3)->GetDouble())
+ };
+
+ CallFunctionAccessFunction(aParams, "MIRR", rPar.Get(0));
+}
+
+void SbRtl_IRR(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 1 || nArgCount > 2 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ // retrieve non-optional params
+ Any aValues = sbxToUnoValue(rPar.Get(1),
+ cppu::UnoType<Sequence<double>>::get() );
+
+ // convert for calc functions
+ Sequence< Sequence< double > > sValues(1);
+ aValues >>= sValues.getArray()[ 0 ];
+ aValues <<= sValues;
+
+ // set default values for Optional args
+ double guess = 0.1;
+ // guess
+ if ( nArgCount >= 2 )
+ {
+ if (rPar.Get(2)->GetType() != SbxEMPTY)
+ guess = rPar.Get(2)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ aValues,
+ Any(guess)
+ };
+
+ CallFunctionAccessFunction(aParams, "IRR", rPar.Get(0));
+}
+
+void SbRtl_IPmt(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 4 || nArgCount > 6 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ // retrieve non-optional params
+
+ double rate = rPar.Get(1)->GetDouble();
+ double per = rPar.Get(2)->GetInteger();
+ double nper = rPar.Get(3)->GetDouble();
+ double pv = rPar.Get(4)->GetDouble();
+
+ // set default values for Optional args
+ double fv = 0;
+ double type = 0;
+
+ // fv
+ if ( nArgCount >= 5 )
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ fv = rPar.Get(5)->GetDouble();
+ }
+ // type
+ if ( nArgCount >= 6 )
+ {
+ if (rPar.Get(6)->GetType() != SbxEMPTY)
+ type = rPar.Get(6)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ Any(rate),
+ Any(per),
+ Any(nper),
+ Any(pv),
+ Any(fv),
+ Any(type)
+ };
+
+ CallFunctionAccessFunction(aParams, "IPmt", rPar.Get(0));
+}
+
+void SbRtl_FV(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 3 || nArgCount > 5 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ // retrieve non-optional params
+
+ double rate = rPar.Get(1)->GetDouble();
+ double nper = rPar.Get(2)->GetDouble();
+ double pmt = rPar.Get(3)->GetDouble();
+
+ // set default values for Optional args
+ double pv = 0;
+ double type = 0;
+
+ // pv
+ if ( nArgCount >= 4 )
+ {
+ if (rPar.Get(4)->GetType() != SbxEMPTY)
+ pv = rPar.Get(4)->GetDouble();
+ }
+ // type
+ if ( nArgCount >= 5 )
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ type = rPar.Get(5)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ Any(rate),
+ Any(nper),
+ Any(pmt),
+ Any(pv),
+ Any(type)
+ };
+
+ CallFunctionAccessFunction(aParams, "FV", rPar.Get(0));
+}
+
+void SbRtl_DDB(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 1;
+
+ if ( nArgCount < 4 || nArgCount > 5 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+ // retrieve non-optional params
+
+ double cost = rPar.Get(1)->GetDouble();
+ double salvage = rPar.Get(2)->GetDouble();
+ double life = rPar.Get(3)->GetDouble();
+ double period = rPar.Get(4)->GetDouble();
+
+ // set default values for Optional args
+ double factor = 2;
+
+ // factor
+ if ( nArgCount >= 5 )
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ factor = rPar.Get(5)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ Any(cost),
+ Any(salvage),
+ Any(life),
+ Any(period),
+ Any(factor)
+ };
+
+ CallFunctionAccessFunction(aParams, "DDB", rPar.Get(0));
+}
+
+void SbRtl_Rate(StarBASIC *, SbxArray & rPar, bool)
+{
+ sal_uInt32 nArgCount = rPar.Count() - 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.Get(1)->GetDouble();
+ pmt = rPar.Get(2)->GetDouble();
+ pv = rPar.Get(3)->GetDouble();
+
+ // set default values for Optional args
+ double fv = 0;
+ double type = 0;
+ double guess = 0.1;
+
+ // fv
+ if ( nArgCount >= 4 )
+ {
+ if (rPar.Get(4)->GetType() != SbxEMPTY)
+ fv = rPar.Get(4)->GetDouble();
+ }
+
+ // type
+ if ( nArgCount >= 5 )
+ {
+ if (rPar.Get(5)->GetType() != SbxEMPTY)
+ type = rPar.Get(5)->GetDouble();
+ }
+
+ // guess
+ if ( nArgCount >= 6 )
+ {
+ if (rPar.Get(6)->GetType() != SbxEMPTY)
+ guess = rPar.Get(6)->GetDouble();
+ }
+
+ Sequence< Any > aParams
+ {
+ Any(nper),
+ Any(pmt),
+ Any(pv),
+ Any(fv),
+ Any(type),
+ Any(guess)
+ };
+
+ CallFunctionAccessFunction(aParams, "Rate", rPar.Get(0));
+}
+
+void SbRtl_StrReverse(StarBASIC *, SbxArray & rPar, bool)
+{
+ if (rPar.Count() != 2)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ SbxVariable* pSbxVariable = rPar.Get(1);
+ if( pSbxVariable->IsNull() )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ OUString aStr = comphelper::string::reverseString(pSbxVariable->GetOUString());
+ rPar.Get(0)->PutString(aStr);
+}
+
+void SbRtl_CompatibilityMode(StarBASIC *, SbxArray & rPar, bool)
+{
+ bool bEnabled = false;
+ sal_uInt32 nCount = rPar.Count();
+ if ( nCount != 1 && nCount != 2 )
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+
+ SbiInstance* pInst = GetSbData()->pInst;
+ if( pInst )
+ {
+ if ( nCount == 2 )
+ {
+ pInst->EnableCompatibility(rPar.Get(1)->GetBool());
+ }
+ bEnabled = pInst->IsCompatibility();
+ }
+ rPar.Get(0)->PutBool(bEnabled);
+}
+
+void SbRtl_Input(StarBASIC *, SbxArray & rPar, bool)
+{
+ // 2 parameters needed
+ if (rPar.Count() < 3)
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+ sal_uInt16 nByteCount = rPar.Get(1)->GetUShort();
+ sal_Int16 nFileNumber = rPar.Get(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.Get(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.Get(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
+
+bool LibreOffice6FloatingPointMode()
+{
+ static bool bMode = std::getenv("LIBREOFFICE6FLOATINGPOINTMODE") != nullptr;
+
+ return bMode || officecfg::Office::Scripting::Basic::Compatibility::UseLibreOffice6FloatingPointConversion::get();
+}
+
+sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam, sal_Int16 nFirstDay )
+{
+ Date aRefDate(1899'12'30);
+ sal_Int32 nDays = static_cast<sal_Int32>(aDate);
+ 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 0000000000..82478bf6d3
--- /dev/null
+++ b/basic/source/runtime/props.cxx
@@ -0,0 +1,171 @@
+/* -*- 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>
+
+
+// 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.Get(0)->PutLong(StarBASIC::GetErl()); }
+
+void SbRtl_Err(StarBASIC *, SbxArray & rPar, bool bWrite)
+{
+ if( SbiRuntime::isVBAEnabled() )
+ {
+ rPar.Get(0)->PutObject(SbxErrObject::getErrObject().get());
+ }
+ else
+ {
+ if( bWrite )
+ {
+ sal_Int32 nVal = rPar.Get(0)->GetLong();
+ if( nVal <= 65535 )
+ StarBASIC::Error( StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nVal) ) );
+ }
+ else
+ rPar.Get(0)->PutLong(StarBASIC::GetVBErrorCode(StarBASIC::GetErrBasic()));
+ }
+}
+
+void SbRtl_False(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutBool(false); }
+
+void SbRtl_Empty(StarBASIC *, SbxArray &, bool) {}
+
+void SbRtl_Nothing(StarBASIC *, SbxArray & rPar, bool)
+{
+ // return an empty object
+ rPar.Get(0)->PutObject(nullptr);
+}
+
+void SbRtl_Null(StarBASIC *, SbxArray & rPar, bool)
+{
+ // returns an empty object-variable
+ rPar.Get(0)->PutNull();
+}
+
+void SbRtl_PI(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutDouble(M_PI); }
+
+void SbRtl_True(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutBool(true); }
+
+void SbRtl_ATTR_NORMAL(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(0); }
+void SbRtl_ATTR_READONLY(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_ATTR_HIDDEN(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(2); }
+void SbRtl_ATTR_SYSTEM(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(4); }
+void SbRtl_ATTR_VOLUME(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(8); }
+void SbRtl_ATTR_DIRECTORY(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(16); }
+void SbRtl_ATTR_ARCHIVE(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(32); }
+
+void SbRtl_V_EMPTY(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(0); }
+void SbRtl_V_NULL(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_V_INTEGER(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(2); }
+void SbRtl_V_LONG(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(3); }
+void SbRtl_V_SINGLE(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(4); }
+void SbRtl_V_DOUBLE(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(5); }
+void SbRtl_V_CURRENCY(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(6); }
+void SbRtl_V_DATE(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(7); }
+void SbRtl_V_STRING(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(8); }
+
+void SbRtl_MB_OK(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(0); }
+void SbRtl_MB_OKCANCEL(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_MB_ABORTRETRYIGNORE(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(2); }
+void SbRtl_MB_YESNOCANCEL(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(3); }
+void SbRtl_MB_YESNO(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(4); }
+void SbRtl_MB_RETRYCANCEL(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(5); }
+void SbRtl_MB_ICONSTOP(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(16); }
+void SbRtl_MB_ICONQUESTION(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(32); }
+void SbRtl_MB_ICONEXCLAMATION(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(48); }
+void SbRtl_MB_ICONINFORMATION(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(64); }
+void SbRtl_MB_DEFBUTTON1(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(0); }
+void SbRtl_MB_DEFBUTTON2(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(256); }
+void SbRtl_MB_DEFBUTTON3(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(512); }
+void SbRtl_MB_APPLMODAL(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(0); }
+void SbRtl_MB_SYSTEMMODAL(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(4096); }
+
+void SbRtl_IDOK(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_IDCANCEL(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(2); }
+void SbRtl_IDABORT(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(3); }
+void SbRtl_IDRETRY(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(4); }
+void SbRtl_IDIGNORE(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(5); }
+void SbRtl_IDYES(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(6); }
+void SbRtl_IDNO(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(7); }
+
+void SbRtl_CF_TEXT(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_CF_BITMAP(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(2); }
+void SbRtl_CF_METAFILEPICT(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(3); }
+
+void SbRtl_TYP_AUTHORFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(7); }
+void SbRtl_TYP_CHAPTERFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(4); }
+void SbRtl_TYP_CONDTXTFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(27); }
+void SbRtl_TYP_DATEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(0); }
+void SbRtl_TYP_DBFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(19); }
+void SbRtl_TYP_DBNAMEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(3); }
+void SbRtl_TYP_DBNEXTSETFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(24); }
+void SbRtl_TYP_DBNUMSETFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(25); }
+void SbRtl_TYP_DBSETNUMBERFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(26); }
+void SbRtl_TYP_DDEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(14); }
+void SbRtl_TYP_DOCINFOFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(18); }
+void SbRtl_TYP_DOCSTATFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(6); }
+void SbRtl_TYP_EXTUSERFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(30); }
+void SbRtl_TYP_FILENAMEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(2); }
+void SbRtl_TYP_FIXDATEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(31); }
+void SbRtl_TYP_FIXTIMEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(32); }
+void SbRtl_TYP_FORMELFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(10); }
+void SbRtl_TYP_GETFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(9); }
+void SbRtl_TYP_GETREFFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(13); }
+void SbRtl_TYP_HIDDENPARAFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(17); }
+void SbRtl_TYP_HIDDENTXTFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(11); }
+void SbRtl_TYP_INPUTFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(16); }
+void SbRtl_TYP_MACROFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(15); }
+void SbRtl_TYP_NEXTPAGEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(28); }
+void SbRtl_TYP_PAGENUMBERFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(5); }
+void SbRtl_TYP_POSTITFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(21); }
+void SbRtl_TYP_PREVPAGEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(29); }
+void SbRtl_TYP_SEQFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(23); }
+void SbRtl_TYP_SETFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(8); }
+void SbRtl_TYP_SETINPFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(33); }
+void SbRtl_TYP_SETREFFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(12); }
+void SbRtl_TYP_TEMPLNAMEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(22); }
+void SbRtl_TYP_TIMEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_TYP_USERFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(20); }
+void SbRtl_TYP_USRINPFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(34); }
+void SbRtl_TYP_SETREFPAGEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(35); }
+void SbRtl_TYP_GETREFPAGEFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(36); }
+void SbRtl_TYP_INTERNETFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(37); }
+
+void SbRtl_SET_ON(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_SET_OFF(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(0); }
+void SbRtl_TOGGLE(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(2); }
+
+void SbRtl_FRAMEANCHORPAGE(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_FRAMEANCHORPARA(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(14); }
+void SbRtl_FRAMEANCHORCHAR(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(15); }
+
+void SbRtl_CLEAR_ALLTABS(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(2); }
+void SbRtl_CLEAR_TAB(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(1); }
+void SbRtl_SET_TAB(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutInteger(0); }
+
+void SbRtl_TYP_JUMPEDITFLD(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(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 0000000000..bdde50944a
--- /dev/null
+++ b/basic/source/runtime/runtime.cxx
@@ -0,0 +1,4766 @@
+/* -*- 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 <comphelper/diagnose_ex.hxx>
+
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/character.hxx>
+
+#include <svl/numformat.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 );
+
+namespace
+{
+class ScopedWritableGuard
+{
+public:
+ ScopedWritableGuard(const SbxVariableRef& rVar, bool bMakeWritable)
+ : m_rVar(rVar)
+ , m_bReset(bMakeWritable && !rVar->CanWrite())
+ {
+ if (m_bReset)
+ {
+ m_rVar->SetFlag(SbxFlagBits::Write);
+ }
+ }
+ ~ScopedWritableGuard()
+ {
+ if (m_bReset)
+ {
+ m_rVar->ResetFlag(SbxFlagBits::Write);
+ }
+ }
+
+private:
+ SbxVariableRef m_rVar;
+ bool m_bReset;
+};
+}
+
+bool SbiRuntime::isVBAEnabled()
+{
+ SbiInstance* pInst = GetSbData()->pInst;
+ return pInst && pInst->pRun && pInst->pRun->bVBAEnabled;
+}
+
+void StarBASIC::SetVBAEnabled( bool bEnabled )
+{
+ if ( bDocBasic )
+ {
+ bVBAEnabled = bEnabled;
+ }
+}
+
+bool StarBASIC::isVBAEnabled() const
+{
+ return bDocBasic && (bVBAEnabled || SbiRuntime::isVBAEnabled());
+}
+
+struct SbiArgv { // Argv stack:
+ SbxArrayRef refArgv; // Argv
+ short nArgc; // Argc
+
+ SbiArgv(SbxArrayRef refArgv_, short nArgc_) :
+ refArgv(std::move(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_) {}
+};
+
+const 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
+};
+
+const 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
+};
+
+const 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.get() )
+{
+ nFlags = pe ? pe->GetDebugFlags() : BasicDebugFlags::NONE;
+ pIosys = pInst->GetIoSystem();
+ pCode =
+ pStmnt = pImg->GetCode() + nStart;
+ refExprStk = new SbxArray;
+ SetVBAEnabled( pMod->IsVBASupport() );
+ 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->Put(pMeth, 0);
+
+ SbxInfo* pInfo = pMeth ? pMeth->GetInfo() : nullptr;
+ sal_uInt32 nParamCount = pParams ? pParams->Count() : 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->unoAddDim(0, nParamArrayParamCount - 1);
+ for (sal_uInt32 j = i; j < nParamCount ; ++j)
+ {
+ SbxVariable* v = pParams->Get(j);
+ sal_Int32 aDimIndex[1];
+ aDimIndex[0] = j - i;
+ pArray->Put(v, aDimIndex);
+ }
+ SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT );
+ pArrayVar->SetFlag( SbxFlagBits::ReadWrite );
+ pArrayVar->PutObject( pArray );
+ refParams->Put(pArrayVar, i);
+
+ // Block ParamArray for missing parameter
+ pInfo = nullptr;
+ break;
+ }
+
+ SbxVariable* v = pParams->Get(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->Put(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->Put(v, i);
+ }
+ if( p )
+ {
+ refParams->PutAlias(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->unoAddDim(0, -1);
+ SbxVariable* pArrayVar = new SbxVariable( SbxVARIANT );
+ pArrayVar->SetFlag( SbxFlagBits::ReadWrite );
+ pArrayVar->PutObject( pArray );
+ refParams->Put(pArrayVar, nParamCount);
+ }
+}
+
+
+// execute a P-Code
+
+bool SbiRuntime::Step()
+{
+ if( bRun )
+ {
+ static sal_uInt32 nLastTime = osl_getGlobalTimer();
+
+ // in any case check casually!
+ if( !( ++nOps & 0xF ) && pInst->IsReschedule() )
+ {
+ sal_uInt32 nTime = osl_getGlobalTimer();
+ if (nTime - nLastTime > 5) // 20 ms
+ {
+ nLastTime = nTime;
+ Application::Reschedule();
+ }
+ }
+
+ // #i48868 blocked by next call level?
+ while( bBlocked )
+ {
+ if( pInst->IsReschedule() ) // And what if not? Busy loop?
+ {
+ Application::Reschedule();
+ nLastTime = osl_getGlobalTimer();
+ }
+ }
+
+ SbiOpcode eOp = static_cast<SbiOpcode>( *pCode++ );
+ sal_uInt32 nOp1;
+ 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;
+ sal_uInt32 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
+ // tdf#123144 - always translate an error number to a vba error message
+ StarBASIC::MakeErrorText( nError, rMsg );
+ rMsg = StarBASIC::GetErrorText();
+ // 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->Put(pVar, nExprLvl++);
+ }
+}
+
+SbxVariableRef SbiRuntime::PopVar()
+{
+#ifdef DBG_UTIL
+ if( !nExprLvl )
+ {
+ StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
+ return new SbxVariable;
+ }
+#endif
+ SbxVariableRef xVar = refExprStk->Get(--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->Get(static_cast<sal_uInt32>(n));
+}
+
+
+void SbiRuntime::TOSMakeTemp()
+{
+ SbxVariable* p = refExprStk->Get(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->Put(p, nExprLvl - 1);
+ }
+ else if( p->GetRefCount() != 1 )
+ {
+ SbxVariable* pNew = new SbxVariable( *p );
+ pNew->SetFlag( SbxFlagBits::ReadWrite );
+ refExprStk->Put(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();
+ // tdf#85371 - grant explicitly write access to the index variable
+ // since it could be the name of a method itself used in the next statement.
+ ScopedWritableGuard aGuard(p->refVar, p->refVar.get() == pMeth);
+ *(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(nullptr);
+ if (xObjVar)
+ {
+ SbxValues v(SbxVARIANT);
+ // Here it may retrieve the value, and change the type from SbxEMPTY to SbxOBJECT
+ xObjVar->Get(v);
+ if (v.eType == SbxOBJECT)
+ pObj = v.pObj;
+ }
+
+ if (SbxDimArray* pArray = dynamic_cast<SbxDimArray*>(pObj))
+ {
+ p->refEnd = reinterpret_cast<SbxVariable*>(pArray);
+
+ sal_Int32 nDims = pArray->GetDims();
+ 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->GetDim(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 or XIndexAccess?
+ Any aAny = pUnoObj->getUnoAny();
+ Reference<XIndexAccess> xIndexAccess;
+ Reference< XEnumerationAccess > xEnumerationAccess;
+ if( aAny >>= xEnumerationAccess )
+ {
+ p->xEnumeration = xEnumerationAccess->createEnumeration();
+ p->eForType = ForType::EachXEnumeration;
+ }
+ // tdf#130307 - support for each loop for objects exposing XIndexAccess
+ else if (aAny >>= xIndexAccess)
+ {
+ p->eForType = ForType::EachXIndexAccess;
+ p->xIndexAccess = xIndexAccess;
+ p->nCurCollectionIndex = 0;
+ }
+ 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
+ ( std::u16string_view aFuncName,
+ std::u16string_view aDLLName,
+ SbxArray* pArgs, // parameter (from index 1, can be NULL)
+ SbxDataType eResType, // return value
+ bool bCDecl ) // true: according to C-conventions
+{
+ 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();
+
+ // tdf#144353 - do not compute any operation with a missing optional variable
+ if ((p1->GetType() == SbxERROR && IsMissing(p1.get(), 1))
+ || (p2->GetType() == SbxERROR && IsMissing(p2, 1)))
+ {
+ Error(ERRCODE_BASIC_NOT_OPTIONAL);
+ return;
+ }
+
+ p2->ResetFlag( SbxFlagBits::Fixed );
+ p2->Compute( eOp, *p1 );
+
+ checkArithmeticOverflow( p2 );
+}
+
+void SbiRuntime::StepUnary( SbxOperator eOp )
+{
+ TOSMakeTemp();
+ SbxVariable* p = GetTOS();
+ // tdf#144353 - do not compute any operation with a missing optional variable
+ if (p->GetType() == SbxERROR && IsMissing(p, 1))
+ {
+ Error(ERRCODE_BASIC_NOT_OPTIONAL);
+ return;
+ }
+ p->Compute( eOp, *p );
+}
+
+void SbiRuntime::StepCompare( SbxOperator eOp )
+{
+ SbxVariableRef p1 = PopVar();
+ SbxVariableRef p2 = PopVar();
+
+ // tdf#144353 - do not compare a missing optional variable
+ if ((p1->GetType() == SbxERROR && SbiRuntime::IsMissing(p1.get(), 1))
+ || (p2->GetType() == SbxERROR && SbiRuntime::IsMissing(p2.get(), 1)))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_NOT_OPTIONAL);
+ return;
+ }
+
+ // 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;
+ if (start < end && *start == '!')
+ {
+ sResult.append('^');
+ start++;
+ }
+ while (start < end && !seenright)
+ {
+ switch (*start)
+ {
+ case '[':
+ case '?':
+ case '*':
+ sResult.append('\\');
+ sResult.append(*start);
+ break;
+ case ']':
+ sResult.append(*start);
+ seenright = 1;
+ 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();
+
+ // tdf#144353 - do not assign a missing optional variable to a property
+ if (refVal->GetType() == SbxERROR && SbiRuntime::IsMissing(refVal.get(), 1))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_NOT_OPTIONAL);
+ return true;
+ }
+
+ 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 )
+ return false;
+
+ 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;
+}
+
+
+// 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( OUString aObjClass, OUString aObjName,
+ SbxObject* pObjParent, SbModule* pClassModule )
+ : m_aObjClass(std::move( aObjClass ))
+ , m_aObjName(std::move( aObjName ))
+ , 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 {
+
+DimAsNewRecoverHash gaDimAsNewRecoverHash;
+
+}
+
+void removeDimAsNewRecoverItem( SbxVariable* pVar )
+{
+ DimAsNewRecoverHash::iterator it = gaDimAsNewRecoverHash.find( pVar );
+ if( it != gaDimAsNewRecoverHash.end() )
+ {
+ gaDimAsNewRecoverHash.erase( it );
+ }
+}
+
+
+// saving object variable
+// not-object variables will cause errors
+
+constexpr OUString pCollectionStr = u"Collection"_ustr;
+
+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::iterator it = gaDimAsNewRecoverHash.find( refVar.get() );
+ if( it != gaDimAsNewRecoverHash.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 );
+ if( pClassModuleObj != nullptr )
+ {
+ SbModule* pClassModule = pClassModuleObj->getClassModule();
+ gaDimAsNewRecoverHash[refVar.get()] =
+ DimAsNewRecoverItem( aObjClass, pValObj->GetName(), pValObj->GetParent(), pClassModule );
+ }
+ else if( aObjClass.equalsIgnoreAsciiCase( "Collection" ) )
+ {
+ gaDimAsNewRecoverHash[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(aRefValString.subView(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->Count() & 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->Count();)
+ {
+ sal_Int32 lb = pDims->Get(i++)->GetLong();
+ sal_Int32 ub = pDims->Get(i++)->GetLong();
+ if( ub < lb )
+ {
+ Error( ERRCODE_BASIC_OUT_OF_RANGE );
+ ub = lb;
+ }
+ pArray->AddDim(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->unoAddDim(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->Get(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->Put(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->GetDims();
+ const sal_Int32 nDimsOld = pOldArray->GetDims();
+
+ 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->GetDim(i, lBoundNew, uBoundNew);
+ pOldArray->GetDim(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->Put(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->Put(pVal.get(), nArgc++);
+ }
+}
+
+// Input to Variable. The variable is on TOS and is
+// is removed afterwards.
+void SbiRuntime::StepINPUT()
+{
+ OUStringBuffer sin;
+ 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 )
+ {
+ OUString 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;
+ }
+ // tdf#85371 - grant explicitly write access to the index variable
+ // since it could be the name of a method itself used in the next statement.
+ ScopedWritableGuard aGuard(pForStk->refVar, pForStk->refVar.get() == pMeth);
+ 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->Put(xVar.get(), refCaseStk->Count());
+}
+
+// end CASE: free variable
+
+void SbiRuntime::StepENDCASE()
+{
+ if (!refCaseStk.is() || !refCaseStk->Count())
+ {
+ StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
+ }
+ else
+ {
+ refCaseStk->Remove(refCaseStk->Count() - 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 );
+ 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 += OUStringChar(ch);
+ }
+ s += p->GetOUString();
+ if( ch )
+ {
+ s += OUStringChar(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 )
+{
+ // tdf#143707 - check if the data type character was added after the string termination symbol
+ SbxDataType eTypeStr;
+ // #57844 use localized function
+ OUString aStr = pImg->GetString(nOp1, &eTypeStr);
+ // also allow , !!!
+ sal_Int32 iComma = aStr.indexOf(',');
+ if( iComma >= 0 )
+ {
+ aStr = aStr.replaceAt(iComma, 1, u".");
+ }
+ 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() )
+ {
+ // tdf#143707 - Check if there was a data type character after the numeric constant,
+ // added by older versions of the fix of the default values for strings.
+ 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;
+ // tdf#142460 - properly handle boolean values in string pool
+ case 'b': eType = SbxBOOL; break;
+ }
+ }
+ // tdf#143707 - if the data type character is different from the default value, it was added
+ // in basic/source/comp/symtbl.cxx. Hence, change the type of the numeric constant to be loaded.
+ else if (eTypeStr != SbxSTRING)
+ {
+ eType = eTypeStr;
+ }
+ 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( 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( 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->Put(pVal.get(), nArgc);
+ refArgv->PutAlias(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->Get(refArgv->Count() - 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->Put(pVar, refArgv->Count() - 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 = pImg->GetCode() + nOp1;
+}
+
+bool SbiRuntime::EvaluateTopOfStackAsBool()
+{
+ SbxVariableRef tos = PopVar();
+ // In a test e.g. If Null then
+ // will evaluate Null will act as if False
+ if ( bVBAEnabled && tos->IsNull() )
+ {
+ return false;
+ }
+
+ // tdf#151503 - do not evaluate a missing optional variable to a boolean
+ if (tos->GetType() == SbxERROR && IsMissing(tos.get(), 1))
+ {
+ Error(ERRCODE_BASIC_NOT_OPTIONAL);
+ return false;
+ }
+
+ if ( tos->IsObject() )
+ {
+ //GetBool applied to an Object attempts to dereference and evaluate
+ //the underlying value as Bool. Here, we're checking rather that
+ //it is not null
+ return tos->GetObject();
+ }
+ else
+ {
+ return tos->GetBool();
+ }
+}
+
+// evaluate TOS, conditional jump (+target)
+
+void SbiRuntime::StepJUMPT( sal_uInt32 nOp1 )
+{
+ if ( EvaluateTopOfStackAsBool() )
+ {
+ StepJUMP( nOp1 );
+ }
+}
+
+// evaluate TOS, conditional jump (+target)
+
+void SbiRuntime::StepJUMPF( sal_uInt32 nOp1 )
+{
+ if ( !EvaluateTopOfStackAsBool() )
+ {
+ 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>(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 = 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->GetDims();
+
+ // Empty array?
+ if( nDims == 1 && p->pArrayLowerBounds[0] > p->pArrayUpperBounds[0] )
+ {
+ bEndLoop = true;
+ break;
+ }
+ SbxVariable* pVal = pArray->Get(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->Count();
+ if( pForStk->nCurCollectionIndex < nCount )
+ {
+ SbxVariable* pRes = xItemArray->Get(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;
+ }
+ // tdf#130307 - support for each loop for objects exposing XIndexAccess
+ case ForType::EachXIndexAccess:
+ {
+ SbiForStack* p = pForStk;
+ if (!p->xIndexAccess)
+ {
+ SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
+ pForStk->eForType = ForType::Error; // terminate loop at the next iteration
+ }
+ else if (pForStk->nCurCollectionIndex < p->xIndexAccess->getCount())
+ {
+ Any aElem = p->xIndexAccess->getByIndex(pForStk->nCurCollectionIndex);
+ pForStk->nCurCollectionIndex++;
+ 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->Count())
+ StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
+ else
+ {
+ SbxVariableRef xTo = PopVar();
+ SbxVariableRef xFrom = PopVar();
+ SbxVariableRef xCase = refCaseStk->Get(refCaseStk->Count() - 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( 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( 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( 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 )
+ {
+ // tdf#85371 - grant explicitly write access to the dimension variable
+ // since in Star/OpenOffice Basic the upper index border is affected,
+ // and the dimension variable could be the name of the method itself.
+ ScopedWritableGuard aGuard(x2, x2.get() == pMeth);
+ 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( 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->Put(pElem, refLocals->Count());
+ }
+ }
+
+ 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->Put(pElem, refLocals->Count());
+ }
+ }
+ }
+ }
+ // #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 )
+ {
+ const OUString aMethName = pMeth->GetName();
+ // tdf#57308 - check if the name is the current method instance
+ if (pMeth->GetName() == rName)
+ {
+ pElem = pMeth;
+ }
+ else
+ {
+ // for statics, set the method's name in front
+ pElem = pMod->Find(aMethName + ":" + rName, SbxClassType::DontCare);
+ }
+ }
+
+
+ // search in parameter list
+ if( !pElem && pMeth )
+ {
+ SbxInfo* pInfo = pMeth->GetInfo();
+ if( pInfo && refParams.is() )
+ {
+ sal_uInt32 nParamCount = refParams->Count();
+ 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->Get(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->Count();
+ for( i = 1 ; i < nArgCount ; i++ )
+ {
+ if (!refArgv->GetAlias(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->Get(i);
+ OUString aName = refArgv->GetAlias(i);
+ if (!aName.isEmpty())
+ {
+ pNames[i] = aName;
+ }
+ pArg->Put(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->Get(i);
+ OUString aName = refArgv->GetAlias(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->Put(pVar, nCurPar++);
+ }
+ refArgv = pArg;
+ }
+ }
+ // own var as parameter 0
+ refArgv->Put(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->Get(pPar->Get(1)->GetInteger());
+ }
+ }
+ }
+
+ // #42940, set parameter 0 to NULL so that var doesn't contain itself
+ if( pPar )
+ {
+ pPar->Put(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->Count() - 1;
+ if( nParamCount != 1 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return pElem;
+ }
+
+ // get index
+ sal_Int32 nIndex = pPar->Get(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->Put(nullptr, 0);
+ }
+ else if (BasicCollection* pCol = dynamic_cast<BasicCollection*>(pObj.get()))
+ {
+ pElem = new SbxVariable( SbxVARIANT );
+ pPar->Put(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->Count();
+ 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->Put(pVar, iLoop);
+ iLoop--;
+ }
+ }
+ pVar = refParams->Get(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 )
+ {
+ // tdf#143707 - check if the data type character was added after the string
+ // termination symbol, and convert the variable if it was present. The
+ // data type character was added in basic/source/comp/symtbl.cxx.
+ SbxDataType eTypeStr;
+ OUString aDefaultStr = pImg->GetString( nDefaultId, &eTypeStr );
+ pVar = new SbxVariable(pParam-> eType);
+ pVar->PutString( aDefaultStr );
+ if (eTypeStr != SbxSTRING)
+ pVar->Convert(eTypeStr);
+ refParams->Put(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->Put(pVar, nIdx);
+ }
+ bOpt = true;
+ }
+ }
+ }
+ if( !bOpt )
+ {
+ Error( ERRCODE_BASIC_NOT_OPTIONAL );
+ }
+ }
+ else if( eType != SbxVARIANT && static_cast<SbxDataType>(pVar->GetType() & 0x0FFF ) != eType )
+ {
+ // tdf#43003 - convert parameter to the requested type
+ pVar->Convert(eType);
+ }
+ 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->Count())
+ {
+ StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
+ }
+ else
+ {
+ SbxVariableRef xComp = PopVar();
+ SbxVariableRef xCase = refCaseStk->Get(refCaseStk->Count() - 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( 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( 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->Get(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 nExpectedForLevel = static_cast<sal_uInt16>( nOp2 / 0x100 );
+ if( !pGosubStk.empty() )
+ {
+ nExpectedForLevel = nExpectedForLevel + pGosubStk.back().nStartForLvl;
+ }
+
+ // if the actual for-level is too small it'd jump out
+ // of a loop -> corrected
+ while( nForLvl > nExpectedForLevel )
+ {
+ PopFor();
+ }
+ }
+
+ // 16.10.96: #31460 new concept for StepInto/Over/Out
+ // see explanation at SbiInstance::CalcBreakCallLevel
+ 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( nOp2 ) );
+ SbxObjectRef pObj = SbxBase::CreateObject( aClass );
+ if( !pObj )
+ {
+ Error( ERRCODE_BASIC_INVALID_OBJECT );
+ }
+ else
+ {
+ OUString aName( pImg->GetString( nOp1 ) );
+ pObj->SetName( aName );
+ // the object must be able to call the BASIC
+ pObj->SetParent( &rBasic );
+ SbxVariableRef pNew = new SbxVariable;
+ pNew->PutObject( pObj.get() );
+ PushVar( pNew.get() );
+ }
+}
+
+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->GetDims();
+ 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->GetDim(i + 1, nLower, nUpper);
+ const sal_Int32 nSize = nUpper - nLower + 1;
+ nTotalSize *= nSize;
+ }
+
+ // Optimization: pre-allocate underlying container
+ if (nTotalSize > 0)
+ pArray->SbxArray::GetRef(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( nOp2 ) );
+ OUString aName;
+ for( sal_Int32 i = 0 ; i < nTotalSize ; ++i )
+ {
+ if (!bRestored || !pArray->SbxArray::GetRef(i)) // For those left unset after preserve
+ {
+ SbxObjectRef pClassObj = SbxBase::CreateObject(aClass);
+ if (!pClassObj)
+ {
+ Error(ERRCODE_BASIC_INVALID_OBJECT);
+ break;
+ }
+ else
+ {
+ if (aName.isEmpty())
+ aName = pImg->GetString(nOp1);
+ pClassObj->SetName(aName);
+ // the object must be able to call the basic
+ pClassObj->SetParent(&rBasic);
+ pArray->SbxArray::Put(pClassObj.get(), i);
+ }
+ }
+ }
+}
+
+void SbiRuntime::StepTCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 )
+{
+ OUString aName( pImg->GetString( nOp1 ) );
+ OUString aClass( pImg->GetString( nOp2 ) );
+
+ SbxObjectRef pCopyObj = createUserTypeImpl( aClass );
+ if( pCopyObj )
+ {
+ pCopyObj->SetName( aName );
+ }
+ SbxVariableRef pNew = new SbxVariable;
+ pNew->PutObject( pCopyObj.get() );
+ pNew->SetDeclareClassName( aClass );
+ PushVar( pNew.get() );
+}
+
+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(nCount);
+ 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( 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->Put(p, refLocals->Count());
+ }
+}
+
+// establishing a module-global variable (+StringID+type)
+
+void SbiRuntime::StepPUBLIC_Impl( sal_uInt32 nOp1, sal_uInt32 nOp2, bool bUsedForClassModule )
+{
+ OUString aName( pImg->GetString( 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( 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( 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->Put(p, pStatics->Count());
+ }
+ }
+ return p;
+}
+// establishing a static variable (+StringID+type)
+void SbiRuntime::StepSTATIC( sal_uInt32 nOp1, sal_uInt32 nOp2 )
+{
+ OUString aName( pImg->GetString( 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 0000000000..5b123d00d5
--- /dev/null
+++ b/basic/source/runtime/stdobj.cxx
@@ -0,0 +1,1097 @@
+/* -*- 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 denylist 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 Method {
+ RtlCall pFunc;
+ std::u16string_view sName;
+ SbxDataType eType;
+ short nArgs;
+ sal_uInt16 nHash;
+ constexpr Method(std::u16string_view name, SbxDataType type, short args, RtlCall func)
+ : pFunc(func)
+ , sName(name)
+ , eType(type)
+ , nArgs(args)
+ , nHash(SbxVariable::MakeHashCode(name))
+ {
+ }
+};
+
+constexpr Method arg(std::u16string_view name, SbxDataType type, short args = 0)
+{
+ return Method(name, type, args, nullptr);
+}
+
+template <int N> constexpr bool MethodsTableValid(const Method (&rMethods)[N])
+{
+ int nCurMethArgs = 0;
+ int nArgsChecked = 0;
+ bool bFinished = false;
+ for (const auto& m : rMethods)
+ {
+ assert(!bFinished); // no entries after end-of-table entry
+ if (bFinished)
+ return false;
+ if (m.nArgs == -1) // end-of-table entry
+ {
+ assert(nCurMethArgs == nArgsChecked); // last method had correct # of arguments
+ if (nCurMethArgs != nArgsChecked)
+ return false;
+ bFinished = true;
+ }
+ else if (m.pFunc) // main (function/sub/etc) entry
+ {
+ assert(nCurMethArgs == nArgsChecked); // previous method had correct # of arguments
+ if (nCurMethArgs != nArgsChecked)
+ return false;
+ nCurMethArgs = m.nArgs & ARGSMASK_;
+ nArgsChecked = 0;
+ }
+ else // subordinate (argument) entry
+ ++nArgsChecked;
+ }
+ assert(bFinished); // its last entry was end-of-table entry
+ return bFinished;
+}
+
+}
+
+constexpr Method aMethods[] = {
+
+{ u"Abs", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Abs },
+ arg(u"number", SbxDOUBLE),
+
+{ u"Array", SbxOBJECT, FUNCTION_, SbRtl_Array },
+{ u"Asc", SbxLONG, 1 | FUNCTION_, SbRtl_Asc },
+ arg(u"string", SbxSTRING),
+
+{ u"AscW", SbxLONG, 1 | FUNCTION_ | COMPATONLY_, SbRtl_Asc },
+ arg(u"string", SbxSTRING),
+
+{ u"Atn", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Atn },
+ arg(u"number", SbxDOUBLE),
+
+{ u"ATTR_ARCHIVE", SbxINTEGER, CPROP_, SbRtl_ATTR_ARCHIVE },
+{ u"ATTR_DIRECTORY", SbxINTEGER, CPROP_, SbRtl_ATTR_DIRECTORY },
+{ u"ATTR_HIDDEN", SbxINTEGER, CPROP_, SbRtl_ATTR_HIDDEN },
+{ u"ATTR_NORMAL", SbxINTEGER, CPROP_, SbRtl_ATTR_NORMAL },
+{ u"ATTR_READONLY", SbxINTEGER, CPROP_, SbRtl_ATTR_READONLY },
+{ u"ATTR_SYSTEM", SbxINTEGER, CPROP_, SbRtl_ATTR_SYSTEM },
+{ u"ATTR_VOLUME", SbxINTEGER, CPROP_, SbRtl_ATTR_VOLUME },
+
+{ u"Beep", SbxNULL, FUNCTION_, SbRtl_Beep },
+{ u"Blue", SbxINTEGER, 1 | FUNCTION_ | NORMONLY_, SbRtl_Blue },
+ arg(u"RGB-Value", SbxLONG),
+
+{ u"CallByName", SbxVARIANT, 3 | FUNCTION_, SbRtl_CallByName },
+ arg(u"Object", SbxOBJECT),
+ arg(u"ProcName", SbxSTRING),
+ arg(u"CallType", SbxINTEGER),
+
+{ u"CBool", SbxBOOL, 1 | FUNCTION_, SbRtl_CBool },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CByte", SbxBYTE, 1 | FUNCTION_, SbRtl_CByte },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CCur", SbxCURRENCY, 1 | FUNCTION_, SbRtl_CCur },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CDate", SbxDATE, 1 | FUNCTION_, SbRtl_CDate },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CDateFromUnoDate", SbxDATE, 1 | FUNCTION_, SbRtl_CDateFromUnoDate },
+ arg(u"UnoDate", SbxOBJECT),
+
+{ u"CDateToUnoDate", SbxOBJECT, 1 | FUNCTION_, SbRtl_CDateToUnoDate },
+ arg(u"Date", SbxDATE),
+
+{ u"CDateFromUnoTime", SbxDATE, 1 | FUNCTION_, SbRtl_CDateFromUnoTime },
+ arg(u"UnoTime", SbxOBJECT),
+
+{ u"CDateToUnoTime", SbxOBJECT, 1 | FUNCTION_, SbRtl_CDateToUnoTime },
+ arg(u"Time", SbxDATE),
+
+{ u"CDateFromUnoDateTime", SbxDATE, 1 | FUNCTION_, SbRtl_CDateFromUnoDateTime },
+ arg(u"UnoDateTime", SbxOBJECT),
+
+{ u"CDateToUnoDateTime", SbxOBJECT, 1 | FUNCTION_, SbRtl_CDateToUnoDateTime },
+ arg(u"DateTime", SbxDATE),
+
+{ u"CDateFromIso", SbxDATE, 1 | FUNCTION_, SbRtl_CDateFromIso },
+ arg(u"IsoDate", SbxSTRING),
+
+{ u"CDateToIso", SbxSTRING, 1 | FUNCTION_, SbRtl_CDateToIso },
+ arg(u"Date", SbxDATE),
+
+{ u"CDec", SbxDECIMAL, 1 | FUNCTION_, SbRtl_CDec },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CDbl", SbxDOUBLE, 1 | FUNCTION_, SbRtl_CDbl },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CF_BITMAP", SbxINTEGER, CPROP_, SbRtl_CF_BITMAP },
+{ u"CF_METAFILEPICT", SbxINTEGER, CPROP_, SbRtl_CF_METAFILEPICT },
+{ u"CF_TEXT", SbxINTEGER, CPROP_, SbRtl_CF_TEXT },
+{ u"ChDir", SbxNULL, 1 | FUNCTION_, SbRtl_ChDir },
+ arg(u"string", SbxSTRING),
+
+{ u"ChDrive", SbxNULL, 1 | FUNCTION_, SbRtl_ChDrive },
+ arg(u"string", SbxSTRING),
+
+{ u"Choose", SbxVARIANT, 2 | FUNCTION_, SbRtl_Choose },
+ arg(u"Index", SbxINTEGER),
+ arg(u"Expression", SbxVARIANT),
+
+{ u"Chr", SbxSTRING, 1 | FUNCTION_, SbRtl_Chr },
+ arg(u"charcode", SbxLONG),
+
+{ u"ChrW", SbxSTRING, 1 | FUNCTION_ | COMPATONLY_, SbRtl_ChrW },
+ arg(u"charcode", SbxLONG),
+
+{ u"CInt", SbxINTEGER, 1 | FUNCTION_, SbRtl_CInt },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CLEAR_ALLTABS", SbxINTEGER, CPROP_, SbRtl_CLEAR_ALLTABS },
+{ u"CLEAR_TAB", SbxINTEGER, CPROP_, SbRtl_CLEAR_TAB },
+{ u"CLng", SbxLONG, 1 | FUNCTION_, SbRtl_CLng },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CompatibilityMode", SbxBOOL, 1 | FUNCTION_, SbRtl_CompatibilityMode },
+ arg(u"bEnable", SbxBOOL),
+
+{ u"ConvertFromUrl", SbxSTRING, 1 | FUNCTION_, SbRtl_ConvertFromUrl },
+ arg(u"Url", SbxSTRING),
+
+{ u"ConvertToUrl", SbxSTRING, 1 | FUNCTION_, SbRtl_ConvertToUrl },
+ arg(u"SystemPath", SbxSTRING),
+
+{ u"Cos", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Cos },
+ arg(u"number", SbxDOUBLE),
+
+{ u"CreateObject", SbxOBJECT, 1 | FUNCTION_, SbRtl_CreateObject },
+ arg(u"class", SbxSTRING),
+
+{ u"CreateUnoListener", SbxOBJECT, 2 | FUNCTION_, SbRtl_CreateUnoListener },
+ arg(u"prefix", SbxSTRING),
+ arg(u"typename", SbxSTRING),
+
+{ u"CreateUnoDialog", SbxOBJECT, 2 | FUNCTION_, SbRtl_CreateUnoDialog },
+ arg(u"dialoglibrary", SbxOBJECT),
+ arg(u"dialogname", SbxSTRING),
+
+{ u"CreateUnoService", SbxOBJECT, 1 | FUNCTION_, SbRtl_CreateUnoService },
+ arg(u"servicename", SbxSTRING),
+
+{ u"CreateUnoServiceWithArguments", SbxOBJECT, 2 | FUNCTION_, SbRtl_CreateUnoServiceWithArguments },
+ arg(u"servicename", SbxSTRING),
+ arg(u"arguments", SbxARRAY),
+
+{ u"CreateUnoStruct", SbxOBJECT, 1 | FUNCTION_, SbRtl_CreateUnoStruct },
+ arg(u"classname", SbxSTRING),
+
+{ u"CreateUnoValue", SbxOBJECT, 2 | FUNCTION_, SbRtl_CreateUnoValue },
+ arg(u"type", SbxSTRING),
+ arg(u"value", SbxVARIANT),
+
+{ u"CreatePropertySet", SbxOBJECT, 1 | FUNCTION_, SbRtl_CreatePropertySet },
+ arg(u"values", SbxARRAY),
+
+{ u"CSng", SbxSINGLE, 1 | FUNCTION_, SbRtl_CSng },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CStr", SbxSTRING, 1 | FUNCTION_, SbRtl_CStr },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CurDir", SbxSTRING, 1 | FUNCTION_, SbRtl_CurDir },
+ arg(u"string", SbxSTRING),
+
+{ u"CVar", SbxVARIANT, 1 | FUNCTION_, SbRtl_CVar },
+ arg(u"expression", SbxVARIANT),
+
+{ u"CVErr", SbxVARIANT, 1 | FUNCTION_, SbRtl_CVErr },
+ arg(u"expression", SbxVARIANT),
+
+{ u"DDB", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, SbRtl_DDB },
+ arg(u"Cost", SbxDOUBLE),
+ arg(u"Salvage", SbxDOUBLE),
+ arg(u"Life", SbxDOUBLE),
+ arg(u"Period", SbxDOUBLE),
+ arg(u"Factor", SbxVARIANT, OPT_),
+
+{ u"Date", SbxDATE, LFUNCTION_, SbRtl_Date },
+{ u"DateAdd", SbxDATE, 3 | FUNCTION_, SbRtl_DateAdd },
+ arg(u"Interval", SbxSTRING),
+ arg(u"Number", SbxLONG),
+ arg(u"Date", SbxDATE),
+
+{ u"DateDiff", SbxDOUBLE, 5 | FUNCTION_, SbRtl_DateDiff },
+ arg(u"Interval", SbxSTRING),
+ arg(u"Date1", SbxDATE),
+ arg(u"Date2", SbxDATE),
+ arg(u"Firstdayofweek", SbxINTEGER, OPT_),
+ arg(u"Firstweekofyear", SbxINTEGER, OPT_),
+
+{ u"DatePart", SbxLONG, 4 | FUNCTION_, SbRtl_DatePart },
+ arg(u"Interval", SbxSTRING),
+ arg(u"Date", SbxDATE),
+ arg(u"Firstdayofweek", SbxINTEGER, OPT_),
+ arg(u"Firstweekofyear", SbxINTEGER, OPT_),
+
+{ u"DateSerial", SbxDATE, 3 | FUNCTION_, SbRtl_DateSerial },
+ arg(u"Year", SbxINTEGER),
+ arg(u"Month", SbxINTEGER),
+ arg(u"Day", SbxINTEGER),
+
+{ u"DateValue", SbxDATE, 1 | FUNCTION_, SbRtl_DateValue },
+ arg(u"String", SbxSTRING),
+
+{ u"Day", SbxINTEGER, 1 | FUNCTION_, SbRtl_Day },
+ arg(u"Date", SbxDATE),
+
+{ u"Ddeexecute", SbxNULL, 2 | FUNCTION_, SbRtl_DDEExecute },
+ arg(u"Channel", SbxLONG),
+ arg(u"Command", SbxSTRING),
+
+{ u"Ddeinitiate", SbxINTEGER, 2 | FUNCTION_, SbRtl_DDEInitiate },
+ arg(u"Application", SbxSTRING),
+ arg(u"Topic", SbxSTRING),
+
+{ u"Ddepoke", SbxNULL, 3 | FUNCTION_, SbRtl_DDEPoke },
+ arg(u"Channel", SbxLONG),
+ arg(u"Item", SbxSTRING),
+ arg(u"Data", SbxSTRING),
+
+{ u"Dderequest", SbxSTRING, 2 | FUNCTION_, SbRtl_DDERequest },
+ arg(u"Channel", SbxLONG),
+ arg(u"Item", SbxSTRING),
+
+{ u"Ddeterminate", SbxNULL, 1 | FUNCTION_, SbRtl_DDETerminate },
+ arg(u"Channel", SbxLONG),
+
+{ u"Ddeterminateall", SbxNULL, FUNCTION_, SbRtl_DDETerminateAll },
+{ u"DimArray", SbxOBJECT, FUNCTION_, SbRtl_DimArray },
+{ u"Dir", SbxSTRING, 2 | FUNCTION_, SbRtl_Dir },
+ arg(u"Pathname", SbxSTRING, OPT_),
+ arg(u"Attributes", SbxINTEGER, OPT_),
+
+{ u"DoEvents", SbxINTEGER, FUNCTION_, SbRtl_DoEvents },
+{ u"DumpAllObjects", SbxEMPTY, 2 | SUB_, SbRtl_DumpAllObjects },
+ arg(u"FileSpec", SbxSTRING),
+ arg(u"DumpAll", SbxINTEGER, OPT_),
+
+{ u"Empty", SbxVARIANT, CPROP_, SbRtl_Empty },
+{ u"EqualUnoObjects", SbxBOOL, 2 | FUNCTION_, SbRtl_EqualUnoObjects },
+ arg(u"Variant", SbxVARIANT),
+ arg(u"Variant", SbxVARIANT),
+
+{ u"EnableReschedule", SbxNULL, 1 | FUNCTION_, SbRtl_EnableReschedule },
+ arg(u"bEnable", SbxBOOL),
+
+{ u"Environ", SbxSTRING, 1 | FUNCTION_, SbRtl_Environ },
+ arg(u"Environmentstring", SbxSTRING),
+
+{ u"EOF", SbxBOOL, 1 | FUNCTION_, SbRtl_EOF },
+ arg(u"Channel", SbxINTEGER),
+
+{ u"Erl", SbxLONG, ROPROP_, SbRtl_Erl },
+{ u"Err", SbxVARIANT, RWPROP_, SbRtl_Err },
+{ u"Error", SbxSTRING, 1 | FUNCTION_, SbRtl_Error },
+ arg(u"code", SbxLONG),
+
+{ u"Exp", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Exp },
+ arg(u"number", SbxDOUBLE),
+
+{ u"False", SbxBOOL, CPROP_, SbRtl_False },
+{ u"FileAttr", SbxINTEGER, 2 | FUNCTION_, SbRtl_FileAttr },
+ arg(u"Channel", SbxINTEGER),
+ arg(u"Attributes", SbxINTEGER),
+
+{ u"FileCopy", SbxNULL, 2 | FUNCTION_, SbRtl_FileCopy },
+ arg(u"Source", SbxSTRING),
+ arg(u"Destination", SbxSTRING),
+
+{ u"FileDateTime", SbxSTRING, 1 | FUNCTION_, SbRtl_FileDateTime },
+ arg(u"filename", SbxSTRING),
+
+{ u"FileExists", SbxBOOL, 1 | FUNCTION_, SbRtl_FileExists },
+ arg(u"filename", SbxSTRING),
+
+{ u"FileLen", SbxLONG, 1 | FUNCTION_, SbRtl_FileLen },
+ arg(u"filename", SbxSTRING),
+
+{ u"FindObject", SbxOBJECT, 1 | FUNCTION_, SbRtl_FindObject },
+ arg(u"Name", SbxSTRING),
+
+{ u"FindPropertyObject", SbxOBJECT, 2 | FUNCTION_, SbRtl_FindPropertyObject },
+ arg(u"Object", SbxOBJECT),
+ arg(u"Name", SbxSTRING),
+
+{ u"Fix", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Fix },
+ arg(u"number", SbxDOUBLE),
+
+{ u"Format", SbxSTRING, 2 | FUNCTION_, SbRtl_Format },
+ arg(u"expression", SbxVARIANT),
+ arg(u"format", SbxSTRING, OPT_),
+
+{ u"FormatDateTime", SbxSTRING, 2 | FUNCTION_ | COMPATONLY_, SbRtl_FormatDateTime},
+ arg(u"Date", SbxDATE),
+ arg(u"NamedFormat", SbxINTEGER, OPT_),
+
+{ u"FormatNumber", SbxSTRING, 5 | FUNCTION_ | COMPATONLY_, SbRtl_FormatNumber },
+ arg(u"expression", SbxDOUBLE),
+ arg(u"numDigitsAfterDecimal", SbxINTEGER, OPT_),
+ arg(u"includeLeadingDigit", SbxINTEGER, OPT_), // vbTriState
+ arg(u"useParensForNegativeNumbers", SbxINTEGER, OPT_), // vbTriState
+ arg(u"groupDigits", SbxINTEGER, OPT_), // vbTriState
+
+{ u"FormatPercent", SbxSTRING, 5 | FUNCTION_ | COMPATONLY_, SbRtl_FormatPercent },
+ arg(u"expression", SbxDOUBLE),
+ arg(u"numDigitsAfterDecimal", SbxINTEGER, OPT_),
+ arg(u"includeLeadingDigit", SbxINTEGER, OPT_), // vbTriState
+ arg(u"useParensForNegativeNumbers", SbxINTEGER, OPT_), // vbTriState
+ arg(u"groupDigits", SbxINTEGER, OPT_), // vbTriState
+
+{ u"Frac", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Frac },
+ arg(u"number", SbxDOUBLE),
+
+{ u"FRAMEANCHORCHAR", SbxINTEGER, CPROP_, SbRtl_FRAMEANCHORCHAR },
+{ u"FRAMEANCHORPAGE", SbxINTEGER, CPROP_, SbRtl_FRAMEANCHORPAGE },
+{ u"FRAMEANCHORPARA", SbxINTEGER, CPROP_, SbRtl_FRAMEANCHORPARA },
+{ u"FreeFile", SbxINTEGER, FUNCTION_, SbRtl_FreeFile },
+{ u"FreeLibrary", SbxNULL, 1 | FUNCTION_, SbRtl_FreeLibrary },
+ arg(u"Modulename", SbxSTRING),
+
+{ u"FV", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, SbRtl_FV },
+ arg(u"Rate", SbxDOUBLE),
+ arg(u"NPer", SbxDOUBLE),
+ arg(u"Pmt", SbxDOUBLE),
+ arg(u"PV", SbxVARIANT, OPT_),
+ arg(u"Due", SbxVARIANT, OPT_),
+
+{ u"Get", SbxNULL, 3 | FUNCTION_, SbRtl_Get },
+ arg(u"filenumber", SbxINTEGER),
+ arg(u"recordnumber", SbxLONG),
+ arg(u"variablename", SbxVARIANT),
+
+{ u"GetAttr", SbxINTEGER, 1 | FUNCTION_, SbRtl_GetAttr },
+ arg(u"filename", SbxSTRING),
+
+{ u"GetDefaultContext", SbxOBJECT, 0 | FUNCTION_, SbRtl_GetDefaultContext },
+{ u"GetDialogZoomFactorX", SbxDOUBLE, FUNCTION_, SbRtl_GetDialogZoomFactorX },
+{ u"GetDialogZoomFactorY", SbxDOUBLE, FUNCTION_, SbRtl_GetDialogZoomFactorY },
+{ u"GetGUIType", SbxINTEGER, FUNCTION_, SbRtl_GetGUIType },
+{ u"GetGUIVersion", SbxLONG, FUNCTION_, SbRtl_GetGUIVersion },
+{ u"GetPathSeparator", SbxSTRING, FUNCTION_, SbRtl_GetPathSeparator },
+{ u"GetProcessServiceManager", SbxOBJECT, 0 | FUNCTION_, SbRtl_GetProcessServiceManager },
+{ u"GetSolarVersion", SbxLONG, FUNCTION_, SbRtl_GetSolarVersion },
+{ u"GetSystemTicks", SbxLONG, FUNCTION_, SbRtl_GetSystemTicks },
+{ u"GetSystemType", SbxINTEGER, FUNCTION_, SbRtl_GetSystemType },
+{ u"GlobalScope", SbxOBJECT, FUNCTION_, SbRtl_GlobalScope },
+{ u"Green", SbxINTEGER, 1 | FUNCTION_ | NORMONLY_, SbRtl_Green },
+ arg(u"RGB-Value", SbxLONG),
+
+{ u"HasUnoInterfaces", SbxBOOL, 1 | FUNCTION_, SbRtl_HasUnoInterfaces },
+ arg(u"InterfaceName", SbxSTRING),
+
+{ u"Hex", SbxSTRING, 1 | FUNCTION_, SbRtl_Hex },
+ arg(u"number", SbxLONG),
+
+{ u"Hour", SbxINTEGER, 1 | FUNCTION_, SbRtl_Hour },
+ arg(u"Date", SbxDATE),
+
+{ u"IDABORT", SbxINTEGER, CPROP_, SbRtl_IDABORT },
+{ u"IDCANCEL", SbxINTEGER, CPROP_, SbRtl_IDCANCEL },
+{ u"IDIGNORE", SbxINTEGER, CPROP_, SbRtl_IDIGNORE },
+{ u"IDNO", SbxINTEGER, CPROP_, SbRtl_IDNO },
+{ u"IDOK", SbxINTEGER, CPROP_, SbRtl_IDOK },
+{ u"IDRETRY", SbxINTEGER, CPROP_, SbRtl_IDRETRY },
+{ u"IDYES", SbxINTEGER, CPROP_, SbRtl_IDYES },
+
+{ u"Iif", SbxVARIANT, 3 | FUNCTION_, SbRtl_Iif },
+ arg(u"Bool", SbxBOOL),
+ arg(u"Variant1", SbxVARIANT),
+ arg(u"Variant2", SbxVARIANT),
+
+{ u"Input", SbxSTRING, 2 | FUNCTION_ | COMPATONLY_, SbRtl_Input },
+ arg(u"Number", SbxLONG),
+ arg(u"FileNumber", SbxLONG),
+
+{ u"InputBox", SbxSTRING, 5 | FUNCTION_, SbRtl_InputBox },
+ arg(u"Prompt", SbxSTRING),
+ arg(u"Title", SbxSTRING, OPT_),
+ arg(u"Default", SbxSTRING, OPT_),
+ arg(u"XPosTwips", SbxLONG, OPT_),
+ arg(u"YPosTwips", SbxLONG, OPT_),
+
+{ u"InStr", SbxLONG, 4 | FUNCTION_, SbRtl_InStr },
+ arg(u"Start", SbxSTRING, OPT_),
+ arg(u"String1", SbxSTRING),
+ arg(u"String2", SbxSTRING),
+ arg(u"Compare", SbxINTEGER, OPT_),
+
+{ u"InStrRev", SbxLONG, 4 | FUNCTION_ | COMPATONLY_, SbRtl_InStrRev },
+ arg(u"StringCheck", SbxSTRING),
+ arg(u"StringMatch", SbxSTRING),
+ arg(u"Start", SbxSTRING, OPT_),
+ arg(u"Compare", SbxINTEGER, OPT_),
+
+{ u"Int", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Int },
+ arg(u"number", SbxDOUBLE),
+
+{ u"IPmt", SbxDOUBLE, 6 | FUNCTION_ | COMPATONLY_, SbRtl_IPmt },
+ arg(u"Rate", SbxDOUBLE),
+ arg(u"Per", SbxDOUBLE),
+ arg(u"NPer", SbxDOUBLE),
+ arg(u"PV", SbxDOUBLE),
+ arg(u"FV", SbxVARIANT, OPT_),
+ arg(u"Due", SbxVARIANT, OPT_),
+
+{ u"IRR", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, SbRtl_IRR },
+ arg(u"ValueArray", SbxARRAY),
+ arg(u"Guess", SbxVARIANT, OPT_),
+
+{ u"IsArray", SbxBOOL, 1 | FUNCTION_, SbRtl_IsArray },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"IsDate", SbxBOOL, 1 | FUNCTION_, SbRtl_IsDate },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"IsEmpty", SbxBOOL, 1 | FUNCTION_, SbRtl_IsEmpty },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"IsError", SbxBOOL, 1 | FUNCTION_, SbRtl_IsError },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"IsMissing", SbxBOOL, 1 | FUNCTION_, SbRtl_IsMissing },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"IsNull", SbxBOOL, 1 | FUNCTION_, SbRtl_IsNull },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"IsNumeric", SbxBOOL, 1 | FUNCTION_, SbRtl_IsNumeric },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"IsObject", SbxBOOL, 1 | FUNCTION_, SbRtl_IsObject },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"IsUnoStruct", SbxBOOL, 1 | FUNCTION_, SbRtl_IsUnoStruct },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"Join", SbxSTRING, 2 | FUNCTION_, SbRtl_Join },
+ arg(u"SourceArray", SbxOBJECT),
+ arg(u"Delimiter", SbxSTRING),
+
+{ u"Kill", SbxNULL, 1 | FUNCTION_, SbRtl_Kill },
+ arg(u"filespec", SbxSTRING),
+
+{ u"LBound", SbxLONG, 1 | FUNCTION_, SbRtl_LBound },
+ arg(u"Variant", SbxVARIANT),
+
+{ u"LCase", SbxSTRING, 1 | FUNCTION_, SbRtl_LCase },
+ arg(u"string", SbxSTRING),
+
+{ u"Left", SbxSTRING, 2 | FUNCTION_, SbRtl_Left },
+ arg(u"String", SbxSTRING),
+ arg(u"Length", SbxLONG),
+
+{ u"Len", SbxLONG, 1 | FUNCTION_, SbRtl_Len },
+ arg(u"StringOrVariant", SbxVARIANT),
+
+{ u"LenB", SbxLONG, 1 | FUNCTION_, SbRtl_Len },
+ arg(u"StringOrVariant", SbxVARIANT),
+
+{ u"Load", SbxNULL, 1 | FUNCTION_, SbRtl_Load },
+ arg(u"object", SbxOBJECT),
+
+{ u"LoadPicture", SbxOBJECT, 1 | FUNCTION_, SbRtl_LoadPicture },
+ arg(u"string", SbxSTRING),
+
+{ u"Loc", SbxLONG, 1 | FUNCTION_, SbRtl_Loc },
+ arg(u"Channel", SbxINTEGER),
+
+{ u"Lof", SbxLONG, 1 | FUNCTION_, SbRtl_Lof },
+ arg(u"Channel", SbxINTEGER),
+
+{ u"Log", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Log },
+ arg(u"number", SbxDOUBLE),
+
+{ u"LTrim", SbxSTRING, 1 | FUNCTION_, SbRtl_LTrim },
+ arg(u"string", SbxSTRING),
+
+{ u"MB_ABORTRETRYIGNORE", SbxINTEGER, CPROP_, SbRtl_MB_ABORTRETRYIGNORE },
+{ u"MB_APPLMODAL", SbxINTEGER, CPROP_, SbRtl_MB_APPLMODAL },
+{ u"MB_DEFBUTTON1", SbxINTEGER, CPROP_, SbRtl_MB_DEFBUTTON1 },
+{ u"MB_DEFBUTTON2", SbxINTEGER, CPROP_, SbRtl_MB_DEFBUTTON2 },
+{ u"MB_DEFBUTTON3", SbxINTEGER, CPROP_, SbRtl_MB_DEFBUTTON3 },
+{ u"MB_ICONEXCLAMATION", SbxINTEGER, CPROP_, SbRtl_MB_ICONEXCLAMATION },
+{ u"MB_ICONINFORMATION", SbxINTEGER, CPROP_, SbRtl_MB_ICONINFORMATION },
+{ u"MB_ICONQUESTION", SbxINTEGER, CPROP_, SbRtl_MB_ICONQUESTION },
+{ u"MB_ICONSTOP", SbxINTEGER, CPROP_, SbRtl_MB_ICONSTOP },
+{ u"MB_OK", SbxINTEGER, CPROP_, SbRtl_MB_OK },
+{ u"MB_OKCANCEL", SbxINTEGER, CPROP_, SbRtl_MB_OKCANCEL },
+{ u"MB_RETRYCANCEL", SbxINTEGER, CPROP_, SbRtl_MB_RETRYCANCEL },
+{ u"MB_SYSTEMMODAL", SbxINTEGER, CPROP_, SbRtl_MB_SYSTEMMODAL },
+{ u"MB_YESNO", SbxINTEGER, CPROP_, SbRtl_MB_YESNO },
+{ u"MB_YESNOCANCEL", SbxINTEGER, CPROP_, SbRtl_MB_YESNOCANCEL },
+
+{ u"Me", SbxOBJECT, 0 | FUNCTION_ | COMPATONLY_, SbRtl_Me },
+{ u"Mid", SbxSTRING, 3 | LFUNCTION_, SbRtl_Mid },
+ arg(u"String", SbxSTRING),
+ arg(u"Start", SbxLONG),
+ arg(u"Length", SbxLONG, OPT_),
+
+{ u"Minute", SbxINTEGER, 1 | FUNCTION_, SbRtl_Minute },
+ arg(u"Date", SbxDATE),
+
+{ u"MIRR", SbxDOUBLE, 3 | FUNCTION_ | COMPATONLY_, SbRtl_MIRR },
+ arg(u"ValueArray", SbxARRAY),
+ arg(u"FinanceRate", SbxDOUBLE),
+ arg(u"ReinvestRate", SbxDOUBLE),
+
+{ u"MkDir", SbxNULL, 1 | FUNCTION_, SbRtl_MkDir },
+ arg(u"pathname", SbxSTRING),
+
+{ u"Month", SbxINTEGER, 1 | FUNCTION_, SbRtl_Month },
+ arg(u"Date", SbxDATE),
+
+{ u"MonthName", SbxSTRING, 2 | FUNCTION_ | COMPATONLY_, SbRtl_MonthName },
+ arg(u"Month", SbxINTEGER),
+ arg(u"Abbreviate", SbxBOOL, OPT_),
+
+{ u"MsgBox", SbxINTEGER, 5 | FUNCTION_, SbRtl_MsgBox },
+ arg(u"Prompt", SbxSTRING),
+ arg(u"Buttons", SbxINTEGER, OPT_),
+ arg(u"Title", SbxSTRING, OPT_),
+ arg(u"Helpfile", SbxSTRING, OPT_),
+ arg(u"Context", SbxINTEGER, OPT_),
+
+{ u"Nothing", SbxOBJECT, CPROP_, SbRtl_Nothing },
+{ u"Now", SbxDATE, FUNCTION_, SbRtl_Now },
+{ u"NPer", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, SbRtl_NPer },
+ arg(u"Rate", SbxDOUBLE),
+ arg(u"Pmt", SbxDOUBLE),
+ arg(u"PV", SbxDOUBLE),
+ arg(u"FV", SbxVARIANT, OPT_),
+ arg(u"Due", SbxVARIANT, OPT_),
+
+{ u"NPV", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, SbRtl_NPV },
+ arg(u"Rate", SbxDOUBLE),
+ arg(u"ValueArray", SbxARRAY),
+
+{ u"Null", SbxNULL, CPROP_, SbRtl_Null },
+
+{ u"Oct", SbxSTRING, 1 | FUNCTION_, SbRtl_Oct },
+ arg(u"number", SbxLONG),
+
+{ u"Partition", SbxSTRING, 4 | FUNCTION_, SbRtl_Partition },
+ arg(u"number", SbxLONG),
+ arg(u"start", SbxLONG),
+ arg(u"stop", SbxLONG),
+ arg(u"interval", SbxLONG),
+
+{ u"Pi", SbxDOUBLE, CPROP_, SbRtl_PI },
+
+{ u"Pmt", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, SbRtl_Pmt },
+ arg(u"Rate", SbxDOUBLE),
+ arg(u"NPer", SbxDOUBLE),
+ arg(u"PV", SbxDOUBLE),
+ arg(u"FV", SbxVARIANT, OPT_),
+ arg(u"Due", SbxVARIANT, OPT_),
+
+{ u"PPmt", SbxDOUBLE, 6 | FUNCTION_ | COMPATONLY_, SbRtl_PPmt },
+ arg(u"Rate", SbxDOUBLE),
+ arg(u"Per", SbxDOUBLE),
+ arg(u"NPer", SbxDOUBLE),
+ arg(u"PV", SbxDOUBLE),
+ arg(u"FV", SbxVARIANT, OPT_),
+ arg(u"Due", SbxVARIANT, OPT_),
+
+{ u"Put", SbxNULL, 3 | FUNCTION_, SbRtl_Put },
+ arg(u"filenumber", SbxINTEGER),
+ arg(u"recordnumber", SbxLONG),
+ arg(u"variablename", SbxVARIANT),
+
+{ u"PV", SbxDOUBLE, 5 | FUNCTION_ | COMPATONLY_, SbRtl_PV },
+ arg(u"Rate", SbxDOUBLE),
+ arg(u"NPer", SbxDOUBLE),
+ arg(u"Pmt", SbxDOUBLE),
+ arg(u"FV", SbxVARIANT, OPT_),
+ arg(u"Due", SbxVARIANT, OPT_),
+
+{ u"QBColor", SbxLONG, 1 | FUNCTION_, SbRtl_QBColor },
+ arg(u"number", SbxINTEGER),
+
+{ u"Randomize", SbxNULL, 1 | FUNCTION_, SbRtl_Randomize },
+ arg(u"Number", SbxDOUBLE, OPT_),
+
+{ u"Rate", SbxDOUBLE, 6 | FUNCTION_ | COMPATONLY_, SbRtl_Rate },
+ arg(u"NPer", SbxDOUBLE),
+ arg(u"Pmt", SbxDOUBLE),
+ arg(u"PV", SbxDOUBLE),
+ arg(u"FV", SbxVARIANT, OPT_),
+ arg(u"Due", SbxVARIANT, OPT_),
+ arg(u"Guess", SbxVARIANT, OPT_),
+
+{ u"Red", SbxINTEGER, 1 | FUNCTION_ | NORMONLY_, SbRtl_Red },
+ arg(u"RGB-Value", SbxLONG),
+
+{ u"Reset", SbxNULL, 0 | FUNCTION_, SbRtl_Reset },
+{ u"ResolvePath", SbxSTRING, 1 | FUNCTION_, SbRtl_ResolvePath },
+ arg(u"Path", SbxSTRING),
+
+{ u"RGB", SbxLONG, 3 | FUNCTION_, SbRtl_RGB },
+ arg(u"Red", SbxINTEGER),
+ arg(u"Green", SbxINTEGER),
+ arg(u"Blue", SbxINTEGER),
+
+{ u"Replace", SbxSTRING, 6 | FUNCTION_, SbRtl_Replace },
+ arg(u"Expression", SbxSTRING),
+ arg(u"Find", SbxSTRING),
+ arg(u"Replace", SbxSTRING),
+ arg(u"Start", SbxINTEGER, OPT_),
+ arg(u"Count", SbxINTEGER, OPT_),
+ arg(u"Compare", SbxINTEGER, OPT_),
+
+{ u"Right", SbxSTRING, 2 | FUNCTION_, SbRtl_Right },
+ arg(u"String", SbxSTRING),
+ arg(u"Length", SbxLONG),
+
+{ u"RmDir", SbxNULL, 1 | FUNCTION_, SbRtl_RmDir },
+ arg(u"pathname", SbxSTRING),
+
+{ u"Round", SbxDOUBLE, 2 | FUNCTION_ | COMPATONLY_, SbRtl_Round },
+ arg(u"Expression", SbxDOUBLE),
+ arg(u"Numdecimalplaces", SbxINTEGER, OPT_),
+
+{ u"Rnd", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Rnd },
+ arg(u"Number", SbxDOUBLE, OPT_),
+
+{ u"RTL", SbxOBJECT, 0 | FUNCTION_ | COMPATONLY_, SbRtl_RTL },
+{ u"RTrim", SbxSTRING, 1 | FUNCTION_, SbRtl_RTrim },
+ arg(u"string", SbxSTRING),
+
+{ u"SavePicture", SbxNULL, 2 | FUNCTION_, SbRtl_SavePicture },
+ arg(u"object", SbxOBJECT),
+ arg(u"string", SbxSTRING),
+
+{ u"Second", SbxINTEGER, 1 | FUNCTION_, SbRtl_Second },
+ arg(u"Date", SbxDATE),
+
+{ u"Seek", SbxLONG, 1 | FUNCTION_, SbRtl_Seek },
+ arg(u"Channel", SbxINTEGER),
+
+{ u"SendKeys", SbxNULL, 2 | FUNCTION_, SbRtl_SendKeys },
+ arg(u"String", SbxSTRING),
+ arg(u"Wait", SbxBOOL, OPT_),
+
+{ u"SetAttr", SbxNULL, 2 | FUNCTION_, SbRtl_SetAttr },
+ arg(u"PathName", SbxSTRING),
+ arg(u"Attributes", SbxINTEGER),
+
+{ u"SET_OFF", SbxINTEGER, CPROP_, SbRtl_SET_OFF },
+{ u"SET_ON", SbxINTEGER, CPROP_, SbRtl_SET_ON },
+{ u"SET_TAB", SbxINTEGER, CPROP_, SbRtl_SET_TAB },
+{ u"Sgn", SbxINTEGER, 1 | FUNCTION_, SbRtl_Sgn },
+ arg(u"number", SbxDOUBLE),
+
+{ u"Shell", SbxLONG, 2 | FUNCTION_, SbRtl_Shell },
+ arg(u"PathName", SbxSTRING),
+ arg(u"WindowStyle", SbxINTEGER, OPT_),
+
+{ u"Sin", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Sin },
+ arg(u"number", SbxDOUBLE),
+
+{ u"SLN", SbxDOUBLE, 3 | FUNCTION_ | COMPATONLY_, SbRtl_SLN },
+ arg(u"Cost", SbxDOUBLE),
+ arg(u"Double", SbxDOUBLE),
+ arg(u"Life", SbxDOUBLE),
+
+{ u"SYD", SbxDOUBLE, 4 | FUNCTION_ | COMPATONLY_, SbRtl_SYD },
+ arg(u"Cost", SbxDOUBLE),
+ arg(u"Salvage", SbxDOUBLE),
+ arg(u"Life", SbxDOUBLE),
+ arg(u"Period", SbxDOUBLE),
+
+{ u"Space", SbxSTRING, 1 | FUNCTION_, SbRtl_Space },
+ arg(u"Number", SbxLONG),
+
+{ u"Spc", SbxSTRING, 1 | FUNCTION_, SbRtl_Space },
+ arg(u"Number", SbxLONG),
+
+{ u"Split", SbxOBJECT, 3 | FUNCTION_, SbRtl_Split },
+ arg(u"expression", SbxSTRING),
+ arg(u"delimiter", SbxSTRING),
+ arg(u"Limit", SbxLONG),
+
+{ u"Sqr", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Sqr },
+ arg(u"number", SbxDOUBLE),
+
+{ u"Str", SbxSTRING, 1 | FUNCTION_, SbRtl_Str },
+ arg(u"number", SbxDOUBLE),
+
+{ u"StrComp", SbxINTEGER, 3 | FUNCTION_, SbRtl_StrComp },
+ arg(u"String1", SbxSTRING),
+ arg(u"String2", SbxSTRING),
+ arg(u"Compare", SbxINTEGER, OPT_),
+
+{ u"StrConv", SbxOBJECT, 3 | FUNCTION_, SbRtl_StrConv },
+ arg(u"String", SbxSTRING),
+ arg(u"Conversion", SbxSTRING),
+ arg(u"LCID", SbxINTEGER, OPT_),
+
+{ u"String", SbxSTRING, 2 | FUNCTION_, SbRtl_String },
+ arg(u"Number", SbxLONG),
+ arg(u"Character", SbxVARIANT),
+
+{ u"StrReverse", SbxSTRING, 1 | FUNCTION_ | COMPATONLY_, SbRtl_StrReverse },
+ arg(u"String1", SbxSTRING),
+
+{ u"Switch", SbxVARIANT, 2 | FUNCTION_, SbRtl_Switch },
+ arg(u"Expression", SbxVARIANT),
+ arg(u"Value", SbxVARIANT),
+
+{ u"Tab", SbxSTRING, 1 | FUNCTION_, SbRtl_Tab },
+ arg(u"Count", SbxLONG),
+
+{ u"Tan", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Tan },
+ arg(u"number", SbxDOUBLE),
+
+{ u"Time", SbxVARIANT, LFUNCTION_, SbRtl_Time },
+{ u"Timer", SbxDATE, FUNCTION_, SbRtl_Timer },
+{ u"TimeSerial", SbxDATE, 3 | FUNCTION_, SbRtl_TimeSerial },
+ arg(u"Hour", SbxLONG),
+ arg(u"Minute", SbxLONG),
+ arg(u"Second", SbxLONG),
+
+{ u"TimeValue", SbxDATE, 1 | FUNCTION_, SbRtl_TimeValue },
+ arg(u"String", SbxSTRING),
+
+{ u"TOGGLE", SbxINTEGER, CPROP_, SbRtl_TOGGLE },
+{ u"Trim", SbxSTRING, 1 | FUNCTION_, SbRtl_Trim },
+ arg(u"String", SbxSTRING),
+
+{ u"True", SbxBOOL, CPROP_, SbRtl_True },
+{ u"TwipsPerPixelX", SbxLONG, FUNCTION_, SbRtl_TwipsPerPixelX },
+{ u"TwipsPerPixelY", SbxLONG, FUNCTION_, SbRtl_TwipsPerPixelY },
+
+{ u"TYP_AUTHORFLD", SbxINTEGER, CPROP_, SbRtl_TYP_AUTHORFLD },
+{ u"TYP_CHAPTERFLD", SbxINTEGER, CPROP_, SbRtl_TYP_CHAPTERFLD },
+{ u"TYP_CONDTXTFLD", SbxINTEGER, CPROP_, SbRtl_TYP_CONDTXTFLD },
+{ u"TYP_DATEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DATEFLD },
+{ u"TYP_DBFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DBFLD },
+{ u"TYP_DBNAMEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DBNAMEFLD },
+{ u"TYP_DBNEXTSETFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DBNEXTSETFLD },
+{ u"TYP_DBNUMSETFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DBNUMSETFLD },
+{ u"TYP_DBSETNUMBERFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DBSETNUMBERFLD },
+{ u"TYP_DDEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DDEFLD },
+{ u"TYP_DOCINFOFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DOCINFOFLD },
+{ u"TYP_DOCSTATFLD", SbxINTEGER, CPROP_, SbRtl_TYP_DOCSTATFLD },
+{ u"TYP_EXTUSERFLD", SbxINTEGER, CPROP_, SbRtl_TYP_EXTUSERFLD },
+{ u"TYP_FILENAMEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_FILENAMEFLD },
+{ u"TYP_FIXDATEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_FIXDATEFLD },
+{ u"TYP_FIXTIMEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_FIXTIMEFLD },
+{ u"TYP_FORMELFLD", SbxINTEGER, CPROP_, SbRtl_TYP_FORMELFLD },
+{ u"TYP_GETFLD", SbxINTEGER, CPROP_, SbRtl_TYP_GETFLD },
+{ u"TYP_GETREFFLD", SbxINTEGER, CPROP_, SbRtl_TYP_GETREFFLD },
+{ u"TYP_GETREFPAGEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_GETREFPAGEFLD },
+{ u"TYP_HIDDENPARAFLD", SbxINTEGER, CPROP_, SbRtl_TYP_HIDDENPARAFLD },
+{ u"TYP_HIDDENTXTFLD", SbxINTEGER, CPROP_, SbRtl_TYP_HIDDENTXTFLD },
+{ u"TYP_INPUTFLD", SbxINTEGER, CPROP_, SbRtl_TYP_INPUTFLD },
+{ u"TYP_INTERNETFLD", SbxINTEGER, CPROP_, SbRtl_TYP_INTERNETFLD },
+{ u"TYP_JUMPEDITFLD", SbxINTEGER, CPROP_, SbRtl_TYP_JUMPEDITFLD },
+{ u"TYP_MACROFLD", SbxINTEGER, CPROP_, SbRtl_TYP_MACROFLD },
+{ u"TYP_NEXTPAGEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_NEXTPAGEFLD },
+{ u"TYP_PAGENUMBERFLD", SbxINTEGER, CPROP_, SbRtl_TYP_PAGENUMBERFLD },
+{ u"TYP_POSTITFLD", SbxINTEGER, CPROP_, SbRtl_TYP_POSTITFLD },
+{ u"TYP_PREVPAGEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_PREVPAGEFLD },
+{ u"TYP_SEQFLD", SbxINTEGER, CPROP_, SbRtl_TYP_SEQFLD },
+{ u"TYP_SETFLD", SbxINTEGER, CPROP_, SbRtl_TYP_SETFLD },
+{ u"TYP_SETINPFLD", SbxINTEGER, CPROP_, SbRtl_TYP_SETINPFLD },
+{ u"TYP_SETREFFLD", SbxINTEGER, CPROP_, SbRtl_TYP_SETREFFLD },
+{ u"TYP_SETREFPAGEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_SETREFPAGEFLD },
+{ u"TYP_TEMPLNAMEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_TEMPLNAMEFLD },
+{ u"TYP_TIMEFLD", SbxINTEGER, CPROP_, SbRtl_TYP_TIMEFLD },
+{ u"TYP_USERFLD", SbxINTEGER, CPROP_, SbRtl_TYP_USERFLD },
+{ u"TYP_USRINPFLD", SbxINTEGER, CPROP_, SbRtl_TYP_USRINPFLD },
+
+{ u"TypeLen", SbxINTEGER, 1 | FUNCTION_, SbRtl_TypeLen },
+ arg(u"Var", SbxVARIANT),
+
+{ u"TypeName", SbxSTRING, 1 | FUNCTION_, SbRtl_TypeName },
+ arg(u"Varname", SbxVARIANT),
+
+{ u"UBound", SbxLONG, 1 | FUNCTION_, SbRtl_UBound },
+ arg(u"Var", SbxVARIANT),
+
+{ u"UCase", SbxSTRING, 1 | FUNCTION_, SbRtl_UCase },
+ arg(u"String", SbxSTRING),
+
+{ u"Unload", SbxNULL, 1 | FUNCTION_, SbRtl_Unload },
+ arg(u"Dialog", SbxOBJECT),
+
+{ u"Val", SbxDOUBLE, 1 | FUNCTION_, SbRtl_Val },
+ arg(u"String", SbxSTRING),
+
+{ u"VarType", SbxINTEGER, 1 | FUNCTION_, SbRtl_VarType },
+ arg(u"Varname", SbxVARIANT),
+
+{ u"V_EMPTY", SbxINTEGER, CPROP_, SbRtl_V_EMPTY },
+{ u"V_NULL", SbxINTEGER, CPROP_, SbRtl_V_NULL },
+{ u"V_INTEGER", SbxINTEGER, CPROP_, SbRtl_V_INTEGER },
+{ u"V_LONG", SbxINTEGER, CPROP_, SbRtl_V_LONG },
+{ u"V_SINGLE", SbxINTEGER, CPROP_, SbRtl_V_SINGLE },
+{ u"V_DOUBLE", SbxINTEGER, CPROP_, SbRtl_V_DOUBLE },
+{ u"V_CURRENCY", SbxINTEGER, CPROP_, SbRtl_V_CURRENCY },
+{ u"V_DATE", SbxINTEGER, CPROP_, SbRtl_V_DATE },
+{ u"V_STRING", SbxINTEGER, CPROP_, SbRtl_V_STRING },
+
+{ u"Wait", SbxNULL, 1 | FUNCTION_, SbRtl_Wait },
+ arg(u"Milliseconds", SbxLONG),
+
+{ u"FuncCaller", SbxVARIANT, FUNCTION_, SbRtl_FuncCaller },
+//#i64882#
+{ u"WaitUntil", SbxNULL, 1 | FUNCTION_, SbRtl_WaitUntil },
+ arg(u"Date", SbxDOUBLE),
+
+{ u"Weekday", SbxINTEGER, 2 | FUNCTION_, SbRtl_Weekday },
+ arg(u"Date", SbxDATE),
+ arg(u"Firstdayofweek", SbxINTEGER, OPT_),
+
+{ u"WeekdayName", SbxSTRING, 3 | FUNCTION_ | COMPATONLY_, SbRtl_WeekdayName },
+ arg(u"Weekday", SbxINTEGER),
+ arg(u"Abbreviate", SbxBOOL, OPT_),
+ arg(u"Firstdayofweek", SbxINTEGER, OPT_),
+
+{ u"Year", SbxINTEGER, 1 | FUNCTION_, SbRtl_Year },
+ arg(u"Date", SbxDATE),
+
+{ {}, SbxNULL, -1, nullptr }}; // end of the table
+
+static_assert(MethodsTableValid(aMethods));
+
+SbiStdObject::SbiStdObject( const OUString& r, StarBASIC* pb ) : SbxObject( r )
+{
+ // #i92642: Remove default properties
+ Remove( "Name", SbxClassType::DontCare );
+ Remove( "Parent", SbxClassType::DontCare );
+
+ SetParent( pb );
+
+ pStdFactory.emplace();
+ SbxBase::AddFactory( &*pStdFactory );
+
+ Insert( new SbStdClipboard );
+}
+
+SbiStdObject::~SbiStdObject()
+{
+ SbxBase::RemoveFactory( &*pStdFactory );
+ 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 );
+ const Method* 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.equalsIgnoreAsciiCase(p->sName)))
+ {
+ 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->IsVBASupport();
+ }
+ 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;
+ SbxClassType eCT = SbxClassType::Object;
+ if( nType & PROPERTY_ )
+ {
+ eCT = SbxClassType::Property;
+ }
+ else if( nType & METHOD_ )
+ {
+ eCT = SbxClassType::Method;
+ }
+ pVar = Make(OUString(p->sName), 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_->Put(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;
+ const Method* p = &aMethods[ --nIdx ];
+ SbxInfo* pInfo_ = new SbxInfo;
+ short nPar = p->nArgs & ARGSMASK_;
+ for( short i = 0; i < nPar; i++ )
+ {
+ p++;
+ SbxFlagBits nFlags_ = static_cast<SbxFlagBits>(( p->nArgs >> 8 ) & 0x03);
+ if( p->nArgs & OPT_ )
+ {
+ nFlags_ |= SbxFlagBits::Optional;
+ }
+ pInfo_->AddParam(OUString(p->sName), 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 0000000000..c24c59af95
--- /dev/null
+++ b/basic/source/runtime/stdobj1.cxx
@@ -0,0 +1,427 @@
+/* -*- 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 <sbstdobj.hxx>
+
+namespace {
+ enum UserData
+ {
+ ATTR_IMP_TYPE = 1,
+ ATTR_IMP_WIDTH = 2,
+ ATTR_IMP_HEIGHT = 3,
+ ATTR_IMP_BOLD = 4,
+ ATTR_IMP_ITALIC = 5,
+ ATTR_IMP_STRIKETHROUGH = 6,
+ ATTR_IMP_UNDERLINE = 7,
+ ATTR_IMP_SIZE = 9,
+ ATTR_IMP_NAME = 10,
+ METH_CLEAR = 20,
+ METH_GETDATA = 21,
+ METH_GETFORMAT = 22,
+ METH_GETTEXT = 23,
+ METH_SETDATA = 24,
+ METH_SETTEXT = 25
+ };
+}
+
+SbStdFactory::SbStdFactory()
+{
+}
+
+SbxObjectRef 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;
+ default: break;
+ }
+
+ 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;
+ default: break;
+ }
+
+ SbxObject::Notify( rBC, rHint );
+}
+
+
+void SbStdClipboard::MethClear( SbxArray const * pPar_ )
+{
+ if (pPar_ && (pPar_->Count() > 1))
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS );
+ return;
+ }
+
+}
+
+void SbStdClipboard::MethGetData( SbxArray* pPar_ )
+{
+ if (!pPar_ || (pPar_->Count() != 2))
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS );
+ return;
+ }
+
+ sal_Int16 nFormat = pPar_->Get(1)->GetInteger();
+ if( nFormat <= 0 || nFormat > 3 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+}
+
+void SbStdClipboard::MethGetFormat( SbxVariable* pVar, SbxArray* pPar_ )
+{
+ if (!pPar_ || (pPar_->Count() != 2))
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS );
+ return;
+ }
+
+ sal_Int16 nFormat = pPar_->Get(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_->Count() > 1))
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS );
+ return;
+ }
+
+ pVar->PutString( OUString() );
+}
+
+void SbStdClipboard::MethSetData( SbxArray* pPar_ )
+{
+ if (!pPar_ || (pPar_->Count() != 3))
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_NUMBER_OF_ARGS );
+ return;
+ }
+
+ sal_Int16 nFormat = pPar_->Get(2)->GetInteger();
+ if( nFormat <= 0 || nFormat > 3 )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return;
+ }
+
+}
+
+void SbStdClipboard::MethSetText( SbxArray const * pPar_ )
+{
+ if (!pPar_ || (pPar_->Count() != 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;
+ default: break;
+ }
+
+ 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 0000000000..08bd70fc21
--- /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