diff options
Diffstat (limited to 'basic/source/runtime')
-rw-r--r-- | basic/source/runtime/basrdll.cxx | 132 | ||||
-rw-r--r-- | basic/source/runtime/comenumwrapper.cxx | 65 | ||||
-rw-r--r-- | basic/source/runtime/comenumwrapper.hxx | 45 | ||||
-rw-r--r-- | basic/source/runtime/ddectrl.cxx | 198 | ||||
-rw-r--r-- | basic/source/runtime/ddectrl.hxx | 54 | ||||
-rw-r--r-- | basic/source/runtime/dllmgr-none.cxx | 113 | ||||
-rw-r--r-- | basic/source/runtime/dllmgr-x64.cxx | 764 | ||||
-rw-r--r-- | basic/source/runtime/dllmgr-x86.cxx | 739 | ||||
-rw-r--r-- | basic/source/runtime/dllmgr.hxx | 54 | ||||
-rw-r--r-- | basic/source/runtime/inputbox.cxx | 142 | ||||
-rw-r--r-- | basic/source/runtime/iosys.cxx | 842 | ||||
-rw-r--r-- | basic/source/runtime/methods.cxx | 4745 | ||||
-rw-r--r-- | basic/source/runtime/methods1.cxx | 2992 | ||||
-rw-r--r-- | basic/source/runtime/props.cxx | 171 | ||||
-rw-r--r-- | basic/source/runtime/runtime.cxx | 4766 | ||||
-rw-r--r-- | basic/source/runtime/stdobj.cxx | 1097 | ||||
-rw-r--r-- | basic/source/runtime/stdobj1.cxx | 427 | ||||
-rw-r--r-- | basic/source/runtime/wnt-x86.asm | 47 |
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 |