summaryrefslogtreecommitdiffstats
path: root/basic/source/sbx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /basic/source/sbx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basic/source/sbx')
-rw-r--r--basic/source/sbx/sbxarray.cxx584
-rw-r--r--basic/source/sbx/sbxbase.cxx358
-rw-r--r--basic/source/sbx/sbxbool.cxx222
-rw-r--r--basic/source/sbx/sbxbyte.cxx313
-rw-r--r--basic/source/sbx/sbxchar.cxx301
-rw-r--r--basic/source/sbx/sbxcoll.cxx308
-rw-r--r--basic/source/sbx/sbxconv.hxx162
-rw-r--r--basic/source/sbx/sbxcurr.cxx484
-rw-r--r--basic/source/sbx/sbxdate.cxx408
-rw-r--r--basic/source/sbx/sbxdbl.cxx266
-rw-r--r--basic/source/sbx/sbxdec.cxx682
-rw-r--r--basic/source/sbx/sbxdec.hxx97
-rw-r--r--basic/source/sbx/sbxexec.cxx391
-rw-r--r--basic/source/sbx/sbxform.cxx1003
-rw-r--r--basic/source/sbx/sbxint.cxx822
-rw-r--r--basic/source/sbx/sbxlng.cxx292
-rw-r--r--basic/source/sbx/sbxobj.cxx904
-rw-r--r--basic/source/sbx/sbxres.cxx80
-rw-r--r--basic/source/sbx/sbxres.hxx51
-rw-r--r--basic/source/sbx/sbxscan.cxx745
-rw-r--r--basic/source/sbx/sbxsng.cxx280
-rw-r--r--basic/source/sbx/sbxstr.cxx331
-rw-r--r--basic/source/sbx/sbxuint.cxx291
-rw-r--r--basic/source/sbx/sbxulng.cxx267
-rw-r--r--basic/source/sbx/sbxvalue.cxx1569
-rw-r--r--basic/source/sbx/sbxvar.cxx604
26 files changed, 11815 insertions, 0 deletions
diff --git a/basic/source/sbx/sbxarray.cxx b/basic/source/sbx/sbxarray.cxx
new file mode 100644
index 0000000000..81ad5bf64a
--- /dev/null
+++ b/basic/source/sbx/sbxarray.cxx
@@ -0,0 +1,584 @@
+/* -*- 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 <o3tl/safeint.hxx>
+#include <tools/debug.hxx>
+#include <tools/stream.hxx>
+#include <basic/sbx.hxx>
+#include <runtime.hxx>
+
+#include <cstddef>
+#include <optional>
+#include <filefmt.hxx>
+
+struct SbxVarEntry
+{
+ SbxVariableRef mpVar;
+ std::optional<OUString> maAlias;
+};
+
+
+// SbxArray
+
+SbxArray::SbxArray( SbxDataType t )
+{
+ eType = t;
+ if( t != SbxVARIANT )
+ SetFlag( SbxFlagBits::Fixed );
+}
+
+SbxArray& SbxArray::operator=( const SbxArray& rArray )
+{
+ if( &rArray != this )
+ {
+ eType = rArray.eType;
+ Clear();
+ for( const auto& rpSrcRef : rArray.mVarEntries )
+ {
+ SbxVariableRef pSrc_ = rpSrcRef.mpVar;
+ if( !pSrc_.is() )
+ continue;
+
+ if( eType != SbxVARIANT )
+ {
+ // Convert no objects
+ if( eType != SbxOBJECT || pSrc_->GetClass() != SbxClassType::Object )
+ {
+ pSrc_->Convert(eType);
+ }
+ }
+ mVarEntries.push_back( rpSrcRef );
+ }
+ }
+ return *this;
+}
+
+SbxArray::~SbxArray()
+{
+}
+
+SbxDataType SbxArray::GetType() const
+{
+ return static_cast<SbxDataType>( eType | SbxARRAY );
+}
+
+void SbxArray::Clear()
+{
+ mVarEntries.clear();
+}
+
+sal_uInt32 SbxArray::Count() const
+{
+ return mVarEntries.size();
+}
+
+SbxVariableRef& SbxArray::GetRef( sal_uInt32 nIdx )
+{
+ // If necessary extend the array
+ DBG_ASSERT( nIdx <= SBX_MAXINDEX32, "SBX: Array-Index > SBX_MAXINDEX32" );
+ // Very Hot Fix
+ if( nIdx > SBX_MAXINDEX32 )
+ {
+ SetError( ERRCODE_BASIC_OUT_OF_RANGE );
+ nIdx = 0;
+ }
+ if ( mVarEntries.size() <= nIdx )
+ mVarEntries.resize(nIdx+1);
+
+ return mVarEntries[nIdx].mpVar;
+}
+
+SbxVariable* SbxArray::Get( sal_uInt32 nIdx )
+{
+ if( !CanRead() )
+ {
+ SetError( ERRCODE_BASIC_PROP_WRITEONLY );
+ return nullptr;
+ }
+ SbxVariableRef& rRef = GetRef( nIdx );
+
+ if ( !rRef.is() )
+ rRef = new SbxVariable( eType );
+
+ return rRef.get();
+}
+
+void SbxArray::Put( SbxVariable* pVar, sal_uInt32 nIdx )
+{
+ if( !CanWrite() )
+ SetError( ERRCODE_BASIC_PROP_READONLY );
+ else
+ {
+ if( pVar )
+ if( eType != SbxVARIANT )
+ // Convert no objects
+ if( eType != SbxOBJECT || pVar->GetClass() != SbxClassType::Object )
+ pVar->Convert( eType );
+ SbxVariableRef& rRef = GetRef( nIdx );
+ // tdf#122250. It is possible that I hold the last reference to myself, so check, otherwise I might
+ // call SetFlag on myself after I have died.
+ bool removingMyself = rRef && rRef->GetParameters() == this && GetRefCount() == 1;
+ if( rRef.get() != pVar )
+ {
+ rRef = pVar;
+ if (!removingMyself)
+ SetFlag( SbxFlagBits::Modified );
+ }
+ }
+}
+
+OUString SbxArray::GetAlias( sal_uInt32 nIdx )
+{
+ if( !CanRead() )
+ {
+ SetError( ERRCODE_BASIC_PROP_WRITEONLY );
+ return OUString();
+ }
+ SbxVarEntry& rRef = reinterpret_cast<SbxVarEntry&>(GetRef( nIdx ));
+
+ if (!rRef.maAlias)
+ return OUString();
+
+ return *rRef.maAlias;
+}
+
+void SbxArray::PutAlias( const OUString& rAlias, sal_uInt32 nIdx )
+{
+ if( !CanWrite() )
+ {
+ SetError( ERRCODE_BASIC_PROP_READONLY );
+ }
+ else
+ {
+ SbxVarEntry& rRef = reinterpret_cast<SbxVarEntry&>( GetRef( nIdx ) );
+ rRef.maAlias = rAlias;
+ }
+}
+
+void SbxArray::Insert( SbxVariable* pVar, sal_uInt32 nIdx )
+{
+ DBG_ASSERT( mVarEntries.size() <= SBX_MAXINDEX32, "SBX: Array gets too big" );
+ if( mVarEntries.size() > SBX_MAXINDEX32 )
+ {
+ return;
+ }
+ SbxVarEntry p;
+ p.mpVar = pVar;
+ size_t nSize = mVarEntries.size();
+ if( nIdx > nSize )
+ {
+ nIdx = nSize;
+ }
+ if( eType != SbxVARIANT && pVar )
+ {
+ p.mpVar->Convert(eType);
+ }
+ if( nIdx == nSize )
+ {
+ mVarEntries.push_back( p );
+ }
+ else
+ {
+ mVarEntries.insert( mVarEntries.begin() + nIdx, p );
+ }
+ SetFlag( SbxFlagBits::Modified );
+}
+
+void SbxArray::Remove( sal_uInt32 nIdx )
+{
+ if( nIdx < mVarEntries.size() )
+ {
+ mVarEntries.erase( mVarEntries.begin() + nIdx );
+ SetFlag( SbxFlagBits::Modified );
+ }
+}
+
+void SbxArray::Remove( SbxVariable const * pVar )
+{
+ if( pVar )
+ {
+ for( size_t i = 0; i < mVarEntries.size(); i++ )
+ {
+ if (mVarEntries[i].mpVar.get() == pVar)
+ {
+ Remove( i ); break;
+ }
+ }
+ }
+}
+
+// Taking over of the data from the passed array, at which
+// the variable of the same name will be overwritten.
+
+void SbxArray::Merge( SbxArray* p )
+{
+ if (!p)
+ return;
+
+ for (auto& rEntry1: p->mVarEntries)
+ {
+ if (!rEntry1.mpVar.is())
+ continue;
+
+ OUString aName = rEntry1.mpVar->GetName();
+ sal_uInt16 nHash = rEntry1.mpVar->GetHashCode();
+
+ // Is the element by the same name already inside?
+ // Then overwrite!
+ for (auto& rEntry2: mVarEntries)
+ {
+ if (!rEntry2.mpVar.is())
+ continue;
+
+ if (rEntry2.mpVar->GetHashCode() == nHash &&
+ rEntry2.mpVar->GetName().equalsIgnoreAsciiCase(aName))
+ {
+ // Take this element and clear the original.
+ rEntry2.mpVar = rEntry1.mpVar;
+ rEntry1.mpVar.clear();
+ break;
+ }
+ }
+
+ if (rEntry1.mpVar.is())
+ {
+ // We don't have element with the same name. Add a new entry.
+ SbxVarEntry aNewEntry;
+ aNewEntry.mpVar = rEntry1.mpVar;
+ if (rEntry1.maAlias)
+ aNewEntry.maAlias = *rEntry1.maAlias;
+ mVarEntries.push_back(aNewEntry);
+ }
+ }
+}
+
+// Search of an element by his name and type. If an element is an object,
+// it will also be scanned...
+
+SbxVariable* SbxArray::Find( const OUString& rName, SbxClassType t )
+{
+ SbxVariable* p = nullptr;
+ if( mVarEntries.empty() )
+ return nullptr;
+ bool bExtSearch = IsSet( SbxFlagBits::ExtSearch );
+ sal_uInt16 nHash = SbxVariable::MakeHashCode( rName );
+ const OUString aNameCI = SbxVariable::NameToCaseInsensitiveName(rName);
+ for (auto& rEntry : mVarEntries)
+ {
+ if (!rEntry.mpVar.is() || !rEntry.mpVar->IsVisible())
+ continue;
+
+ // The very secure search works as well, if there is no hashcode!
+ sal_uInt16 nVarHash = rEntry.mpVar->GetHashCode();
+ // tdf#148358 - compare the names case-insensitive
+ if ( (!nVarHash || nVarHash == nHash)
+ && (t == SbxClassType::DontCare || rEntry.mpVar->GetClass() == t)
+ && (rEntry.mpVar->GetName(SbxNameType::CaseInsensitive) == aNameCI))
+ {
+ p = rEntry.mpVar.get();
+ p->ResetFlag(SbxFlagBits::ExtFound);
+ break;
+ }
+
+ // Did we have an array/object with extended search?
+ if (bExtSearch && rEntry.mpVar->IsSet(SbxFlagBits::ExtSearch))
+ {
+ switch (rEntry.mpVar->GetClass())
+ {
+ case SbxClassType::Object:
+ {
+ // Objects are not allowed to scan their parent.
+ SbxFlagBits nOld = rEntry.mpVar->GetFlags();
+ rEntry.mpVar->ResetFlag(SbxFlagBits::GlobalSearch);
+ p = static_cast<SbxObject&>(*rEntry.mpVar).Find(rName, t);
+ rEntry.mpVar->SetFlags(nOld);
+ }
+ break;
+ case SbxClassType::Array:
+ // Casting SbxVariable to SbxArray? Really?
+ p = reinterpret_cast<SbxArray&>(*rEntry.mpVar).Find(rName, t);
+ break;
+ default:
+ ;
+ }
+
+ if (p)
+ {
+ p->SetFlag(SbxFlagBits::ExtFound);
+ break;
+ }
+ }
+ }
+ return p;
+}
+
+bool SbxArray::LoadData( SvStream& rStrm, sal_uInt16 /*nVer*/ )
+{
+ sal_uInt16 nElem;
+ Clear();
+ bool bRes = true;
+ SbxFlagBits f = nFlags;
+ nFlags |= SbxFlagBits::Write;
+ rStrm.ReadUInt16( nElem );
+ nElem &= 0x7FFF;
+ for( sal_uInt32 n = 0; n < nElem; n++ )
+ {
+ sal_uInt16 nIdx;
+ rStrm.ReadUInt16( nIdx );
+ SbxVariableRef pVar = static_cast<SbxVariable*>(Load( rStrm ).get());
+ if( pVar )
+ {
+ SbxVariableRef& rRef = GetRef( nIdx );
+ rRef = pVar;
+ }
+ else
+ {
+ bRes = false;
+ break;
+ }
+ }
+ nFlags = f;
+ return bRes;
+}
+
+std::pair<bool, sal_uInt32> SbxArray::StoreData( SvStream& rStrm ) const
+{
+ sal_uInt32 nElem = 0;
+ // Which elements are even defined?
+ for( auto& rEntry: mVarEntries )
+ {
+ if (rEntry.mpVar.is() && !(rEntry.mpVar->GetFlags() & SbxFlagBits::DontStore))
+ nElem++;
+ }
+ rStrm.WriteUInt16( nElem );
+
+ sal_uInt32 nVersion = B_IMG_VERSION_12;
+ for( size_t n = 0; n < mVarEntries.size(); n++ )
+ {
+ const SbxVarEntry& rEntry = mVarEntries[n];
+ if (rEntry.mpVar.is() && !(rEntry.mpVar->GetFlags() & SbxFlagBits::DontStore))
+ {
+ rStrm.WriteUInt16( n );
+ const auto& [bSuccess, nVersionModule] = rEntry.mpVar->Store(rStrm);
+ if (!bSuccess)
+ return { false, 0 };
+ else if (nVersionModule > nVersion)
+ {
+ nVersion = nVersionModule;
+ }
+ }
+ }
+ return { true, nVersion };
+}
+
+// #100883 Method to set method directly to parameter array
+void SbxArray::PutDirect( SbxVariable* pVar, sal_uInt32 nIdx )
+{
+ SbxVariableRef& rRef = GetRef( nIdx );
+ rRef = pVar;
+}
+
+
+// SbxArray
+
+SbxDimArray::SbxDimArray( SbxDataType t ) : SbxArray( t ), mbHasFixedSize( false )
+{
+}
+
+SbxDimArray& SbxDimArray::operator=( const SbxDimArray& rArray )
+{
+ if( &rArray != this )
+ {
+ SbxArray::operator=( static_cast<const SbxArray&>(rArray) );
+ m_vDimensions = rArray.m_vDimensions;
+ mbHasFixedSize = rArray.mbHasFixedSize;
+ }
+ return *this;
+}
+
+SbxDimArray::~SbxDimArray()
+{
+}
+
+void SbxDimArray::Clear()
+{
+ m_vDimensions.clear();
+ SbxArray::Clear();
+}
+
+// Add a dimension
+
+void SbxDimArray::AddDimImpl( sal_Int32 lb, sal_Int32 ub, bool bAllowSize0 )
+{
+ ErrCode eRes = ERRCODE_NONE;
+ if( ub < lb && !bAllowSize0 )
+ {
+ eRes = ERRCODE_BASIC_OUT_OF_RANGE;
+ ub = lb;
+ }
+ SbxDim d;
+ d.nLbound = lb;
+ d.nUbound = ub;
+ d.nSize = ub - lb + 1;
+ m_vDimensions.push_back(d);
+ if( eRes )
+ SetError( eRes );
+}
+
+void SbxDimArray::AddDim( sal_Int32 lb, sal_Int32 ub )
+{
+ AddDimImpl( lb, ub, false );
+}
+
+void SbxDimArray::unoAddDim( sal_Int32 lb, sal_Int32 ub )
+{
+ AddDimImpl( lb, ub, true );
+}
+
+
+// Readout dimension data
+
+bool SbxDimArray::GetDim( sal_Int32 n, sal_Int32& rlb, sal_Int32& rub ) const
+{
+ if( n < 1 || o3tl::make_unsigned(n) > m_vDimensions.size() )
+ {
+ SetError( ERRCODE_BASIC_OUT_OF_RANGE );
+ rub = rlb = 0;
+ return false;
+ }
+ SbxDim d = m_vDimensions[n - 1];
+ rub = d.nUbound;
+ rlb = d.nLbound;
+ return true;
+}
+
+// Element-Ptr with the help of an index list
+
+sal_uInt32 SbxDimArray::Offset( const sal_Int32* pIdx )
+{
+ sal_uInt32 nPos = 0;
+ for( const auto& rDimension : m_vDimensions )
+ {
+ sal_Int32 nIdx = *pIdx++;
+ if( nIdx < rDimension.nLbound || nIdx > rDimension.nUbound )
+ {
+ nPos = sal_uInt32(SBX_MAXINDEX32) + 1; break;
+ }
+ nPos = nPos * rDimension.nSize + nIdx - rDimension.nLbound;
+ }
+ if( m_vDimensions.empty() || nPos > SBX_MAXINDEX32 )
+ {
+ SetError( ERRCODE_BASIC_OUT_OF_RANGE );
+ nPos = 0;
+ }
+ return nPos;
+}
+
+SbxVariable* SbxDimArray::Get( const sal_Int32* pIdx )
+{
+ return SbxArray::Get( Offset( pIdx ) );
+}
+
+void SbxDimArray::Put( SbxVariable* p, const sal_Int32* pIdx )
+{
+ SbxArray::Put( p, Offset( pIdx ) );
+}
+
+// Element-Number with the help of Parameter-Array
+sal_uInt32 SbxDimArray::Offset( SbxArray* pPar )
+{
+#if HAVE_FEATURE_SCRIPTING
+ if (m_vDimensions.empty() || !pPar ||
+ ((m_vDimensions.size() != sal::static_int_cast<size_t>(pPar->Count() - 1))
+ && SbiRuntime::isVBAEnabled()))
+ {
+ SetError( ERRCODE_BASIC_OUT_OF_RANGE );
+ return 0;
+ }
+#endif
+ sal_uInt32 nPos = 0;
+ sal_uInt32 nOff = 1; // Non element 0!
+ for (auto const& vDimension : m_vDimensions)
+ {
+ sal_Int32 nIdx = pPar->Get( nOff++ )->GetLong();
+ if( nIdx < vDimension.nLbound || nIdx > vDimension.nUbound )
+ {
+ nPos = sal_uInt32(SBX_MAXINDEX32)+1;
+ break;
+ }
+ nPos = nPos * vDimension.nSize + nIdx - vDimension.nLbound;
+ if (IsError())
+ break;
+ }
+ if( nPos > o3tl::make_unsigned(SBX_MAXINDEX32) )
+ {
+ SetError( ERRCODE_BASIC_OUT_OF_RANGE );
+ nPos = 0;
+ }
+ return nPos;
+}
+
+SbxVariable* SbxDimArray::Get( SbxArray* pPar )
+{
+ return SbxArray::Get( Offset( pPar ) );
+}
+
+bool SbxDimArray::LoadData( SvStream& rStrm, sal_uInt16 nVer )
+{
+ short nTmp(0);
+ rStrm.ReadInt16(nTmp);
+
+ if (nTmp > 0)
+ {
+ auto nDimension = o3tl::make_unsigned(nTmp);
+
+ const size_t nMinRecordSize = 4;
+ const size_t nMaxPossibleRecords = rStrm.remainingSize() / nMinRecordSize;
+ if (nDimension > nMaxPossibleRecords)
+ {
+ SAL_WARN("basic", "SbxDimArray::LoadData more entries claimed than stream could contain");
+ return false;
+ }
+
+ for (decltype(nDimension) i = 0; i < nDimension && rStrm.GetError() == ERRCODE_NONE; ++i)
+ {
+ sal_Int16 lb(0), ub(0);
+ rStrm.ReadInt16( lb ).ReadInt16( ub );
+ AddDim( lb, ub );
+ }
+ }
+ return SbxArray::LoadData( rStrm, nVer );
+}
+
+std::pair<bool, sal_uInt32> SbxDimArray::StoreData( SvStream& rStrm ) const
+{
+ assert(m_vDimensions.size() <= sal::static_int_cast<size_t>(std::numeric_limits<sal_Int16>::max()));
+ rStrm.WriteInt16( m_vDimensions.size() );
+ for( std::size_t i = 1; i <= m_vDimensions.size(); i++ )
+ {
+ sal_Int32 lb32, ub32;
+ GetDim(i, lb32, ub32);
+ assert(lb32 >= -SBX_MAXINDEX && ub32 <= SBX_MAXINDEX);
+ rStrm.WriteInt16(lb32).WriteInt16(ub32);
+ }
+ return SbxArray::StoreData( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxbase.cxx b/basic/source/sbx/sbxbase.cxx
new file mode 100644
index 0000000000..3189977a6b
--- /dev/null
+++ b/basic/source/sbx/sbxbase.cxx
@@ -0,0 +1,358 @@
+/* -*- 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 <tools/debug.hxx>
+#include <tools/stream.hxx>
+#include <vcl/svapp.hxx>
+
+#include <basic/sbx.hxx>
+#include <sbxfac.hxx>
+#include <sbxform.hxx>
+#include <basic/sbxmeth.hxx>
+#include <sbxprop.hxx>
+#include <sbxbase.hxx>
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <filefmt.hxx>
+
+// AppData-Structure for SBX:
+
+
+SbxAppData::SbxAppData()
+ : eErrCode(ERRCODE_NONE)
+ , aErrorMsg(OUString())
+ , eBasicFormaterLangType(LANGUAGE_DONTKNOW)
+{
+}
+
+SbxAppData::~SbxAppData()
+{
+ SolarMutexGuard g;
+
+ pBasicFormater.reset();
+ // basic manager repository must be destroyed before factories
+ mrImplRepository.clear();
+}
+
+SbxBase::SbxBase()
+{
+ nFlags = SbxFlagBits::ReadWrite;
+}
+
+SbxBase::SbxBase( const SbxBase& r )
+ : SvRefBase( r )
+{
+ nFlags = r.nFlags;
+}
+
+SbxBase::~SbxBase()
+{
+}
+
+SbxBase& SbxBase::operator=( const SbxBase& r )
+{
+ nFlags = r.nFlags;
+ return *this;
+}
+
+SbxDataType SbxBase::GetType() const
+{
+ return SbxEMPTY;
+}
+
+bool SbxBase::IsFixed() const
+{
+ return IsSet( SbxFlagBits::Fixed );
+}
+
+void SbxBase::SetModified( bool b )
+{
+ if( IsSet( SbxFlagBits::NoModify ) )
+ return;
+ if( b )
+ SetFlag( SbxFlagBits::Modified );
+ else
+ ResetFlag( SbxFlagBits::Modified );
+}
+
+ErrCode const & SbxBase::GetError()
+{
+ return GetSbxData_Impl().eErrCode;
+}
+
+OUString const & SbxBase::GetErrorMsg()
+{
+ return GetSbxData_Impl().aErrorMsg;
+}
+
+void SbxBase::SetError(ErrCode e, const OUString& rMsg)
+{
+ SbxAppData& r = GetSbxData_Impl();
+ if (e && r.eErrCode == ERRCODE_NONE)
+ {
+ r.eErrCode = e;
+ r.aErrorMsg = rMsg;
+ }
+}
+
+void SbxBase::SetError( ErrCode e )
+{
+ SbxAppData& r = GetSbxData_Impl();
+ if( e && r.eErrCode == ERRCODE_NONE )
+ r.eErrCode = e;
+}
+
+bool SbxBase::IsError()
+{
+ return GetSbxData_Impl().eErrCode != ERRCODE_NONE;
+}
+
+void SbxBase::ResetError()
+{
+ GetSbxData_Impl().eErrCode = ERRCODE_NONE;
+ GetSbxData_Impl().aErrorMsg = OUString();
+}
+
+void SbxBase::AddFactory( SbxFactory* pFac )
+{
+ GetSbxData_Impl().m_Factories.emplace_back(pFac);
+}
+
+void SbxBase::RemoveFactory( SbxFactory const * pFac )
+{
+ if (!IsSbxData_Impl())
+ return;
+ SbxAppData& r = GetSbxData_Impl();
+ auto it = std::find(r.m_Factories.begin(), r.m_Factories.end(), pFac);
+ if (it != r.m_Factories.end())
+ r.m_Factories.erase( it );
+}
+
+
+SbxBaseRef SbxBase::Create( sal_uInt16 nSbxId, sal_uInt32 nCreator )
+{
+ // #91626: Hack to skip old Basic dialogs
+ // Problem: There does not exist a factory any more,
+ // so we have to create a dummy SbxVariable instead
+ if( nSbxId == 0x65 ) // Dialog Id
+ return new SbxVariable;
+
+ if( nCreator == SBXCR_SBX )
+ switch( nSbxId )
+ {
+ case SBXID_VALUE: return new SbxValue;
+ case SBXID_VARIABLE: return new SbxVariable;
+ case SBXID_ARRAY: return new SbxArray;
+ case SBXID_DIMARRAY: return new SbxDimArray;
+ case SBXID_OBJECT: return new SbxObject( "" );
+ case SBXID_COLLECTION: return new SbxCollection;
+ case SBXID_FIXCOLLECTION:
+ return new SbxStdCollection;
+ case SBXID_METHOD: return new SbxMethod( "", SbxEMPTY );
+ case SBXID_PROPERTY: return new SbxProperty( "", SbxEMPTY );
+ }
+ // Unknown type: go over the factories!
+ SbxAppData& r = GetSbxData_Impl();
+ SbxBaseRef pNew;
+ for (auto const& rpFac : r.m_Factories)
+ {
+ pNew = rpFac->Create( nSbxId, nCreator );
+ if( pNew )
+ break;
+ }
+ SAL_WARN_IF(!pNew, "basic", "No factory for SBX ID " << nSbxId);
+ return pNew;
+}
+
+SbxObjectRef SbxBase::CreateObject( const OUString& rClass )
+{
+ SbxAppData& r = GetSbxData_Impl();
+ SbxObjectRef pNew;
+ for (auto const& rpFac : r.m_Factories)
+ {
+ pNew = rpFac->CreateObject( rClass );
+ if( pNew )
+ break;
+ }
+ SAL_WARN_IF(!pNew, "basic", "No factory for object class " << rClass);
+ return pNew;
+}
+
+namespace {
+
+// coverity[ -taint_source ]
+[[nodiscard]] SbxFlagBits CorrectFlags(SbxFlagBits nFlags)
+{
+ // Correcting a foolishness of mine:
+ if (nFlags & SbxFlagBits::Reserved)
+ nFlags |= SbxFlagBits::GlobalSearch;
+ return nFlags & ~SbxFlagBits::Reserved;
+}
+
+}
+
+SbxBaseRef SbxBase::Load( SvStream& rStrm )
+{
+ sal_uInt16 nSbxId(0), nFlagsTmp(0), nVer(0);
+ sal_uInt32 nCreator(0), nSize(0);
+ rStrm.ReadUInt32( nCreator ).ReadUInt16( nSbxId ).ReadUInt16( nFlagsTmp ).ReadUInt16( nVer );
+ SbxFlagBits nFlags = CorrectFlags(static_cast<SbxFlagBits>(nFlagsTmp));
+
+ sal_uInt64 nOldPos = rStrm.Tell();
+ rStrm.ReadUInt32( nSize );
+ SbxBaseRef p = Create( nSbxId, nCreator );
+ if( p )
+ {
+ p->nFlags = nFlags;
+ if( p->LoadData( rStrm, nVer ) )
+ {
+ sal_uInt64 const nNewPos = rStrm.Tell();
+ nOldPos += nSize;
+ DBG_ASSERT( nOldPos >= nNewPos, "SBX: Too much data loaded" );
+ if( nOldPos != nNewPos )
+ rStrm.Seek( nOldPos );
+ if( !p->LoadCompleted() )
+ {
+ // Deleting of the object
+ SbxBaseRef xDeleteRef( p );
+ p = nullptr;
+ }
+ }
+ else
+ {
+ rStrm.SetError( SVSTREAM_FILEFORMAT_ERROR );
+ // Deleting of the object
+ SbxBaseRef xDeleteRef( p );
+ p = nullptr;
+ }
+ }
+ else
+ rStrm.SetError( SVSTREAM_FILEFORMAT_ERROR );
+ return p;
+}
+
+std::pair<bool, sal_uInt32> SbxBase::Store( SvStream& rStrm )
+{
+ if( ( nFlags & SbxFlagBits::DontStore ) == SbxFlagBits::NONE )
+ {
+ rStrm.WriteUInt32( SBXCR_SBX )
+ .WriteUInt16( GetSbxId() )
+ .WriteUInt16( static_cast<sal_uInt16>(GetFlags()) )
+ .WriteUInt16( GetVersion() );
+ sal_uInt64 const nOldPos = rStrm.Tell();
+ rStrm.WriteUInt32( 0 );
+ auto [bRes, nVersion] = StoreData(rStrm);
+ sal_uInt64 const nNewPos = rStrm.Tell();
+ rStrm.Seek( nOldPos );
+ rStrm.WriteUInt32( nNewPos - nOldPos );
+ rStrm.Seek( nNewPos );
+ if( rStrm.GetError() != ERRCODE_NONE )
+ bRes = false;
+ if( bRes )
+ bRes = true;
+ return { bRes, nVersion };
+ }
+ else
+ return { true, B_IMG_VERSION_12 };
+}
+
+bool SbxBase::LoadCompleted()
+{
+ return true;
+}
+
+//////////////////////////////// SbxFactory
+
+SbxFactory::~SbxFactory()
+{
+}
+
+SbxBaseRef SbxFactory::Create( sal_uInt16, sal_uInt32 )
+{
+ return nullptr;
+}
+
+SbxObjectRef SbxFactory::CreateObject( const OUString& )
+{
+ return nullptr;
+}
+
+///////////////////////////////// SbxInfo
+
+SbxInfo::~SbxInfo()
+{}
+
+void SbxInfo::AddParam(const OUString& rName, SbxDataType eType, SbxFlagBits nFlags)
+{
+ m_Params.push_back(std::make_unique<SbxParamInfo>(rName, eType, nFlags));
+}
+
+const SbxParamInfo* SbxInfo::GetParam( sal_uInt16 n ) const
+{
+ if (n < 1 || n > m_Params.size())
+ return nullptr;
+ else
+ return m_Params[n - 1].get();
+}
+
+void SbxInfo::LoadData( SvStream& rStrm, sal_uInt16 nVer )
+{
+ m_Params.clear();
+ sal_uInt16 nParam;
+ aComment = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
+ RTL_TEXTENCODING_ASCII_US);
+ aHelpFile = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
+ RTL_TEXTENCODING_ASCII_US);
+ rStrm.ReadUInt32( nHelpId ).ReadUInt16( nParam );
+ while( nParam-- )
+ {
+ sal_uInt16 nType(0), nFlagsTmp(0);
+ sal_uInt32 nUserData = 0;
+ OUString aName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
+ RTL_TEXTENCODING_ASCII_US);
+ rStrm.ReadUInt16( nType ).ReadUInt16( nFlagsTmp );
+ SbxFlagBits nFlags = static_cast<SbxFlagBits>(nFlagsTmp);
+ if( nVer > 1 )
+ rStrm.ReadUInt32( nUserData );
+ AddParam( aName, static_cast<SbxDataType>(nType), nFlags );
+ SbxParamInfo& p(*m_Params.back());
+ p.nUserData = nUserData;
+ }
+}
+
+void SbxInfo::StoreData( SvStream& rStrm ) const
+{
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, aComment,
+ RTL_TEXTENCODING_ASCII_US );
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, aHelpFile,
+ RTL_TEXTENCODING_ASCII_US);
+ rStrm.WriteUInt32( nHelpId ).WriteUInt16( m_Params.size() );
+ for (auto const& i : m_Params)
+ {
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, i->aName,
+ RTL_TEXTENCODING_ASCII_US);
+ rStrm.WriteUInt16( i->eType )
+ .WriteUInt16( static_cast<sal_uInt16>(i->nFlags) )
+ .WriteUInt32( i->nUserData );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxbool.cxx b/basic/source/sbx/sbxbool.cxx
new file mode 100644
index 0000000000..2f5f031329
--- /dev/null
+++ b/basic/source/sbx/sbxbool.cxx
@@ -0,0 +1,222 @@
+/* -*- 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 <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include "sbxres.hxx"
+#include <rtlproto.hxx>
+
+enum SbxBOOL ImpGetBool( const SbxValues* p )
+{
+ enum SbxBOOL nRes;
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = SbxFALSE; break;
+ case SbxCHAR:
+ nRes = p->nChar ? SbxTRUE : SbxFALSE; break;
+ case SbxBYTE:
+ nRes = p->nByte ? SbxTRUE : SbxFALSE; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ nRes = p->nInteger ? SbxTRUE : SbxFALSE; break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = p->nUShort ? SbxTRUE : SbxFALSE; break;
+ case SbxLONG:
+ nRes = p->nLong ? SbxTRUE : SbxFALSE; break;
+ case SbxULONG:
+ nRes = p->nULong ? SbxTRUE : SbxFALSE; break;
+ case SbxSINGLE:
+ nRes = p->nSingle ? SbxTRUE : SbxFALSE; break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ nRes = p->nDouble ? SbxTRUE : SbxFALSE; break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ double dVal = 0.0;
+ if( p->pDecimal )
+ p->pDecimal->getDouble( dVal );
+ nRes = dVal ? SbxTRUE : SbxFALSE;
+ }
+ break;
+ case SbxSALINT64:
+ case SbxCURRENCY:
+ nRes = p->nInt64 ? SbxTRUE : SbxFALSE; break;
+ case SbxSALUINT64:
+ nRes = p->uInt64 ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ nRes = SbxFALSE;
+ if ( p->pOUString )
+ {
+ if( p->pOUString->equalsIgnoreAsciiCase( GetSbxRes( StringId::True ) ) )
+ nRes = SbxTRUE;
+ else if( !p->pOUString->equalsIgnoreAsciiCase( GetSbxRes( StringId::False ) ) )
+ {
+ // it can be convertible to a number
+ bool bError = true;
+ double n;
+ SbxDataType t;
+ sal_uInt16 nLen = 0;
+ if( ImpScan( *p->pOUString, n, t, &nLen, !LibreOffice6FloatingPointMode() ) == ERRCODE_NONE )
+ {
+ if( nLen == p->pOUString->getLength() )
+ {
+ bError = false;
+ if( n != 0.0 )
+ nRes = SbxTRUE;
+ }
+ }
+ if( bError )
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetBool() ? SbxTRUE : SbxFALSE;
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = SbxFALSE;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ nRes = *p->pInteger ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxLONG:
+ nRes = *p->pLong ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxULONG:
+ nRes = *p->pULong ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ nRes = *p->pUShort ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxSINGLE:
+ nRes = ( *p->pSingle != 0 ) ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ nRes = ( *p->pDouble != 0 ) ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ nRes = ( *p->pnInt64 ) ? SbxTRUE : SbxFALSE; break;
+ case SbxBYREF | SbxSALUINT64:
+ nRes = ( *p->puInt64 ) ? SbxTRUE : SbxFALSE; break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = SbxFALSE;
+ }
+ return nRes;
+}
+
+void ImpPutBool( SbxValues* p, sal_Int16 n )
+{
+ if( n )
+ n = SbxTRUE;
+ switch( +p->eType )
+ {
+ case SbxCHAR:
+ p->nChar = static_cast<sal_Unicode>(n); break;
+ case SbxUINT:
+ p->nByte = static_cast<sal_uInt8>(n); break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ p->nInteger = n; break;
+ case SbxLONG:
+ p->nLong = n; break;
+ case SbxULONG:
+ p->nULong = static_cast<sal_uInt32>(n); break;
+ case SbxERROR:
+ case SbxUSHORT:
+ p->nUShort = static_cast<sal_uInt16>(n); break;
+ case SbxSINGLE:
+ p->nSingle = n; break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ p->nInt64 = static_cast<sal_Int64>(n); break;
+ case SbxSALUINT64:
+ p->uInt64 = static_cast<sal_uInt64>(n); break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ ImpCreateDecimal( p )->setInt( n );
+ break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if ( !p->pOUString )
+ p->pOUString = new OUString( GetSbxRes( n ? StringId::True : StringId::False ) );
+ else
+ *p->pOUString = GetSbxRes( n ? StringId::True : StringId::False );
+ break;
+
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutBool( n != 0 );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ *p->pChar = static_cast<sal_Unicode>(n); break;
+ case SbxBYREF | SbxBYTE:
+ *p->pByte = static_cast<sal_uInt8>(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ *p->pInteger = n; break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ *p->pUShort = static_cast<sal_uInt16>(n); break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = n; break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = static_cast<sal_uInt32>(n); break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = n; break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = n; break;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = static_cast<sal_Int64>(n); break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = static_cast<sal_uInt64>(n); break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxbyte.cxx b/basic/source/sbx/sbxbyte.cxx
new file mode 100644
index 0000000000..071fcd6338
--- /dev/null
+++ b/basic/source/sbx/sbxbyte.cxx
@@ -0,0 +1,313 @@
+/* -*- 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>
+
+#include <o3tl/safeint.hxx>
+#include <comphelper/errcode.hxx>
+//#include <basic/sbx.hxx>
+#include <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+
+#include <rtl/math.hxx>
+
+sal_uInt8 ImpGetByte( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_uInt8 nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ if( p->nChar > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(p->nChar);
+ break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ if( p->nInteger > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else if( p->nInteger < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(p->nInteger);
+ break;
+ case SbxERROR:
+ case SbxUSHORT:
+ if( p->nUShort > o3tl::make_unsigned(SbxMAXBYTE) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(p->nUShort);
+ break;
+ case SbxLONG:
+ if( p->nLong > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else if( p->nLong < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(p->nLong);
+ break;
+ case SbxULONG:
+ if( p->nULong > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(p->nULong);
+ break;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ {
+ sal_Int64 val = p->nInt64;
+ if ( p->eType == SbxCURRENCY )
+ val = val / CURRENCY_FACTOR;
+ if( val > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else if( p->nInt64 < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(val);
+ break;
+ }
+ case SbxSALUINT64:
+ if( p->uInt64 > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(p->uInt64);
+ break;
+ case SbxSINGLE:
+ if( p->nSingle > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else if( p->nSingle < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(rtl::math::round( p->nSingle ));
+ break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ double dVal;
+ if( p->eType == SbxDECIMAL )
+ {
+ dVal = 0.0;
+ if( p->pDecimal )
+ p->pDecimal->getDouble( dVal );
+ }
+ else
+ dVal = p->nDouble;
+
+ if( dVal > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else if( dVal < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(rtl::math::round( dVal ));
+ break;
+ }
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ nRes = 0;
+ else
+ {
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else if( d > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
+ }
+ else if( d < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt8>( d + 0.5 );
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetByte();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxBYTE:
+ nRes = p->nByte; break;
+
+ // from here on will be tested
+ case SbxBYREF | SbxCHAR:
+ aTmp.nChar = *p->pChar; goto ref;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ aTmp.nInteger = *p->pInteger; goto ref;
+ case SbxBYREF | SbxLONG:
+ aTmp.nLong = *p->pLong; goto ref;
+ case SbxBYREF | SbxULONG:
+ aTmp.nULong = *p->pULong; goto ref;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ aTmp.nUShort = *p->pUShort; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutByte( SbxValues* p, sal_uInt8 n )
+{
+ switch( +p->eType )
+ {
+ case SbxBYTE:
+ p->nByte = n; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ p->nInteger = n; break;
+ case SbxERROR:
+ case SbxUSHORT:
+ p->nUShort = n; break;
+ case SbxLONG:
+ p->nLong = n; break;
+ case SbxULONG:
+ p->nULong = n; break;
+ case SbxSINGLE:
+ p->nSingle = n; break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+ case SbxCURRENCY:
+ p->nInt64 = n * CURRENCY_FACTOR; break;
+ case SbxSALINT64:
+ p->nInt64 = n; break;
+ case SbxSALUINT64:
+ p->uInt64 = n; break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ ImpCreateDecimal( p )->setByte( n );
+ break;
+
+ case SbxCHAR:
+ p->nChar = static_cast<sal_Unicode>(n); break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ ImpCvtNum( static_cast<double>(n), 0, *p->pOUString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutByte( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ *p->pChar = static_cast<sal_Unicode>(n); break;
+ case SbxBYREF | SbxBYTE:
+ *p->pByte = n; break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ *p->pInteger = n; break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ *p->pUShort = n; break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = n; break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = n; break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = n; break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = n; break;
+ case SbxBYREF | SbxCURRENCY:
+ p->nInt64 = n * CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = n; break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = n; break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxchar.cxx b/basic/source/sbx/sbxchar.cxx
new file mode 100644
index 0000000000..f3a6715a29
--- /dev/null
+++ b/basic/source/sbx/sbxchar.cxx
@@ -0,0 +1,301 @@
+/* -*- 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 <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+
+#include <rtl/math.hxx>
+
+sal_Unicode ImpGetChar( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_Unicode nRes = 0;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar; break;
+ case SbxBYTE:
+ nRes = static_cast<sal_Unicode>(p->nByte);
+ break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ if( p->nInteger < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMINCHAR;
+ }
+ else
+ nRes = static_cast<sal_Unicode>(p->nInteger);
+ break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = static_cast<sal_Unicode>(p->nUShort);
+ break;
+ case SbxLONG:
+ if( p->nLong > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXCHAR;
+ }
+ else if( p->nLong < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMINCHAR;
+ }
+ else
+ nRes = static_cast<sal_Unicode>(p->nLong);
+ break;
+ case SbxULONG:
+ if( p->nULong > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXCHAR;
+ }
+ else
+ nRes = static_cast<sal_Unicode>(p->nULong);
+ break;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ {
+ sal_Int64 val = p->nInt64;
+
+ if ( p->eType == SbxCURRENCY )
+ val = val / CURRENCY_FACTOR;
+
+ if( val > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXCHAR;
+ }
+ else if( p->nInt64 < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMINCHAR;
+ }
+ else
+ nRes = static_cast<sal_Unicode>(val);
+ break;
+ }
+ case SbxSALUINT64:
+ if( p->uInt64 > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXCHAR;
+ }
+ else
+ nRes = static_cast<sal_Unicode>(p->uInt64);
+ break;
+ case SbxSINGLE:
+ if( p->nSingle > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXCHAR;
+ }
+ else if( p->nSingle < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMINCHAR;
+ }
+ else
+ nRes = static_cast<sal_Unicode>(rtl::math::round( p->nSingle ));
+ break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ double dVal;
+ if( p->eType == SbxDECIMAL )
+ {
+ dVal = 0.0;
+ if( p->pDecimal )
+ p->pDecimal->getDouble( dVal );
+ }
+ else
+ dVal = p->nDouble;
+
+ if( dVal > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXCHAR;
+ }
+ else if( dVal < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMINCHAR;
+ }
+ else
+ nRes = static_cast<sal_uInt8>(rtl::math::round( dVal ));
+ break;
+ }
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if ( p->pOUString )
+ {
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else if( d > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXCHAR;
+ }
+ else if( d < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMINCHAR;
+ }
+ else
+ nRes = static_cast<sal_Unicode>(rtl::math::round( d ));
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetChar();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar; break;
+ // from here on will be tested
+ case SbxBYREF | SbxBYTE:
+ aTmp.nByte = *p->pByte; goto ref;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ aTmp.nInteger = *p->pInteger; goto ref;
+ case SbxBYREF | SbxLONG:
+ aTmp.nLong = *p->pLong; goto ref;
+ case SbxBYREF | SbxULONG:
+ aTmp.nULong = *p->pULong; goto ref;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ aTmp.nUShort = *p->pUShort; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutChar( SbxValues* p, sal_Unicode n )
+{
+ SbxValues aTmp;
+start:
+ switch( +p->eType )
+ {
+ case SbxCHAR:
+ p->nChar = n; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ p->nInteger = n; break;
+ case SbxLONG:
+ p->nLong = n; break;
+ case SbxSINGLE:
+ p->nSingle = n; break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+ case SbxCURRENCY:
+ p->nInt64 = n * CURRENCY_FACTOR; break;
+ case SbxSALINT64:
+ p->nInt64 = n; break;
+ case SbxSALUINT64:
+ p->uInt64 = n; break;
+ case SbxBYREF | SbxDECIMAL:
+ ImpCreateDecimal( p )->setChar( n );
+ break;
+
+ // from here on will be tested
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if ( !p->pOUString )
+ p->pOUString = new OUString( n );
+ else
+ *p->pOUString = OUString( n );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutChar( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ *p->pChar = n; break;
+ case SbxBYREF | SbxBYTE:
+ *p->pByte = static_cast<sal_uInt8>(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ *p->pInteger = n; break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ *p->pUShort = static_cast<sal_uInt16>(n); break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = static_cast<sal_Int32>(n); break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = static_cast<sal_uInt32>(n); break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = static_cast<float>(n); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = static_cast<double>(n); break;
+ case SbxBYREF | SbxCURRENCY:
+ p->nInt64 = n * CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = n; break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = n; break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxcoll.cxx b/basic/source/sbx/sbxcoll.cxx
new file mode 100644
index 0000000000..a29e832fa2
--- /dev/null
+++ b/basic/source/sbx/sbxcoll.cxx
@@ -0,0 +1,308 @@
+/* -*- 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 <o3tl/safeint.hxx>
+#include <tools/stream.hxx>
+
+#include <basic/sbx.hxx>
+#include <basic/sberrors.hxx>
+#include "sbxres.hxx"
+
+
+static OUString pCount;
+static OUString pAdd;
+static OUString pItem;
+static OUString pRemove;
+static sal_uInt16 nCountHash = 0, nAddHash, nItemHash, nRemoveHash;
+
+
+SbxCollection::SbxCollection()
+ : SbxObject( "" )
+{
+ if( !nCountHash )
+ {
+ pCount = GetSbxRes( StringId::CountProp );
+ pAdd = GetSbxRes( StringId::AddMeth );
+ pItem = GetSbxRes( StringId::ItemMeth );
+ pRemove = GetSbxRes( StringId::RemoveMeth );
+ nCountHash = MakeHashCode( pCount );
+ nAddHash = MakeHashCode( pAdd );
+ nItemHash = MakeHashCode( pItem );
+ nRemoveHash = MakeHashCode( pRemove );
+ }
+ Initialize();
+ // For Access on itself
+ StartListening(GetBroadcaster(), DuplicateHandling::Prevent);
+}
+
+SbxCollection::SbxCollection( const SbxCollection& rColl )
+ : SvRefBase( rColl ), SbxObject( rColl )
+{}
+
+SbxCollection& SbxCollection::operator=( const SbxCollection& r )
+{
+ if( &r != this )
+ SbxObject::operator=( r );
+ return *this;
+}
+
+SbxCollection::~SbxCollection()
+{}
+
+void SbxCollection::Clear()
+{
+ SbxObject::Clear();
+ Initialize();
+}
+
+void SbxCollection::Initialize()
+{
+ SetType( SbxOBJECT );
+ SetFlag( SbxFlagBits::Fixed );
+ ResetFlag( SbxFlagBits::Write );
+ SbxVariable* p;
+ p = Make( pCount , SbxClassType::Property, SbxINTEGER );
+ p->ResetFlag( SbxFlagBits::Write );
+ p->SetFlag( SbxFlagBits::DontStore );
+ p = Make( pAdd, SbxClassType::Method, SbxEMPTY );
+ p->SetFlag( SbxFlagBits::DontStore );
+ p = Make( pItem , SbxClassType::Method, SbxOBJECT );
+ p->SetFlag( SbxFlagBits::DontStore );
+ p = Make( pRemove, SbxClassType::Method, SbxEMPTY );
+ p->SetFlag( SbxFlagBits::DontStore );
+}
+
+SbxVariable* SbxCollection::Find( const OUString& rName, SbxClassType t )
+{
+ if( GetParameters() )
+ {
+ SbxObject* pObj = static_cast<SbxObject*>(GetObject());
+ return pObj ? pObj->Find( rName, t ) : nullptr;
+ }
+ else
+ {
+ return SbxObject::Find( rName, t );
+ }
+}
+
+void SbxCollection::Notify( SfxBroadcaster& rCst, const SfxHint& rHint )
+{
+ const SbxHint* p = dynamic_cast<const SbxHint*>(&rHint);
+ if( p )
+ {
+ const SfxHintId nId = p->GetId();
+ bool bRead = ( nId == SfxHintId::BasicDataWanted );
+ bool bWrite = ( nId == SfxHintId::BasicDataChanged );
+ SbxVariable* pVar = p->GetVar();
+ SbxArray* pArg = pVar->GetParameters();
+ if( bRead || bWrite )
+ {
+ OUString aVarName( pVar->GetName() );
+ if( pVar == this )
+ {
+ CollItem( pArg );
+ }
+ else if( pVar->GetHashCode() == nCountHash
+ && aVarName.equalsIgnoreAsciiCase( pCount ) )
+ {
+ pVar->PutLong(sal::static_int_cast<sal_Int32>(pObjs->Count()));
+ }
+ else if( pVar->GetHashCode() == nAddHash
+ && aVarName.equalsIgnoreAsciiCase( pAdd ) )
+ {
+ CollAdd( pArg );
+ }
+ else if( pVar->GetHashCode() == nItemHash
+ && aVarName.equalsIgnoreAsciiCase( pItem ) )
+ {
+ CollItem( pArg );
+ }
+ else if( pVar->GetHashCode() == nRemoveHash
+ && aVarName.equalsIgnoreAsciiCase( pRemove ) )
+ {
+ CollRemove( pArg );
+ }
+ else
+ {
+ SbxObject::Notify( rCst, rHint );
+ }
+ return;
+ }
+ }
+ SbxObject::Notify( rCst, rHint );
+}
+
+// Default: argument is object
+
+void SbxCollection::CollAdd( SbxArray* pPar_ )
+{
+ if (pPar_->Count() != 2)
+ {
+ SetError( ERRCODE_BASIC_WRONG_ARGS );
+ }
+ else
+ {
+ SbxBase* pObj = pPar_->Get(1)->GetObject();
+ if( !pObj || dynamic_cast<const SbxObject*>(pObj) == nullptr )
+ {
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ else
+ {
+ Insert( static_cast<SbxObject*>(pObj) );
+ }
+ }
+}
+
+// Default: index from 1 or object name
+
+void SbxCollection::CollItem( SbxArray* pPar_ )
+{
+ if (pPar_->Count() != 2)
+ {
+ SetError( ERRCODE_BASIC_WRONG_ARGS );
+ }
+ else
+ {
+ SbxVariable* pRes = nullptr;
+ SbxVariable* p = pPar_->Get(1);
+ if( p->GetType() == SbxSTRING )
+ {
+ pRes = Find( p->GetOUString(), SbxClassType::Object );
+ }
+ else
+ {
+ short n = p->GetInteger();
+ if (n >= 1 && o3tl::make_unsigned(n) <= pObjs->Count())
+ {
+ pRes = pObjs->Get(static_cast<sal_uInt32>(n) - 1);
+ }
+ }
+ if( !pRes )
+ {
+ SetError( ERRCODE_BASIC_BAD_INDEX );
+ }
+ pPar_->Get(0)->PutObject(pRes);
+ }
+}
+
+// Default: index from 1
+
+void SbxCollection::CollRemove( SbxArray* pPar_ )
+{
+ if (pPar_->Count() != 2)
+ SetError( ERRCODE_BASIC_WRONG_ARGS );
+ else
+ {
+ short n = pPar_->Get(1)->GetInteger();
+ if (n < 1 || o3tl::make_unsigned(n) > pObjs->Count())
+ SetError( ERRCODE_BASIC_BAD_INDEX );
+ else
+ Remove(pObjs->Get(static_cast<sal_uInt32>(n) - 1));
+ }
+}
+
+bool SbxCollection::LoadData( SvStream& rStrm, sal_uInt16 nVer )
+{
+ bool bRes = SbxObject::LoadData( rStrm, nVer );
+ Initialize();
+ return bRes;
+}
+
+
+SbxStdCollection::SbxStdCollection()
+ : bAddRemoveOk( true )
+{}
+
+SbxStdCollection::SbxStdCollection( const SbxStdCollection& r )
+ : SvRefBase( r ), SbxCollection( r ),
+ aElemClass( r.aElemClass ), bAddRemoveOk( r.bAddRemoveOk )
+{}
+
+SbxStdCollection& SbxStdCollection::operator=( const SbxStdCollection& r )
+{
+ if( &r != this )
+ {
+ if( !r.aElemClass.equalsIgnoreAsciiCase( aElemClass ) )
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ }
+ else
+ {
+ SbxCollection::operator=( r );
+ }
+ }
+ return *this;
+}
+
+SbxStdCollection::~SbxStdCollection()
+{}
+
+// Default: Error, if wrong object
+
+void SbxStdCollection::Insert( SbxVariable* p )
+{
+ SbxObject* pObj = dynamic_cast<SbxObject*>( p );
+ if( pObj && !pObj->IsClass( aElemClass ) )
+ SetError( ERRCODE_BASIC_BAD_ACTION );
+ else
+ SbxCollection::Insert( p );
+}
+
+void SbxStdCollection::CollAdd( SbxArray* pPar_ )
+{
+ if( !bAddRemoveOk )
+ SetError( ERRCODE_BASIC_BAD_ACTION );
+ else
+ SbxCollection::CollAdd( pPar_ );
+}
+
+void SbxStdCollection::CollRemove( SbxArray* pPar_ )
+{
+ if( !bAddRemoveOk )
+ SetError( ERRCODE_BASIC_BAD_ACTION );
+ else
+ SbxCollection::CollRemove( pPar_ );
+}
+
+bool SbxStdCollection::LoadData( SvStream& rStrm, sal_uInt16 nVer )
+{
+ bool bRes = SbxCollection::LoadData( rStrm, nVer );
+ if( bRes )
+ {
+ aElemClass = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
+ RTL_TEXTENCODING_ASCII_US);
+ rStrm.ReadCharAsBool( bAddRemoveOk );
+ }
+ return bRes;
+}
+
+std::pair<bool, sal_uInt32> SbxStdCollection::StoreData( SvStream& rStrm ) const
+{
+ const auto& [bRes, nVersion] = SbxCollection::StoreData(rStrm);
+ if( bRes )
+ {
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, aElemClass,
+ RTL_TEXTENCODING_ASCII_US);
+ rStrm.WriteBool( bAddRemoveOk );
+ }
+ return { bRes, nVersion };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxconv.hxx b/basic/source/sbx/sbxconv.hxx
new file mode 100644
index 0000000000..a3837d9a3d
--- /dev/null
+++ b/basic/source/sbx/sbxconv.hxx
@@ -0,0 +1,162 @@
+/* -*- 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 "sbxdec.hxx"
+#include <basic/sberrors.hxx>
+#include <basic/sbx.hxx>
+#include <basic/sbxcore.hxx>
+#include <basic/sbxdef.hxx>
+
+#include <o3tl/float_int_conversion.hxx>
+#include <rtl/math.hxx>
+#include <sal/types.h>
+
+class SbxArray;
+
+template <typename I> inline I DoubleTo(double f, I min, I max)
+{
+ f = rtl::math::round(f);
+ if (!o3tl::convertsToAtMost(f, max))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return max;
+ }
+ if (!o3tl::convertsToAtLeast(f, min))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return min;
+ }
+ return f;
+}
+
+inline auto ImpDoubleToChar(double f) { return DoubleTo<sal_Unicode>(f, SbxMINCHAR, SbxMAXCHAR); }
+inline auto ImpDoubleToByte(double f) { return DoubleTo<sal_uInt8>(f, 0, SbxMAXBYTE); }
+inline auto ImpDoubleToUShort(double f) { return DoubleTo<sal_uInt16>(f, 0, SbxMAXUINT); }
+inline auto ImpDoubleToInteger(double f) { return DoubleTo<sal_Int16>(f, SbxMININT, SbxMAXINT); }
+inline auto ImpDoubleToULong(double f) { return DoubleTo<sal_uInt32>(f, 0, SbxMAXULNG); }
+inline auto ImpDoubleToLong(double f) { return DoubleTo<sal_Int32>(f, SbxMINLNG, SbxMAXLNG); }
+inline auto ImpDoubleToSalUInt64(double d) { return DoubleTo<sal_uInt64>(d, 0, SAL_MAX_UINT64); }
+inline auto ImpDoubleToSalInt64(double d)
+{
+ return DoubleTo<sal_Int64>(d, SAL_MIN_INT64, SAL_MAX_INT64);
+}
+
+// SBXSCAN.CXX
+extern void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString=false );
+extern ErrCode ImpScan
+ ( const OUString& rSrc, double& nVal, SbxDataType& rType, sal_uInt16* pLen,
+ bool bOnlyIntntl );
+
+// with advanced evaluation (International, "TRUE"/"FALSE")
+extern bool ImpConvStringExt( OUString& rSrc, SbxDataType eTargetType );
+
+void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep, sal_Unicode& rcDecimalSepAlt );
+
+// SBXINT.CXX
+
+sal_Int16 ImpGetInteger( const SbxValues* );
+void ImpPutInteger( SbxValues*, sal_Int16 );
+
+sal_Int64 ImpGetInt64( const SbxValues* );
+void ImpPutInt64( SbxValues*, sal_Int64 );
+sal_uInt64 ImpGetUInt64( const SbxValues* );
+void ImpPutUInt64( SbxValues*, sal_uInt64 );
+
+double ImpSalUInt64ToDouble( sal_uInt64 n );
+
+// SBXLNG.CXX
+
+sal_Int32 ImpGetLong( const SbxValues* );
+void ImpPutLong( SbxValues*, sal_Int32 );
+
+// SBXSNG.CXX
+
+float ImpGetSingle( const SbxValues* );
+void ImpPutSingle( SbxValues*, float );
+
+// SBXDBL.CXX
+
+double ImpGetDouble( const SbxValues* );
+void ImpPutDouble( SbxValues*, double, bool bCoreString=false );
+
+// SBXCURR.CXX
+
+sal_Int64 ImpGetCurrency( const SbxValues* );
+void ImpPutCurrency( SbxValues*, const sal_Int64 );
+
+inline sal_Int64 ImpDoubleToCurrency( double d )
+{
+ if (d > 0)
+ return static_cast<sal_Int64>( d * CURRENCY_FACTOR + 0.5);
+ else
+ return static_cast<sal_Int64>( d * CURRENCY_FACTOR - 0.5);
+}
+
+inline double ImpCurrencyToDouble( const sal_Int64 r )
+ { return static_cast<double>(r) / double(CURRENCY_FACTOR); }
+
+
+// SBXDEC.CXX
+
+SbxDecimal* ImpCreateDecimal( SbxValues* p );
+SbxDecimal* ImpGetDecimal( const SbxValues* p );
+void ImpPutDecimal( SbxValues* p, SbxDecimal* pDec );
+
+// SBXDATE.CXX
+
+double ImpGetDate( const SbxValues* );
+void ImpPutDate( SbxValues*, double );
+
+// SBXSTR.CXX
+
+OUString ImpGetString( const SbxValues* );
+OUString ImpGetCoreString( const SbxValues* );
+void ImpPutString( SbxValues*, const OUString* );
+
+// SBXCHAR.CXX
+
+sal_Unicode ImpGetChar( const SbxValues* );
+void ImpPutChar( SbxValues*, sal_Unicode );
+
+// SBXBYTE.CXX
+sal_uInt8 ImpGetByte( const SbxValues* );
+void ImpPutByte( SbxValues*, sal_uInt8 );
+
+// SBXUINT.CXX
+
+sal_uInt16 ImpGetUShort( const SbxValues* );
+void ImpPutUShort( SbxValues*, sal_uInt16 );
+
+// SBXULNG.CXX
+
+sal_uInt32 ImpGetULong( const SbxValues* );
+void ImpPutULong( SbxValues*, sal_uInt32 );
+
+// SBXBOOL.CXX
+
+enum SbxBOOL ImpGetBool( const SbxValues* );
+void ImpPutBool( SbxValues*, sal_Int16 );
+
+// ByteArray <--> String
+SbxArray* StringToByteArray(const OUString& rStr);
+OUString ByteArrayToString(SbxArray* pArr);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxcurr.cxx b/basic/source/sbx/sbxcurr.cxx
new file mode 100644
index 0000000000..54b00102dd
--- /dev/null
+++ b/basic/source/sbx/sbxcurr.cxx
@@ -0,0 +1,484 @@
+/* -*- 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 <rtl/ustrbuf.hxx>
+
+#include <basic/sberrors.hxx>
+#include <basic/sbxvar.hxx>
+#include <o3tl/string_view.hxx>
+#include "sbxconv.hxx"
+
+
+static OUString ImpCurrencyToString( sal_Int64 rVal )
+{
+ bool isNeg = ( rVal < 0 );
+ sal_Int64 absVal = isNeg ? -rVal : rVal;
+
+ sal_Unicode const cDecimalSep = '.';
+
+ OUString aAbsStr = OUString::number( absVal );
+
+ sal_Int32 initialLen = aAbsStr.getLength();
+
+ bool bLessThanOne = false;
+ if ( initialLen <= 4 ) // if less the 1
+ bLessThanOne = true;
+
+ sal_Int32 nCapacity = 6; // minimum e.g. 0.0000
+
+ if ( !bLessThanOne )
+ {
+ nCapacity = initialLen + 1;
+ }
+
+ if ( isNeg )
+ ++nCapacity;
+
+ OUStringBuffer aBuf( nCapacity );
+ aBuf.setLength( nCapacity );
+
+
+ sal_Int32 nDigitCount = 0;
+ sal_Int32 nInsertIndex = nCapacity - 1;
+ sal_Int32 nEndIndex = isNeg ? 1 : 0;
+
+ for ( sal_Int32 charCpyIndex = aAbsStr.getLength() - 1; nInsertIndex >= nEndIndex; ++nDigitCount )
+ {
+ if ( nDigitCount == 4 )
+ aBuf[nInsertIndex--] = cDecimalSep;
+ if ( nDigitCount < initialLen )
+ aBuf[nInsertIndex--] = aAbsStr[ charCpyIndex-- ];
+ else
+ // Handle leading 0's to right of decimal point
+ // Note: in VBA the stringification is a little more complex
+ // but more natural as only the necessary digits
+ // to the right of the decimal places are displayed
+ // It would be great to conditionally be able to display like that too
+
+ // Val OOo (Cur) VBA (Cur)
+ // --- --------- ---------
+ // 0 0.0000 0
+ // 0.1 0.1000 0.1
+
+ aBuf[nInsertIndex--] = '0';
+ }
+ if ( isNeg )
+ aBuf[nInsertIndex] = '-';
+
+ aAbsStr = aBuf.makeStringAndClear();
+ return aAbsStr;
+}
+
+
+static sal_Int64 ImpStringToCurrency( std::u16string_view rStr )
+{
+
+ sal_Int32 nFractDigit = 4;
+
+ sal_Unicode const cDeciPnt = '.';
+ sal_Unicode const c1000Sep = ',';
+
+ // lets use the existing string number conversions
+ // there is a performance impact here ( multiple string copies )
+ // but better I think than a home brewed string parser, if we need a parser
+ // we should share some existing ( possibly from calc is there a currency
+ // conversion there ? #TODO check )
+
+ std::u16string_view sTmp = o3tl::trim( rStr );
+ auto p = sTmp.begin();
+ auto pEnd = sTmp.end();
+
+ // normalise string number by removing thousand & decimal point separators
+ OUStringBuffer sNormalisedNumString( static_cast<sal_Int32>(sTmp.size()) + nFractDigit );
+
+ if ( p != pEnd && (*p == '-' || *p == '+' ) )
+ sNormalisedNumString.append( *p++ );
+
+ while ( p != pEnd && *p >= '0' && *p <= '9' )
+ {
+ sNormalisedNumString.append( *p++ );
+ // #TODO in vba mode set runtime error when a space ( or other )
+ // illegal character is found
+ if( p != pEnd && *p == c1000Sep )
+ p++;
+ }
+
+ bool bRoundUp = false;
+
+ if( p != pEnd && *p == cDeciPnt )
+ {
+ p++;
+ while( nFractDigit && p != pEnd && *p >= '0' && *p <= '9' )
+ {
+ sNormalisedNumString.append( *p++ );
+ nFractDigit--;
+ }
+ // Consume trailing content
+ // Round up if necessary
+ if( p != pEnd && *p >= '5' && *p <= '9' )
+ bRoundUp = true;
+ while( p != pEnd && *p >= '0' && *p <= '9' )
+ p++;
+ }
+ // can we raise error here ? ( previous behaviour was more forgiving )
+ // so... not sure that could break existing code, let's see if anyone
+ // complains.
+
+ if ( p != pEnd )
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ while( nFractDigit )
+ {
+ sNormalisedNumString.append( '0' );
+ nFractDigit--;
+ }
+
+ sal_Int64 result = o3tl::toInt64(sNormalisedNumString);
+
+ if ( bRoundUp )
+ ++result;
+ return result;
+}
+
+
+sal_Int64 ImpGetCurrency( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_Int64 nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxERROR:
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ nRes = 0; break;
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCURRENCY:
+ nRes = p->nInt64; break;
+ case SbxBYTE:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nByte);
+ break;
+ case SbxCHAR:
+ nRes = sal_Int64(CURRENCY_FACTOR) * reinterpret_cast<sal_Int64>(p->pChar);
+ break;
+ case SbxBOOL:
+ case SbxINTEGER:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nInteger);
+ break;
+ case SbxUSHORT:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nUShort);
+ break;
+ case SbxLONG:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nLong);
+ break;
+ case SbxULONG:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nULong);
+ break;
+
+ case SbxSALINT64:
+ {
+ nRes = p->nInt64 * CURRENCY_FACTOR; break;
+#if 0
+ // Huh, is the 'break' above intentional? That means this
+ // is unreachable, obviously. Avoid warning by ifdeffing
+ // this out for now. Do not delete this #if 0 block unless
+ // you know for sure the 'break' above is intentional.
+ if ( nRes > SAL_MAX_INT64 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MAX_INT64;
+ }
+#endif
+ }
+ case SbxSALUINT64:
+ nRes = p->nInt64 * CURRENCY_FACTOR; break;
+#if 0
+ // As above
+ if ( nRes > SAL_MAX_INT64 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MAX_INT64;
+ }
+ else if ( nRes < SAL_MIN_INT64 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MIN_INT64;
+ }
+ break;
+#endif
+//TODO: bring back SbxINT64 types here for limits -1 with flag value at SAL_MAX/MIN
+ case SbxSINGLE:
+ if( p->nSingle * CURRENCY_FACTOR + 0.5 > float(SAL_MAX_INT64)
+ || p->nSingle * CURRENCY_FACTOR - 0.5 < float(SAL_MIN_INT64) )
+ {
+ nRes = SAL_MAX_INT64;
+ if( p->nSingle * CURRENCY_FACTOR - 0.5 < float(SAL_MIN_INT64) )
+ nRes = SAL_MIN_INT64;
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ nRes = ImpDoubleToCurrency( static_cast<double>(p->nSingle) );
+ break;
+
+ case SbxDATE:
+ case SbxDOUBLE:
+ if( p->nDouble * CURRENCY_FACTOR + 0.5 > double(SAL_MAX_INT64)
+ || p->nDouble * CURRENCY_FACTOR - 0.5 < double(SAL_MIN_INT64) )
+ {
+ nRes = SAL_MAX_INT64;
+ if( p->nDouble * CURRENCY_FACTOR - 0.5 < double(SAL_MIN_INT64) )
+ nRes = SAL_MIN_INT64;
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ nRes = ImpDoubleToCurrency( p->nDouble );
+ break;
+
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ double d = 0.0;
+ if( p->pDecimal )
+ p->pDecimal->getDouble( d );
+ nRes = ImpDoubleToCurrency( d );
+ break;
+ }
+
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ nRes=0;
+ else
+ nRes = ImpStringToCurrency( *p->pOUString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetCurrency();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ nRes=0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pChar);
+ break;
+ case SbxBYREF | SbxBYTE:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pByte);
+ break;
+ case SbxBYREF | SbxBOOL:
+ case SbxBYREF | SbxINTEGER:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pInteger);
+ break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pUShort);
+ break;
+
+ // from here on had to be tested
+ case SbxBYREF | SbxLONG:
+ aTmp.nLong = *p->pLong; goto ref;
+ case SbxBYREF | SbxULONG:
+ aTmp.nULong = *p->pULong; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & ~SbxBYREF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ nRes=0;
+ }
+ return nRes;
+}
+
+
+void ImpPutCurrency( SbxValues* p, const sal_Int64 r )
+{
+ SbxValues aTmp;
+start:
+ switch( +p->eType )
+ {
+ // Here are tests necessary
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger; goto direct;
+ case SbxLONG:
+ aTmp.pLong = &p->nLong; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ // from here no longer
+ case SbxSINGLE:
+ p->nSingle = static_cast<float>( r / CURRENCY_FACTOR ); break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = ImpCurrencyToDouble( r ); break;
+ case SbxSALUINT64:
+ p->uInt64 = r / CURRENCY_FACTOR; break;
+ case SbxSALINT64:
+ p->nInt64 = r / CURRENCY_FACTOR; break;
+
+ case SbxCURRENCY:
+ p->nInt64 = r; break;
+
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ SbxDecimal* pDec = ImpCreateDecimal( p );
+ if( !pDec->setDouble( ImpCurrencyToDouble( r ) / CURRENCY_FACTOR ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+
+ *p->pOUString = ImpCurrencyToString( r );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutCurrency( r );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ {
+ sal_Int64 val = r / CURRENCY_FACTOR;
+ if( val > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXCHAR;
+ }
+ else if( val < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMINCHAR;
+ }
+ *p->pChar = static_cast<sal_Unicode>(val); break;
+ }
+ case SbxBYREF | SbxBYTE:
+ {
+ sal_Int64 val = r / CURRENCY_FACTOR;
+ if( val > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXBYTE;
+ }
+ else if( val < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0;
+ }
+ *p->pByte = static_cast<sal_uInt8>(val); break;
+ }
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ {
+ sal_Int64 val = r / CURRENCY_FACTOR;
+ if( r > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXINT;
+ }
+ else if( r < SbxMININT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMININT;
+ }
+ *p->pInteger = static_cast<sal_uInt16>(val); break;
+ }
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ {
+ sal_Int64 val = r / CURRENCY_FACTOR;
+ if( val > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXUINT;
+ }
+ else if( val < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0;
+ }
+ *p->pUShort = static_cast<sal_uInt16>(val); break;
+ }
+ case SbxBYREF | SbxLONG:
+ {
+ sal_Int64 val = r / CURRENCY_FACTOR;
+ if( val > SbxMAXLNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXLNG;
+ }
+ else if( val < SbxMINLNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMINLNG;
+ }
+ *p->pLong = static_cast<sal_Int32>(val); break;
+ }
+ case SbxBYREF | SbxULONG:
+ {
+ sal_Int64 val = r / CURRENCY_FACTOR;
+ if( val > SbxMAXULNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXULNG;
+ }
+ else if( val < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0;
+ }
+ *p->pULong = static_cast<sal_uInt32>(val); break;
+ }
+ case SbxBYREF | SbxCURRENCY:
+ *p->pnInt64 = r; break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = r / CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = static_cast<sal_uInt64>(r) / CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSINGLE:
+ p->nSingle = static_cast<float>( r / CURRENCY_FACTOR ); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = ImpCurrencyToDouble( r ); break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxdate.cxx b/basic/source/sbx/sbxdate.cxx
new file mode 100644
index 0000000000..057e16f09d
--- /dev/null
+++ b/basic/source/sbx/sbxdate.cxx
@@ -0,0 +1,408 @@
+/* -*- 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 <rtl/math.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <tools/color.hxx>
+#include <i18nlangtag/lang.h>
+#include <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include <runtime.hxx>
+#include <sbintern.hxx>
+#include <math.h>
+#include <memory>
+#include <config_features.h>
+
+
+double ImpGetDate( const SbxValues* p )
+{
+ double nRes;
+ SbxValue* pVal;
+
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0;
+ break;
+ case SbxCHAR:
+ nRes = p->nChar;
+ break;
+ case SbxBYTE:
+ nRes = p->nByte;
+ break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ nRes = p->nInteger;
+ break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = p->nUShort;
+ break;
+ case SbxLONG:
+ nRes = static_cast<double>(p->nLong);
+ break;
+ case SbxULONG:
+ nRes = static_cast<double>(p->nULong);
+ break;
+ case SbxSINGLE:
+ nRes = p->nSingle;
+ break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ nRes = p->nDouble;
+ break;
+ case SbxCURRENCY:
+ nRes = ImpCurrencyToDouble( p->nInt64 );
+ break;
+ case SbxSALINT64:
+ nRes = static_cast< double >(p->nInt64);
+ break;
+ case SbxSALUINT64:
+ nRes = ImpSalUInt64ToDouble( p->uInt64 );
+ break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ if (!p->pDecimal || !p->pDecimal->getDouble(nRes))
+ nRes = 0.0;
+ break;
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+#if HAVE_FEATURE_SCRIPTING
+ if( !p->pOUString )
+ {
+ nRes = 0;
+ }
+ else
+ {
+ LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
+ std::shared_ptr<SvNumberFormatter> pFormatter;
+ if (GetSbData()->pInst)
+ {
+ pFormatter = GetSbData()->pInst->GetNumberFormatter();
+ }
+ else
+ {
+ sal_uInt32 nDummy;
+ pFormatter = SbiInstance::PrepareNumberFormatter( nDummy, nDummy, nDummy );
+ }
+
+ sal_uInt32 nIndex;
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType = SvNumFormatType::DEFINED | SvNumFormatType::DATE | SvNumFormatType::TIME | SvNumFormatType::CURRENCY
+ | SvNumFormatType::NUMBER | SvNumFormatType::SCIENTIFIC | SvNumFormatType::FRACTION;
+
+ // Default templates of the formatter have only two-digit
+ // date. Therefore register an own format.
+
+ // HACK, because the number formatter in PutandConvertEntry replace the wildcard
+ // for month, day, year not according to the configuration.
+ // Problem: Print Year(Date) under Engl. OS
+ // quod vide basic/source/runtime/runtime.cxx
+
+ SvtSysLocale aSysLocale;
+ DateOrder eDate = aSysLocale.GetLocaleData().getDateOrder();
+ 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 + " HH:MM:SS";
+
+ pFormatter->PutandConvertEntry( aStr, nCheckPos, nType,
+ nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
+ bool bSuccess = pFormatter->IsNumberFormat( *p->pOUString, nIndex, nRes );
+ if ( bSuccess )
+ {
+ SvNumFormatType nType_ = pFormatter->GetType( nIndex );
+ if(!(nType_ & ( SvNumFormatType::DATETIME | SvNumFormatType::DATE |
+ SvNumFormatType::TIME | SvNumFormatType::DEFINED )))
+ {
+ bSuccess = false;
+ }
+ }
+
+ if ( !bSuccess )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ }
+#else
+ nRes = 0;
+#endif
+ break;
+ case SbxOBJECT:
+ pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ {
+ nRes = pVal->GetDate();
+ }
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar;
+ break;
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte;
+ break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ nRes = *p->pInteger;
+ break;
+ case SbxBYREF | SbxLONG:
+ nRes = *p->pLong;
+ break;
+ case SbxBYREF | SbxULONG:
+ nRes = *p->pULong;
+ break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ nRes = *p->pUShort;
+ break;
+ case SbxBYREF | SbxSINGLE:
+ nRes = *p->pSingle;
+ break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ nRes = *p->pDouble;
+ break;
+ case SbxBYREF | SbxCURRENCY:
+ nRes = ImpCurrencyToDouble( *p->pnInt64 );
+ break;
+ case SbxBYREF | SbxSALINT64:
+ nRes = static_cast< double >(*p->pnInt64);
+ break;
+ case SbxBYREF | SbxSALUINT64:
+ nRes = ImpSalUInt64ToDouble( *p->puInt64 );
+ break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ break;
+ }
+ return nRes;
+}
+
+void ImpPutDate( SbxValues* p, double n )
+{
+ SbxValues aTmp;
+ SbxDecimal* pDec;
+ SbxValue* pVal;
+
+start:
+ switch( +p->eType )
+ {
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n;
+ break;
+ // from here will be tested
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar;
+ goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte;
+ goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger;
+ goto direct;
+ case SbxLONG:
+ aTmp.pLong = &p->nLong;
+ goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong;
+ goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort;
+ goto direct;
+ case SbxSINGLE:
+ aTmp.pSingle = &p->nSingle;
+ goto direct;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ aTmp.pnInt64 = &p->nInt64;
+ goto direct;
+ case SbxSALUINT64:
+ aTmp.puInt64 = &p->uInt64;
+ goto direct;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ pDec = ImpCreateDecimal( p );
+ if( !pDec->setDouble( n ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ }
+ break;
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ {
+#if HAVE_FEATURE_SCRIPTING
+ if( !p->pOUString )
+ {
+ p->pOUString = new OUString;
+ }
+ const Color* pColor;
+
+ LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
+ std::shared_ptr<SvNumberFormatter> pFormatter;
+ if (GetSbData()->pInst)
+ {
+ pFormatter = GetSbData()->pInst->GetNumberFormatter();
+ }
+ else
+ {
+ sal_uInt32 nDummy;
+ pFormatter = SbiInstance::PrepareNumberFormatter( nDummy, nDummy, nDummy );
+ }
+
+ sal_uInt32 nIndex;
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+
+ SvtSysLocale aSysLocale;
+ DateOrder eDate = aSysLocale.GetLocaleData().getDateOrder();
+ OUString aStr;
+ // if the whole-number part is 0, we want no year!
+ if( n <= -1.0 || n >= 1.0 )
+ {
+ // Time only if != 00:00:00
+ if( rtl::math::approxEqual(floor( n ), n) )
+ {
+ switch( eDate )
+ {
+ default:
+ case DateOrder::MDY: aStr = "MM/DD/YYYY"; break;
+ case DateOrder::DMY: aStr = "DD/MM/YYYY"; break;
+ case DateOrder::YMD: aStr = "YYYY/MM/DD"; break;
+ }
+ }
+ else
+ {
+ switch( eDate )
+ {
+ default:
+ case DateOrder::MDY: aStr = "MM/DD/YYYY HH:MM:SS"; break;
+ case DateOrder::DMY: aStr = "DD/MM/YYYY HH:MM:SS"; break;
+ case DateOrder::YMD: aStr = "YYYY/MM/DD HH:MM:SS"; break;
+ }
+ }
+ }
+ else
+ {
+ aStr = "HH:MM:SS";
+ }
+ pFormatter->PutandConvertEntry( aStr,
+ nCheckPos,
+ nType,
+ nIndex,
+ LANGUAGE_ENGLISH_US,
+ eLangType, true);
+ pFormatter->GetOutputString( n, nIndex, *p->pOUString, &pColor );
+#endif
+ break;
+ }
+ case SbxOBJECT:
+ pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ {
+ pVal->PutDate( n );
+ }
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ }
+ break;
+ case SbxBYREF | SbxCHAR:
+ *p->pChar = ImpDoubleToChar(n);
+ break;
+ case SbxBYREF | SbxBYTE:
+ *p->pByte = ImpDoubleToByte(n);
+ break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ *p->pInteger = ImpDoubleToInteger(n);
+ break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ *p->pUShort = ImpDoubleToUShort(n);
+ break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = ImpDoubleToLong(n);
+ break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = ImpDoubleToULong(n);
+ break;
+ case SbxBYREF | SbxSINGLE:
+ if( n > SbxMAXSNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXSNG;
+ }
+ else if( n < SbxMINSNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINSNG;
+ }
+ *p->pSingle = static_cast<float>(n);
+ break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = ImpDoubleToSalInt64( n );
+ break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = ImpDoubleToSalUInt64( n );
+ break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = n;
+ break;
+ case SbxBYREF | SbxCURRENCY:
+ if( n > SbxMAXCURR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXCURR;
+ }
+ else if( n < SbxMINCURR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINCURR;
+ }
+ *p->pnInt64 = ImpDoubleToCurrency( n );
+ break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxdbl.cxx b/basic/source/sbx/sbxdbl.cxx
new file mode 100644
index 0000000000..10cdbc203b
--- /dev/null
+++ b/basic/source/sbx/sbxdbl.cxx
@@ -0,0 +1,266 @@
+/* -*- 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 <comphelper/errcode.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+#include <runtime.hxx>
+
+double ImpGetDouble( const SbxValues* p )
+{
+ double nRes;
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar; break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ nRes = p->nInteger; break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = p->nUShort; break;
+ case SbxLONG:
+ nRes = p->nLong; break;
+ case SbxULONG:
+ nRes = p->nULong; break;
+ case SbxSINGLE:
+ nRes = p->nSingle; break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ nRes = p->nDouble; break;
+ case SbxCURRENCY:
+ nRes = ImpCurrencyToDouble( p->nInt64 ); break;
+ case SbxSALINT64:
+ nRes = static_cast< double >(p->nInt64); break;
+ case SbxSALUINT64:
+ nRes = ImpSalUInt64ToDouble( p->uInt64 ); break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ if (!p->pDecimal || !p->pDecimal->getDouble(nRes))
+ nRes = 0.0;
+ break;
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ {
+ nRes = 0;
+#if HAVE_FEATURE_SCRIPTING
+ if ( SbiRuntime::isVBAEnabled() )// VBA only behaviour
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+#endif
+ }
+ else
+ {
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ {
+ nRes = 0;
+#if HAVE_FEATURE_SCRIPTING
+ if ( SbiRuntime::isVBAEnabled() )// VBA only behaviour
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+#endif
+ }
+ else
+ nRes = d;
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetDouble();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar; break;
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte; break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ nRes = *p->pInteger; break;
+ case SbxBYREF | SbxLONG:
+ nRes = *p->pLong; break;
+ case SbxBYREF | SbxULONG:
+ nRes = *p->pULong; break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ nRes = *p->pUShort; break;
+ case SbxBYREF | SbxSINGLE:
+ nRes = *p->pSingle; break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ nRes = *p->pDouble; break;
+ case SbxBYREF | SbxCURRENCY:
+ nRes = ImpCurrencyToDouble( *p->pnInt64 ); break;
+ case SbxBYREF | SbxSALINT64:
+ nRes = static_cast< double >(*p->pnInt64); break;
+ case SbxBYREF | SbxSALUINT64:
+ nRes = ImpSalUInt64ToDouble( *p->puInt64 ); break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutDouble( SbxValues* p, double n, bool bCoreString )
+{
+ SbxValues aTmp;
+start:
+ switch( +p->eType )
+ {
+ // Here are tests necessary
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger; goto direct;
+ case SbxLONG:
+ aTmp.pLong = &p->nLong; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ case SbxSINGLE:
+ aTmp.pSingle = &p->nSingle; goto direct;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ SbxDecimal* pDec = ImpCreateDecimal( p );
+ if( !pDec->setDouble( n ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ case SbxCURRENCY:
+ if( n > SbxMAXCURR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXCURR;
+ }
+ else if( n < SbxMINCURR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINCURR;
+ }
+ p->nInt64 = ImpDoubleToCurrency( n );
+ break;
+
+ // from here on no longer
+ case SbxSALINT64:
+ p->nInt64 = ImpDoubleToSalInt64( n ); break;
+ case SbxSALUINT64:
+ p->uInt64 = ImpDoubleToSalUInt64( n ); break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ // tdf#107953 - show 17 significant digits
+ ImpCvtNum( n, 17, *p->pOUString, bCoreString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutDouble( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ *p->pChar = ImpDoubleToChar(n); break;
+ case SbxBYREF | SbxBYTE:
+ *p->pByte = ImpDoubleToByte(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ *p->pInteger = ImpDoubleToInteger(n); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ *p->pUShort = ImpDoubleToUShort(n); break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = ImpDoubleToLong(n); break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = ImpDoubleToULong(n); break;
+ case SbxBYREF | SbxSINGLE:
+ if( n > SbxMAXSNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXSNG;
+ }
+ else if( n < SbxMINSNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINSNG;
+ }
+ else if( n > 0 && n < SbxMAXSNG2 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXSNG2;
+ }
+ else if( n < 0 && n > SbxMINSNG2 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINSNG2;
+ }
+ *p->pSingle = static_cast<float>(n); break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = ImpDoubleToSalInt64( n ); break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = ImpDoubleToSalUInt64( n ); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = n; break;
+ case SbxBYREF | SbxCURRENCY:
+ if( n > SbxMAXCURR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXCURR;
+ }
+ else if( n < SbxMINCURR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINCURR;
+ }
+ *p->pnInt64 = ImpDoubleToCurrency( n ); break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxdec.cxx b/basic/source/sbx/sbxdec.cxx
new file mode 100644
index 0000000000..cad5601f2f
--- /dev/null
+++ b/basic/source/sbx/sbxdec.cxx
@@ -0,0 +1,682 @@
+/* -*- 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>
+#ifdef _WIN32
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <systools/win32/oleauto.hxx>
+#endif
+
+#include <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+
+#include <com/sun/star/bridge/oleautomation/Decimal.hpp>
+
+// Implementation SbxDecimal
+SbxDecimal::SbxDecimal()
+ : mnRefCount(0)
+{
+ setInt( 0 );
+}
+
+SbxDecimal::SbxDecimal( const SbxDecimal& rDec )
+ : mnRefCount(0)
+{
+#ifdef _WIN32
+ maDec = rDec.maDec;
+#else
+ (void)rDec;
+#endif
+}
+
+SbxDecimal::SbxDecimal
+ ( const css::bridge::oleautomation::Decimal& rAutomationDec )
+ : mnRefCount(0)
+{
+#ifdef _WIN32
+ maDec.scale = rAutomationDec.Scale;
+ maDec.sign = rAutomationDec.Sign;
+ maDec.Lo32 = rAutomationDec.LowValue;
+ maDec.Mid32 = rAutomationDec.MiddleValue;
+ maDec.Hi32 = rAutomationDec.HighValue;
+#else
+ (void)rAutomationDec;
+#endif
+}
+
+void SbxDecimal::fillAutomationDecimal
+ ( css::bridge::oleautomation::Decimal& rAutomationDec )
+{
+#ifdef _WIN32
+ rAutomationDec.Scale = maDec.scale;
+ rAutomationDec.Sign = maDec.sign;
+ rAutomationDec.LowValue = maDec.Lo32;
+ rAutomationDec.MiddleValue = maDec.Mid32;
+ rAutomationDec.HighValue = maDec.Hi32;
+#else
+ (void)rAutomationDec;
+#endif
+}
+
+void releaseDecimalPtr( SbxDecimal*& rpDecimal )
+{
+ if( rpDecimal )
+ {
+ rpDecimal->mnRefCount--;
+ if( rpDecimal->mnRefCount == 0 )
+ delete rpDecimal;
+ rpDecimal = nullptr;
+ }
+}
+
+#ifdef _WIN32
+
+bool SbxDecimal::operator -= ( const SbxDecimal &r )
+{
+ HRESULT hResult = VarDecSub( &maDec, const_cast<LPDECIMAL>(&r.maDec), &maDec );
+ bool bRet = ( hResult == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::operator += ( const SbxDecimal &r )
+{
+ HRESULT hResult = VarDecAdd( &maDec, const_cast<LPDECIMAL>(&r.maDec), &maDec );
+ bool bRet = ( hResult == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::operator /= ( const SbxDecimal &r )
+{
+ HRESULT hResult = VarDecDiv( &maDec, const_cast<LPDECIMAL>(&r.maDec), &maDec );
+ bool bRet = ( hResult == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::operator *= ( const SbxDecimal &r )
+{
+ HRESULT hResult = VarDecMul( &maDec, const_cast<LPDECIMAL>(&r.maDec), &maDec );
+ bool bRet = ( hResult == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::neg()
+{
+ HRESULT hResult = VarDecNeg( &maDec, &maDec );
+ bool bRet = ( hResult == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::isZero() const
+{
+ SbxDecimal aZeroDec;
+ aZeroDec.setLong( 0 );
+ bool bZero = CmpResult::EQ == compare( *this, aZeroDec );
+ return bZero;
+}
+
+SbxDecimal::CmpResult compare( const SbxDecimal &rLeft, const SbxDecimal &rRight )
+{
+ HRESULT hResult = VarDecCmp( const_cast<LPDECIMAL>(&rLeft.maDec), const_cast<LPDECIMAL>(&rRight.maDec) );
+ SbxDecimal::CmpResult eRes = static_cast<SbxDecimal::CmpResult>(hResult);
+ return eRes;
+}
+
+void SbxDecimal::setChar( sal_Unicode val )
+{
+ VarDecFromUI2( static_cast<sal_uInt16>(val), &maDec );
+}
+
+void SbxDecimal::setByte( sal_uInt8 val )
+{
+ VarDecFromUI1( val, &maDec );
+}
+
+void SbxDecimal::setShort( sal_Int16 val )
+{
+ VarDecFromI2( static_cast<short>(val), &maDec );
+}
+
+void SbxDecimal::setLong( sal_Int32 val )
+{
+ VarDecFromI4(static_cast<LONG>(val), &maDec);
+}
+
+void SbxDecimal::setUShort( sal_uInt16 val )
+{
+ VarDecFromUI2( val, &maDec );
+}
+
+void SbxDecimal::setULong( sal_uInt32 val )
+{
+ VarDecFromUI4( static_cast<ULONG>(val), &maDec );
+}
+
+bool SbxDecimal::setSingle( float val )
+{
+ bool bRet = ( VarDecFromR4( val, &maDec ) == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::setDouble( double val )
+{
+ bool bRet = ( VarDecFromR8( val, &maDec ) == S_OK );
+ return bRet;
+}
+
+void SbxDecimal::setInt( int val )
+{
+ setLong( static_cast<sal_Int32>(val) );
+}
+
+void SbxDecimal::setUInt( unsigned int val )
+{
+ setULong( static_cast<sal_uInt32>(val) );
+}
+
+bool SbxDecimal::setString( OUString* pOUString )
+{
+ assert(pOUString);
+
+ static LCID nLANGID = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
+
+ // Convert delimiter
+ sal_Unicode cDecimalSep;
+ sal_Unicode cThousandSep;
+ sal_Unicode cDecimalSepAlt;
+ ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
+
+ bool bRet = false;
+ HRESULT hResult;
+ if( cDecimalSep != '.' || cThousandSep != ',' )
+ {
+ int nLen = pOUString->getLength();
+ std::unique_ptr<sal_Unicode[]> pBuffer(new sal_Unicode[nLen + 1]);
+ pBuffer[nLen] = 0;
+
+ const sal_Unicode* pSrc = pOUString->getStr();
+ for( int i = 0 ; i < nLen ; ++i )
+ {
+ sal_Unicode c = pSrc[i];
+ if (c == cDecimalSep)
+ c = '.';
+ else if (c == cThousandSep)
+ c = ',';
+
+ pBuffer[i] = c;
+ }
+ hResult = VarDecFromStr( o3tl::toW(pBuffer.get()), nLANGID, 0, &maDec );
+ }
+ else
+ {
+ hResult = VarDecFromStr( o3tl::toW(pOUString->getStr()), nLANGID, 0, &maDec );
+ }
+ bRet = ( hResult == S_OK );
+ return bRet;
+}
+
+
+bool SbxDecimal::getChar( sal_Unicode& rVal )
+{
+ USHORT n;
+ bool bRet = ( VarUI2FromDec( &maDec, &n ) == S_OK );
+ if (bRet) {
+ rVal = n;
+ }
+ return bRet;
+}
+
+bool SbxDecimal::getShort( sal_Int16& rVal )
+{
+ bool bRet = ( VarI2FromDec( &maDec, &rVal ) == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::getLong( sal_Int32& rVal )
+{
+ bool bRet = ( VarI4FromDec( &maDec, &rVal ) == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::getUShort( sal_uInt16& rVal )
+{
+ bool bRet = ( VarUI2FromDec( &maDec, &rVal ) == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::getULong( sal_uInt32& rVal )
+{
+ bool bRet = ( VarUI4FromDec( &maDec, &rVal ) == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::getSingle( float& rVal )
+{
+ bool bRet = ( VarR4FromDec( &maDec, &rVal ) == S_OK );
+ return bRet;
+}
+
+bool SbxDecimal::getDouble( double& rVal )
+{
+ bool bRet = ( VarR8FromDec( &maDec, &rVal ) == S_OK );
+ return bRet;
+}
+
+#else
+// !_WIN32
+
+bool SbxDecimal::operator -= ( const SbxDecimal & )
+{
+ return false;
+}
+
+bool SbxDecimal::operator += ( const SbxDecimal & )
+{
+ return false;
+}
+
+bool SbxDecimal::operator /= ( const SbxDecimal & )
+{
+ return false;
+}
+
+bool SbxDecimal::operator *= ( const SbxDecimal & )
+{
+ return false;
+}
+
+bool SbxDecimal::neg()
+{
+ return false;
+}
+
+bool SbxDecimal::isZero() const
+{
+ return false;
+}
+
+SbxDecimal::CmpResult compare( SAL_UNUSED_PARAMETER const SbxDecimal &, SAL_UNUSED_PARAMETER const SbxDecimal & )
+{
+ return SbxDecimal::CmpResult::LT;
+}
+
+void SbxDecimal::setChar( SAL_UNUSED_PARAMETER sal_Unicode ) {}
+void SbxDecimal::setByte( SAL_UNUSED_PARAMETER sal_uInt8 ) {}
+void SbxDecimal::setShort( SAL_UNUSED_PARAMETER sal_Int16 ) {}
+void SbxDecimal::setLong( SAL_UNUSED_PARAMETER sal_Int32 ) {}
+void SbxDecimal::setUShort( SAL_UNUSED_PARAMETER sal_uInt16 ) {}
+void SbxDecimal::setULong( SAL_UNUSED_PARAMETER sal_uInt32 ) {}
+bool SbxDecimal::setSingle( SAL_UNUSED_PARAMETER float ) { return false; }
+bool SbxDecimal::setDouble( SAL_UNUSED_PARAMETER double ) { return false; }
+void SbxDecimal::setInt( SAL_UNUSED_PARAMETER int ) {}
+void SbxDecimal::setUInt( SAL_UNUSED_PARAMETER unsigned int ) {}
+bool SbxDecimal::setString( SAL_UNUSED_PARAMETER OUString* ) { return false; }
+
+bool SbxDecimal::getChar( SAL_UNUSED_PARAMETER sal_Unicode& ) { return false; }
+bool SbxDecimal::getShort( SAL_UNUSED_PARAMETER sal_Int16& ) { return false; }
+bool SbxDecimal::getLong( SAL_UNUSED_PARAMETER sal_Int32& ) { return false; }
+bool SbxDecimal::getUShort( SAL_UNUSED_PARAMETER sal_uInt16& ) { return false; }
+bool SbxDecimal::getULong( SAL_UNUSED_PARAMETER sal_uInt32& ) { return false; }
+bool SbxDecimal::getSingle( SAL_UNUSED_PARAMETER float& ) { return false; }
+bool SbxDecimal::getDouble( SAL_UNUSED_PARAMETER double& ) { return false; }
+
+#endif
+
+void SbxDecimal::getString( OUString& rString )
+{
+#ifdef _WIN32
+ static LCID nLANGID = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
+
+ sal::systools::BStr pBStr;
+ // VarBstrFromDec allocates new BSTR that needs to be released with SysFreeString
+ HRESULT hResult = VarBstrFromDec( &maDec, nLANGID, 0, &pBStr );
+ if( hResult == S_OK )
+ {
+ // Convert delimiter
+ sal_Unicode cDecimalSep;
+ sal_Unicode cThousandSep;
+ sal_Unicode cDecimalSepAlt;
+ ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
+
+ if( cDecimalSep != '.' || cThousandSep != ',' )
+ {
+ sal_Unicode c;
+ int i = 0;
+ while( (c = pBStr[i]) != 0 )
+ {
+ if( c == '.' )
+ pBStr[i] = cDecimalSep;
+ else if( c == ',' )
+ pBStr[i] = cThousandSep;
+ i++;
+ }
+ }
+ rString = pBStr;
+ }
+#else
+ (void)rString;
+#endif
+}
+
+SbxDecimal* ImpCreateDecimal( SbxValues* p )
+{
+ if( !p )
+ return nullptr;
+
+ SbxDecimal*& rpDecimal = p->pDecimal;
+ if( rpDecimal == nullptr )
+ {
+ rpDecimal = new SbxDecimal();
+ rpDecimal->addRef();
+ }
+ return rpDecimal;
+}
+
+SbxDecimal* ImpGetDecimal( const SbxValues* p )
+{
+ SbxValues aTmp;
+ SbxDecimal* pnDecRes;
+
+ SbxDataType eType = p->eType;
+ if( eType == SbxDECIMAL && p->pDecimal )
+ {
+ pnDecRes = new SbxDecimal( *p->pDecimal );
+ pnDecRes->addRef();
+ return pnDecRes;
+ }
+ pnDecRes = new SbxDecimal();
+ pnDecRes->addRef();
+
+start:
+ switch( +eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ pnDecRes->setShort( 0 ); break;
+ case SbxCHAR:
+ pnDecRes->setChar( p->nChar ); break;
+ case SbxBYTE:
+ pnDecRes->setByte( p->nByte ); break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ pnDecRes->setInt( p->nInteger ); break;
+ case SbxERROR:
+ case SbxUSHORT:
+ pnDecRes->setUShort( p->nUShort ); break;
+ case SbxLONG:
+ pnDecRes->setLong( p->nLong ); break;
+ case SbxULONG:
+ pnDecRes->setULong( p->nULong ); break;
+ case SbxSINGLE:
+ if( !pnDecRes->setSingle( p->nSingle ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ case SbxCURRENCY:
+ {
+ if( !pnDecRes->setDouble( ImpCurrencyToDouble( p->nInt64 ) ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ case SbxSALINT64:
+ {
+ if( !pnDecRes->setDouble( static_cast<double>(p->nInt64) ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ case SbxSALUINT64:
+ {
+ if( !pnDecRes->setDouble( static_cast<double>(p->uInt64) ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ case SbxDATE:
+ case SbxDOUBLE:
+ {
+ double dVal = p->nDouble;
+ if( !pnDecRes->setDouble( dVal ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ case SbxLPSTR:
+ case SbxSTRING:
+ case SbxBYREF | SbxSTRING:
+ if ( p->pOUString )
+ pnDecRes->setString( p->pOUString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pnDecRes->setDecimal( pVal->GetDecimal() );
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ pnDecRes->setShort( 0 );
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ pnDecRes->setChar( *p->pChar ); break;
+ case SbxBYREF | SbxBYTE:
+ pnDecRes->setByte( *p->pByte ); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ pnDecRes->setInt( *p->pInteger ); break;
+ case SbxBYREF | SbxLONG:
+ pnDecRes->setLong( *p->pLong ); break;
+ case SbxBYREF | SbxULONG:
+ pnDecRes->setULong( *p->pULong ); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ pnDecRes->setUShort( *p->pUShort ); break;
+
+ // from here on had to be tested
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); pnDecRes->setShort( 0 );
+ }
+ return pnDecRes;
+}
+
+void ImpPutDecimal( SbxValues* p, SbxDecimal* pDec )
+{
+ if( !pDec )
+ return;
+
+ SbxValues aTmp;
+start:
+ switch( +p->eType )
+ {
+ // here had to be tested
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger; goto direct;
+ case SbxLONG:
+ aTmp.pLong = &p->nLong; goto direct;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ aTmp.pnInt64 = &p->nInt64; goto direct;
+ case SbxSALUINT64:
+ aTmp.puInt64 = &p->uInt64; goto direct;
+
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ // from here on no longer
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ if( pDec != p->pDecimal )
+ {
+ releaseDecimalPtr( p->pDecimal );
+ p->pDecimal = pDec;
+ if( pDec )
+ pDec->addRef();
+ }
+ break;
+ }
+ case SbxSINGLE:
+ {
+ float f(0.0);
+ pDec->getSingle( f );
+ p->nSingle = f;
+ break;
+ }
+ case SbxDATE:
+ case SbxDOUBLE:
+ {
+ double d(0.0);
+ pDec->getDouble( d );
+ p->nDouble = d;
+ break;
+ }
+
+ case SbxLPSTR:
+ case SbxSTRING:
+ case SbxBYREF | SbxSTRING:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ pDec->getString( *p->pOUString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutDecimal( pDec );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ if( !pDec->getChar( *p->pChar ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pChar = 0;
+ }
+ break;
+ case SbxBYREF | SbxBYTE:
+ if( !pDec->getChar( *p->pChar ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pByte = 0;
+ }
+ break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ if( !pDec->getShort( *p->pInteger ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pInteger = 0;
+ }
+ break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ if( !pDec->getUShort( *p->pUShort ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pUShort = 0;
+ }
+ break;
+ case SbxBYREF | SbxLONG:
+ if( !pDec->getLong( *p->pLong ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pLong = 0;
+ }
+ break;
+ case SbxBYREF | SbxULONG:
+ if( !pDec->getULong( *p->pULong ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pULong = 0;
+ }
+ break;
+ case SbxBYREF | SbxCURRENCY:
+ {
+ double d(0.0);
+ if( !pDec->getDouble( d ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pnInt64 = ImpDoubleToCurrency( d );
+ }
+ break;
+ case SbxBYREF | SbxSALINT64:
+ {
+ double d(0.0);
+ if( !pDec->getDouble( d ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ else
+ *p->pnInt64 = ImpDoubleToSalInt64( d );
+ }
+ break;
+ case SbxBYREF | SbxSALUINT64:
+ {
+ double d(0.0);
+ if( !pDec->getDouble( d ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ else
+ *p->puInt64 = ImpDoubleToSalUInt64( d );
+ }
+ break;
+ case SbxBYREF | SbxSINGLE:
+ if( !pDec->getSingle( *p->pSingle ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pSingle = 0;
+ }
+ break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ if( !pDec->getDouble( *p->pDouble ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->pDouble = 0;
+ }
+ break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxdec.hxx b/basic/source/sbx/sbxdec.hxx
new file mode 100644
index 0000000000..756fdc9f92
--- /dev/null
+++ b/basic/source/sbx/sbxdec.hxx
@@ -0,0 +1,97 @@
+/* -*- 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
+
+#ifdef _WIN32
+#include <prewin.h>
+#include <postwin.h>
+#include <comutil.h>
+#include <oleauto.h>
+#endif
+
+#include <com/sun/star/bridge/oleautomation/Decimal.hpp>
+
+
+// Decimal support
+// Implementation only for windows
+
+class SbxDecimal
+{
+ friend void releaseDecimalPtr( SbxDecimal*& rpDecimal );
+
+#ifdef _WIN32
+ DECIMAL maDec;
+#endif
+ sal_Int32 mnRefCount;
+
+public:
+ SbxDecimal();
+ SbxDecimal( const SbxDecimal& rDec );
+ explicit SbxDecimal( const css::bridge::oleautomation::Decimal& rAutomationDec );
+
+ void addRef()
+ { mnRefCount++; }
+
+ void fillAutomationDecimal( css::bridge::oleautomation::Decimal& rAutomationDec );
+
+ void setChar( sal_Unicode val );
+ void setByte( sal_uInt8 val );
+ void setShort( sal_Int16 val );
+ void setLong( sal_Int32 val );
+ void setUShort( sal_uInt16 val );
+ void setULong( sal_uInt32 val );
+ bool setSingle( float val );
+ bool setDouble( double val );
+ void setInt( int val );
+ void setUInt( unsigned int val );
+ bool setString( OUString* pOUString );
+ void setDecimal( SbxDecimal const * pDecimal )
+ {
+#ifdef _WIN32
+ if( pDecimal )
+ maDec = pDecimal->maDec;
+#else
+ (void)pDecimal;
+#endif
+ }
+
+ bool getChar( sal_Unicode& rVal );
+ bool getShort( sal_Int16& rVal );
+ bool getLong( sal_Int32& rVal );
+ bool getUShort( sal_uInt16& rVal );
+ bool getULong( sal_uInt32& rVal );
+ bool getSingle( float& rVal );
+ bool getDouble( double& rVal );
+ void getString( OUString& rString );
+
+ bool operator -= ( const SbxDecimal &r );
+ bool operator += ( const SbxDecimal &r );
+ bool operator /= ( const SbxDecimal &r );
+ bool operator *= ( const SbxDecimal &r );
+ bool neg();
+
+ bool isZero() const;
+
+ // must match the return values of the Microsoft VarDecCmp Automation function
+ enum class CmpResult { LT, EQ, GT };
+ friend CmpResult compare( const SbxDecimal &rLeft, const SbxDecimal &rRight );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxexec.cxx b/basic/source/sbx/sbxexec.cxx
new file mode 100644
index 0000000000..af7d12c006
--- /dev/null
+++ b/basic/source/sbx/sbxexec.cxx
@@ -0,0 +1,391 @@
+/* -*- 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>
+
+#include <basic/sbx.hxx>
+#include <basic/sberrors.hxx>
+#include <rtl/character.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <basiccharclass.hxx>
+
+static SbxVariableRef Element
+ ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf,
+ SbxClassType, bool bCompatible );
+
+static const sal_Unicode* SkipWhitespace( const sal_Unicode* p )
+{
+ while( BasicCharClass::isWhitespace(*p) )
+ p++;
+ return p;
+}
+
+// Scanning of a symbol. The symbol were inserted in rSym, the return value
+// is the new scan position. The symbol is at errors empty.
+
+static const sal_Unicode* Symbol( const sal_Unicode* p, OUString& rSym, bool bCompatible )
+{
+ sal_uInt16 nLen = 0;
+ // Did we have a nonstandard symbol?
+ if( *p == '[' )
+ {
+ rSym = ++p;
+ while( *p && *p != ']' )
+ {
+ p++;
+ nLen++;
+ }
+ p++;
+ }
+ else
+ {
+ // A symbol had to begin with an alphabetic character or an underline
+ if( !BasicCharClass::isAlpha( *p, bCompatible ) && *p != '_' )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_SYNTAX );
+ }
+ else
+ {
+ rSym = p;
+ // The it can contain alphabetic characters, numbers or underlines
+ while( *p && (BasicCharClass::isAlphaNumeric( *p, bCompatible ) || *p == '_') )
+ {
+ p++;
+ nLen++;
+ }
+ // Ignore standard BASIC suffixes
+ if( *p && (*p == '%' || *p == '&' || *p == '!' || *p == '#' || *p == '$' ) )
+ {
+ p++;
+ }
+ }
+ }
+ rSym = rSym.copy( 0, nLen );
+ return p;
+}
+
+// Qualified name. Element.Element...
+
+static SbxVariableRef QualifiedName
+ ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, SbxClassType t, bool bCompatible )
+{
+
+ SbxVariableRef refVar;
+ const sal_Unicode* p = SkipWhitespace( *ppBuf );
+ if( BasicCharClass::isAlpha( *p, bCompatible ) || *p == '_' || *p == '[' )
+ {
+ // Read in the element
+ refVar = Element( pObj, pGbl, &p, t, bCompatible );
+ while( refVar.is() && (*p == '.' || *p == '!') )
+ {
+ // It follows still an objectelement. The current element
+ // had to be a SBX-Object or had to deliver such an object!
+ pObj = dynamic_cast<SbxObject*>( refVar.get() );
+ if( !pObj )
+ // Then it had to deliver an object
+ pObj = dynamic_cast<SbxObject*>( refVar->GetObject() );
+ refVar.clear();
+ if( !pObj )
+ break;
+ p++;
+ // And the next element please
+ refVar = Element( pObj, pGbl, &p, t, bCompatible );
+ }
+ }
+ else
+ SbxBase::SetError( ERRCODE_BASIC_SYNTAX );
+ *ppBuf = p;
+ return refVar;
+}
+
+// Read in of an operand. This could be a number, a string or
+// a function (with optional parameters).
+
+static SbxVariableRef Operand
+ ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bVar, bool bCompatible )
+{
+ SbxVariableRef refVar( new SbxVariable );
+ const sal_Unicode* p = SkipWhitespace( *ppBuf );
+ if( !bVar && ( rtl::isAsciiDigit( *p )
+ || ( *p == '.' && rtl::isAsciiDigit( *( p+1 ) ) )
+ || *p == '-'
+ || *p == '&' ) )
+ {
+ // A number could be scanned in directly!
+ sal_uInt16 nLen;
+ if( !refVar->Scan( OUString( p ), &nLen ) )
+ {
+ refVar.clear();
+ }
+ else
+ {
+ p += nLen;
+ }
+ }
+ else if( !bVar && *p == '"' )
+ {
+ // A string
+ OUStringBuffer aString;
+ p++;
+ for( ;; )
+ {
+ // This is perhaps an error
+ if( !*p )
+ {
+ return nullptr;
+ }
+ // Double quotes are OK
+ if( *p == '"' && (*++p) != '"' )
+ {
+ break;
+ }
+ aString.append(*p++);
+ }
+ refVar->PutString( aString.makeStringAndClear() );
+ }
+ else
+ {
+ refVar = QualifiedName( pObj, pGbl, &p, SbxClassType::DontCare, bCompatible );
+ }
+ *ppBuf = p;
+ return refVar;
+}
+
+// Read in of a simple term. The operands +, -, * and /
+// are supported.
+
+static SbxVariableRef MulDiv( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
+{
+ const sal_Unicode* p = *ppBuf;
+ SbxVariableRef refVar( Operand( pObj, pGbl, &p, false, bCompatible ) );
+ p = SkipWhitespace( p );
+ while( refVar.is() && ( *p == '*' || *p == '/' ) )
+ {
+ sal_Unicode cOp = *p++;
+ SbxVariableRef refVar2( Operand( pObj, pGbl, &p, false, bCompatible ) );
+ if( refVar2.is() )
+ {
+ // temporary variable!
+ SbxVariable* pVar = refVar.get();
+ pVar = new SbxVariable( *pVar );
+ refVar = pVar;
+ if( cOp == '*' )
+ *refVar *= *refVar2;
+ else
+ *refVar /= *refVar2;
+ }
+ else
+ {
+ refVar.clear();
+ break;
+ }
+ }
+ *ppBuf = p;
+ return refVar;
+}
+
+static SbxVariableRef PlusMinus( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
+{
+ const sal_Unicode* p = *ppBuf;
+ SbxVariableRef refVar( MulDiv( pObj, pGbl, &p, bCompatible ) );
+ p = SkipWhitespace( p );
+ while( refVar.is() && ( *p == '+' || *p == '-' ) )
+ {
+ sal_Unicode cOp = *p++;
+ SbxVariableRef refVar2( MulDiv( pObj, pGbl, &p, bCompatible ) );
+ if( refVar2.is() )
+ {
+ // temporary Variable!
+ SbxVariable* pVar = refVar.get();
+ pVar = new SbxVariable( *pVar );
+ refVar = pVar;
+ if( cOp == '+' )
+ *refVar += *refVar2;
+ else
+ *refVar -= *refVar2;
+ }
+ else
+ {
+ refVar.clear();
+ break;
+ }
+ }
+ *ppBuf = p;
+ return refVar;
+}
+
+static SbxVariableRef Assign( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
+{
+ const sal_Unicode* p = *ppBuf;
+ SbxVariableRef refVar( Operand( pObj, pGbl, &p, true, bCompatible ) );
+ p = SkipWhitespace( p );
+ if( refVar.is() )
+ {
+ if( *p == '=' )
+ {
+ // Assign only onto properties!
+ if( refVar->GetClass() != SbxClassType::Property )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_BAD_ACTION );
+ refVar.clear();
+ }
+ else
+ {
+ p++;
+ SbxVariableRef refVar2( PlusMinus( pObj, pGbl, &p, bCompatible ) );
+ if( refVar2.is() )
+ {
+ SbxVariable* pVar = refVar.get();
+ SbxVariable* pVar2 = refVar2.get();
+ *pVar = *pVar2;
+ pVar->SetParameters( nullptr );
+ }
+ }
+ }
+ else
+ // Simple call: once activating
+ refVar->Broadcast( SfxHintId::BasicDataWanted );
+ }
+ *ppBuf = p;
+ return refVar;
+}
+
+// Read in of an element. This is a symbol, optional followed
+// by a parameter list. The symbol will be searched in the
+// specified object and the parameter list will be attached if necessary.
+
+static SbxVariableRef Element
+ ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf,
+ SbxClassType t, bool bCompatible )
+{
+ OUString aSym;
+ const sal_Unicode* p = Symbol( *ppBuf, aSym, bCompatible );
+ SbxVariableRef refVar;
+ if( !aSym.isEmpty() )
+ {
+ SbxFlagBits nOld = pObj->GetFlags();
+ if( pObj == pGbl )
+ {
+ pObj->SetFlag( SbxFlagBits::GlobalSearch );
+ }
+ refVar = pObj->Find( aSym, t );
+ pObj->SetFlags( nOld );
+ if( refVar.is() )
+ {
+ refVar->SetParameters( nullptr );
+ // Follow still parameter?
+ p = SkipWhitespace( p );
+ if( *p == '(' )
+ {
+ p++;
+ auto refPar = tools::make_ref<SbxArray>();
+ sal_uInt32 nArg = 0;
+ // We are once relaxed and accept as well
+ // the line- or command end as delimiter
+ // Search parameter always global!
+ while( *p && *p != ')' && *p != ']' )
+ {
+ SbxVariableRef refArg = PlusMinus( pGbl, pGbl, &p, bCompatible );
+ if( !refArg.is() )
+ {
+ // Error during the parsing
+ refVar.clear(); break;
+ }
+ else
+ {
+ // One copies the parameter, so that
+ // one have the current status (triggers also
+ // the call per access)
+ refPar->Put(new SbxVariable(*refArg), ++nArg);
+ }
+ p = SkipWhitespace( p );
+ if( *p == ',' )
+ p++;
+ }
+ if( *p == ')' )
+ p++;
+ if( refVar.is() )
+ refVar->SetParameters( refPar.get() );
+ }
+ }
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_METHOD, aSym );
+ }
+ *ppBuf = p;
+ return refVar;
+}
+
+// Mainroutine
+
+SbxVariable* SbxObject::Execute( const OUString& rTxt )
+{
+ SbxVariableRef pVar;
+ const sal_Unicode* p = rTxt.getStr();
+ for( ;; )
+ {
+ p = SkipWhitespace( p );
+ if( !*p )
+ {
+ break;
+ }
+ if( *p++ != '[' )
+ {
+ SetError( ERRCODE_BASIC_SYNTAX ); break;
+ }
+ pVar = Assign( this, this, &p, IsOptionCompatible() );
+ if( !pVar.is() )
+ {
+ break;
+ }
+ p = SkipWhitespace( p );
+ if( *p++ != ']' )
+ {
+ SetError( ERRCODE_BASIC_SYNTAX ); break;
+ }
+ }
+ return pVar.get();
+}
+
+SbxVariable* SbxObject::FindQualified( const OUString& rName, SbxClassType t )
+{
+ SbxVariableRef pVar;
+ const sal_Unicode* p = rName.getStr();
+ p = SkipWhitespace( p );
+ if( !*p )
+ {
+ return nullptr;
+ }
+ pVar = QualifiedName( this, this, &p, t, IsOptionCompatible() );
+ p = SkipWhitespace( p );
+ if( *p )
+ {
+ SetError( ERRCODE_BASIC_SYNTAX );
+ }
+ return pVar.get();
+}
+
+bool SbxObject::IsOptionCompatible() const
+{
+ if (const SbxObject* pObj = GetParent())
+ return pObj->IsOptionCompatible();
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxform.cxx b/basic/source/sbx/sbxform.cxx
new file mode 100644
index 0000000000..84e5f22fd8
--- /dev/null
+++ b/basic/source/sbx/sbxform.cxx
@@ -0,0 +1,1003 @@
+/* -*- 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 <sbxform.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <rtl/character.hxx>
+#include <o3tl/sprintf.hxx>
+#include <o3tl/string_view.hxx>
+#include <string_view>
+#include <utility>
+
+/*
+TODO: are there any Star-Basic characteristics unconsidered?
+
+ what means: * as placeholder
+
+COMMENT: Visual-Basic treats the following (invalid) format-strings
+ as shown:
+
+ ##0##.##0## --> ##000.000##
+
+ (this class behaves the same way)
+*/
+
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+
+#define NO_DIGIT_ -1
+
+#define MAX_NO_OF_DIGITS DBL_DIG
+#define MAX_DOUBLE_BUFFER_LENGTH MAX_NO_OF_DIGITS + 9
+ // +1 for leading sign
+ // +1 for digit before the decimal point
+ // +1 for decimal point
+ // +2 for exponent E and exp. leading sign
+ // +3 for the exponent's value
+ // +1 for closing 0
+
+#define CREATE_1000SEP_CHAR '@'
+
+#define FORMAT_SEPARATOR ';'
+
+// predefined formats for the Format$()-command:
+constexpr OUString BASICFORMAT_GENERALNUMBER = u"General Number"_ustr;
+constexpr OUString BASICFORMAT_CURRENCY = u"Currency"_ustr;
+constexpr OUString BASICFORMAT_FIXED = u"Fixed"_ustr;
+constexpr OUString BASICFORMAT_STANDARD = u"Standard"_ustr;
+constexpr OUString BASICFORMAT_PERCENT = u"Percent"_ustr;
+constexpr OUString BASICFORMAT_SCIENTIFIC = u"Scientific"_ustr;
+constexpr OUString BASICFORMAT_YESNO = u"Yes/No"_ustr;
+constexpr OUString BASICFORMAT_TRUEFALSE = u"True/False"_ustr;
+constexpr OUString BASICFORMAT_ONOFF = u"On/Off"_ustr;
+
+// Comment: Visual-Basic has a maximum of 12 positions after the
+// decimal point for floating-point-numbers.
+// all format-strings are compatible to Visual-Basic:
+constexpr OUStringLiteral GENERALNUMBER_FORMAT = u"0.############";
+constexpr OUStringLiteral FIXED_FORMAT = u"0.00";
+constexpr OUStringLiteral STANDARD_FORMAT = u"@0.00";
+constexpr OUStringLiteral PERCENT_FORMAT = u"0.00%";
+constexpr OUStringLiteral SCIENTIFIC_FORMAT = u"#.00E+00";
+// Comment: the character @ means that thousand-separators shall
+// be generated. That's a StarBasic 'extension'.
+
+
+static double get_number_of_digits( double dNumber )
+//double floor_log10_fabs( double dNumber )
+{
+ if( dNumber==0.0 )
+ return 0.0; // used to be 1.0, now 0.0 because of #40025;
+ else
+ return floor( log10( fabs( dNumber ) ) );
+}
+
+
+SbxBasicFormater::SbxBasicFormater( sal_Unicode _cDecPoint, sal_Unicode _cThousandSep,
+ OUString _sOnStrg,
+ OUString _sOffStrg,
+ OUString _sYesStrg,
+ OUString _sNoStrg,
+ OUString _sTrueStrg,
+ OUString _sFalseStrg,
+ OUString _sCurrencyStrg,
+ OUString _sCurrencyFormatStrg )
+ : cDecPoint(_cDecPoint)
+ , cThousandSep(_cThousandSep)
+ , sOnStrg(std::move(_sOnStrg))
+ , sOffStrg(std::move(_sOffStrg))
+ , sYesStrg(std::move(_sYesStrg))
+ , sNoStrg(std::move(_sNoStrg))
+ , sTrueStrg(std::move(_sTrueStrg))
+ , sFalseStrg(std::move(_sFalseStrg))
+ , sCurrencyStrg(std::move(_sCurrencyStrg))
+ , sCurrencyFormatStrg(std::move(_sCurrencyFormatStrg))
+ , dNum(0.0)
+ , nNumExp(0)
+ , nExpExp(0)
+{
+}
+
+// function to output an error-text (for debugging)
+// displaces all characters of the string, starting from nStartPos
+// for one position to larger indexes, i. e. place for a new
+// character (which is to be inserted) is created.
+// ATTENTION: the string MUST be long enough!
+inline void SbxBasicFormater::ShiftString( OUStringBuffer& sStrg, sal_uInt16 nStartPos )
+{
+ sStrg.remove(nStartPos,1);
+}
+
+void SbxBasicFormater::AppendDigit( OUStringBuffer& sStrg, short nDigit )
+{
+ if( nDigit>=0 && nDigit<=9 )
+ {
+ sStrg.append(static_cast<sal_Unicode>(nDigit+'0'));
+ }
+}
+
+void SbxBasicFormater::LeftShiftDecimalPoint( OUStringBuffer& sStrg )
+{
+ sal_Int32 nPos = -1;
+
+ for(sal_Int32 i = 0; i < sStrg.getLength(); i++)
+ {
+ if(sStrg[i] == cDecPoint)
+ {
+ nPos = i;
+ break;
+ }
+ }
+ if( nPos >= 0 )
+ {
+ sStrg[nPos] = sStrg[nPos - 1];
+ sStrg[nPos - 1] = cDecPoint;
+ }
+}
+
+// returns a flag if rounding a 9
+void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos, bool& bOverflow )
+{
+ if( nPos<0 )
+ {
+ return;
+ }
+ bOverflow = false;
+ sal_Unicode c = sStrg[nPos];
+ if( nPos > 0 && (c == cDecPoint || c == cThousandSep) )
+ {
+ StrRoundDigit( sStrg, nPos - 1, bOverflow );
+ // CHANGE from 9.3.1997: end the method immediately after recursive call!
+ return;
+ }
+ // skip non-digits:
+ // COMMENT:
+ // in a valid format-string the number's output should be done
+ // in one piece, i. e. special characters should ONLY be in
+ // front OR behind the number and not right in the middle of
+ // the format information for the number
+ while( nPos >= 0 && ! rtl::isAsciiDigit(sStrg[nPos]))
+ {
+ nPos--;
+ }
+ if( nPos==-1 )
+ {
+ ShiftString( sStrg, 0 );
+ sStrg[0] = '1';
+ bOverflow = true;
+ }
+ else
+ {
+ sal_Unicode c2 = sStrg[nPos];
+ if( rtl::isAsciiDigit(c2) )
+ {
+ if( c2 == '9' )
+ {
+ sStrg[nPos] = '0';
+ StrRoundDigit( sStrg, nPos - 1, bOverflow );
+ }
+ else
+ {
+ sStrg[nPos] = c2 + 1;
+ }
+ }
+ else
+ {
+ ShiftString( sStrg,nPos+1 );
+ sStrg[nPos + 1] = '1';
+ bOverflow = true;
+ }
+ }
+}
+
+void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos )
+{
+ bool bOverflow;
+
+ StrRoundDigit( sStrg, nPos, bOverflow );
+}
+
+void SbxBasicFormater::ParseBack( OUStringBuffer& sStrg, std::u16string_view sFormatStrg,
+ short nFormatPos )
+{
+ for( sal_Int32 i = nFormatPos;
+ i>0 && sFormatStrg[ i ] == '#' && sStrg[sStrg.getLength() - 1] == '0';
+ i-- )
+ {
+ sStrg.setLength(sStrg.getLength() - 1 );
+ }
+}
+
+void SbxBasicFormater::InitScan( double _dNum )
+{
+ char sBuffer[ MAX_DOUBLE_BUFFER_LENGTH ];
+
+ dNum = _dNum;
+ InitExp( get_number_of_digits( dNum ) );
+ // maximum of 15 positions behind the decimal point, example: -1.234000000000000E-001
+ /*int nCount =*/ o3tl::sprintf( sBuffer,"%+22.15lE",dNum );
+ sSciNumStrg = OUString::createFromAscii( sBuffer );
+}
+
+
+void SbxBasicFormater::InitExp( double _dNewExp )
+{
+ nNumExp = static_cast<short>(_dNewExp);
+ sNumExpStrg = (nNumExp >= 0 ? std::u16string_view(u"+") : std::u16string_view(u""))
+ + OUString::number(nNumExp);
+ nExpExp = static_cast<short>(get_number_of_digits( static_cast<double>(nNumExp) ));
+}
+
+
+short SbxBasicFormater::GetDigitAtPosScan( short nPos, bool& bFoundFirstDigit )
+{
+ // trying to read a higher digit,
+ // e. g. position 4 in 1.234,
+ // or to read a digit outside of the
+ // number's dissolution (double)
+ if( nPos>nNumExp || abs(nNumExp-nPos)>MAX_NO_OF_DIGITS )
+ {
+ return NO_DIGIT_;
+ }
+ // determine the index of the position in the number-string:
+ // skip the leading sign
+ sal_uInt16 no = 1;
+ // skip the decimal point if necessary
+ if( nPos<nNumExp )
+ no++;
+ no += nNumExp-nPos;
+ // query of the number's first valid digit --> set flag
+ if( nPos==nNumExp )
+ bFoundFirstDigit = true;
+ return static_cast<short>(sSciNumStrg[ no ] - '0');
+}
+
+short SbxBasicFormater::GetDigitAtPosExpScan( short nPos, bool& bFoundFirstDigit )
+{
+ if( nPos>nExpExp )
+ return -1;
+
+ sal_uInt16 no = 1;
+ no += nExpExp-nPos;
+
+ if( nPos==nExpExp )
+ bFoundFirstDigit = true;
+ return static_cast<short>(sNumExpStrg[ no ] - '0');
+}
+
+// a value for the exponent can be given because the number maybe shall
+// not be displayed in a normed way (e. g. 1.2345e-03) but maybe 123.345e-3 !
+short SbxBasicFormater::GetDigitAtPosExpScan( double dNewExponent, short nPos,
+ bool& bFoundFirstDigit )
+{
+ InitExp( dNewExponent );
+
+ return GetDigitAtPosExpScan( nPos,bFoundFirstDigit );
+}
+
+// Copies the respective part of the format-string, if existing, and returns it.
+// So a new string is created, which has to be freed by the caller later.
+OUString SbxBasicFormater::GetPosFormatString( std::u16string_view sFormatStrg, bool & bFound )
+{
+ bFound = false; // default...
+ size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
+
+ if( nPos != std::u16string_view::npos )
+ {
+ bFound = true;
+ // the format-string for positive numbers is
+ // everything before the first ';'
+ return OUString(sFormatStrg.substr( 0,nPos ));
+ }
+
+ return OUString();
+}
+
+// see also GetPosFormatString()
+OUString SbxBasicFormater::GetNegFormatString( std::u16string_view sFormatStrg, bool & bFound )
+{
+ bFound = false; // default...
+ size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
+
+ if( nPos != std::u16string_view::npos)
+ {
+ // the format-string for negative numbers is
+ // everything between the first and the second ';'
+ std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
+ nPos = sTempStrg.find( FORMAT_SEPARATOR );
+ bFound = true;
+ if( nPos == std::u16string_view::npos )
+ {
+ return OUString(sTempStrg);
+ }
+ else
+ {
+ return OUString(sTempStrg.substr( 0,nPos ));
+ }
+ }
+ return OUString();
+}
+
+// see also GetPosFormatString()
+OUString SbxBasicFormater::Get0FormatString( std::u16string_view sFormatStrg, bool & bFound )
+{
+ bFound = false; // default...
+ size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
+
+ if( nPos != std::u16string_view::npos )
+ {
+ // the format string for the zero is
+ // everything after the second ';'
+ std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
+ nPos = sTempStrg.find( FORMAT_SEPARATOR );
+ if( nPos != std::u16string_view::npos )
+ {
+ bFound = true;
+ sTempStrg = sTempStrg.substr( nPos+1 );
+ nPos = sTempStrg.find( FORMAT_SEPARATOR );
+ if( nPos == std::u16string_view::npos )
+ {
+ return OUString(sTempStrg);
+ }
+ else
+ {
+ return OUString(sTempStrg.substr( 0,nPos ));
+ }
+ }
+ }
+
+ return OUString();
+}
+
+// see also GetPosFormatString()
+OUString SbxBasicFormater::GetNullFormatString( std::u16string_view sFormatStrg, bool & bFound )
+{
+ bFound = false; // default...
+ size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
+
+ if( nPos != std::u16string_view::npos )
+ {
+ // the format-string for the Null is
+ // everything after the third ';'
+ std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
+ nPos = sTempStrg.find( FORMAT_SEPARATOR );
+ if( nPos != std::u16string_view::npos )
+ {
+ sTempStrg = sTempStrg.substr( nPos+1 );
+ nPos = sTempStrg.find( FORMAT_SEPARATOR );
+ if( nPos != std::u16string_view::npos )
+ {
+ bFound = true;
+ return OUString(sTempStrg.substr( nPos+1 ));
+ }
+ }
+ }
+
+ return OUString();
+}
+
+// returns value <> 0 in case of an error
+void SbxBasicFormater::AnalyseFormatString( std::u16string_view sFormatStrg,
+ short& nNoOfDigitsLeft, short& nNoOfDigitsRight,
+ short& nNoOfOptionalDigitsLeft,
+ short& nNoOfExponentDigits, short& nNoOfOptionalExponentDigits,
+ bool& bPercent, bool& bCurrency, bool& bScientific,
+ bool& bGenerateThousandSeparator,
+ short& nMultipleThousandSeparators )
+{
+ sal_Int32 nLen;
+ short nState = 0;
+
+ nLen = sFormatStrg.size();
+ nNoOfDigitsLeft = 0;
+ nNoOfDigitsRight = 0;
+ nNoOfOptionalDigitsLeft = 0;
+ nNoOfExponentDigits = 0;
+ nNoOfOptionalExponentDigits = 0;
+ bPercent = false;
+ bCurrency = false;
+ bScientific = false;
+ // from 11.7.97: as soon as a comma (point?) is found in the format string,
+ // all three decimal powers are marked (i. e. thousand, million, ...)
+ bGenerateThousandSeparator = sFormatStrg.find( ',' ) != std::u16string_view::npos;
+ nMultipleThousandSeparators = 0;
+
+ for( sal_Int32 i = 0; i < nLen; i++ )
+ {
+ sal_Unicode c = sFormatStrg[ i ];
+ switch( c )
+ {
+ case '#':
+ case '0':
+ if( nState==0 )
+ {
+ nNoOfDigitsLeft++;
+// TODO here maybe better error inspection of the mantissa for valid syntax (see grammar)h
+ // ATTENTION: 'undefined' behaviour if # and 0 are combined!
+ // REMARK: #-placeholders are actually useless for
+ // scientific display before the decimal point!
+ if( c=='#' )
+ {
+ nNoOfOptionalDigitsLeft++;
+ }
+ }
+ else if( nState==1 )
+ {
+ nNoOfDigitsRight++;
+ }
+ else if( nState==-1 ) // search 0 in the exponent
+ {
+ if( c=='#' ) // # switches on the condition
+ {
+ nNoOfOptionalExponentDigits++;
+ nState = -2;
+ }
+ nNoOfExponentDigits++;
+ }
+ else if( nState==-2 ) // search # in the exponent
+ {
+ if( c=='0' )
+ {
+ // ERROR: 0 after # in the exponent is NOT allowed!!
+ return;
+ }
+ nNoOfOptionalExponentDigits++;
+ nNoOfExponentDigits++;
+ }
+ break;
+ case '.':
+ nState++;
+ if( nState>1 )
+ {
+ return; // ERROR: too many decimal points
+ }
+ break;
+ case '%':
+ bPercent = true;
+ break;
+ case '(':
+ bCurrency = true;
+ break;
+ case ',':
+ {
+ sal_Unicode ch = sFormatStrg[ i+1 ];
+
+ if( ch!=0 && (ch==',' || ch=='.') )
+ {
+ nMultipleThousandSeparators++;
+ }
+ }
+ break;
+ case 'e':
+ case 'E':
+ // #i13821 not when no digits before
+ if( nNoOfDigitsLeft > 0 || nNoOfDigitsRight > 0 )
+ {
+ nState = -1; // abort counting digits
+ bScientific = true;
+ }
+ break;
+ // OWN command-character which turns on
+ // the creation of thousand-separators
+ case '\\':
+ // Ignore next char
+ i++;
+ break;
+ case CREATE_1000SEP_CHAR:
+ bGenerateThousandSeparator = true;
+ break;
+ }
+ }
+}
+
+// the flag bCreateSign says that at the mantissa a leading sign
+// shall be created
+void SbxBasicFormater::ScanFormatString( double dNumber,
+ std::u16string_view sFormatStrg, OUString& sReturnStrgFinal,
+ bool bCreateSign )
+{
+ short /*nErr,*/nNoOfDigitsLeft,nNoOfDigitsRight,nNoOfOptionalDigitsLeft,
+ nNoOfExponentDigits,nNoOfOptionalExponentDigits,
+ nMultipleThousandSeparators;
+ bool bPercent,bCurrency,bScientific,bGenerateThousandSeparator;
+
+ OUStringBuffer sReturnStrg(32);
+
+ // analyse the format-string, i. e. determine the following values:
+ /*
+ - number of digits before decimal point
+ - number of digits after decimal point
+ - optional digits before decimal point
+ - number of digits in the exponent
+ - optional digits in the exponent
+ - percent-character found?
+ - () for negative leading sign?
+ - exponential-notation?
+ - shall thousand-separators be generated?
+ - is a percent-character being found? --> dNumber *= 100.0;
+ - are there thousand-separators in a row?
+ ,, or ,. --> dNumber /= 1000.0;
+ - other errors? multiple decimal points, E's, etc.
+ --> errors are simply ignored at the moment
+ */
+ AnalyseFormatString( sFormatStrg, nNoOfDigitsLeft, nNoOfDigitsRight,
+ nNoOfOptionalDigitsLeft, nNoOfExponentDigits,
+ nNoOfOptionalExponentDigits,
+ bPercent, bCurrency, bScientific,
+ bGenerateThousandSeparator, nMultipleThousandSeparators );
+ // special handling for special characters
+ if( bPercent )
+ {
+ dNumber *= 100.0;
+ }
+// TODO: this condition (,, or ,.) is NOT Visual-Basic compatible!
+ // Question: shall this stay here (requirements)?
+ if( nMultipleThousandSeparators )
+ {
+ dNumber /= 1000.0;
+ }
+ double dExponent;
+ short i,nLen;
+ short nState,nDigitPos,nExponentPos,nMaxDigit,nMaxExponentDigit;
+ bool bFirstDigit,bFirstExponentDigit,bFoundFirstDigit,
+ bIsNegative,bZeroSpaceOn, bSignHappend,bDigitPosNegative;
+
+ bSignHappend = false;
+ bFoundFirstDigit = false;
+ bIsNegative = dNumber < 0.0;
+ nLen = sFormatStrg.size();
+ dExponent = get_number_of_digits( dNumber );
+ nExponentPos = 0;
+ nMaxExponentDigit = 0;
+ nMaxDigit = static_cast<short>(dExponent);
+ bDigitPosNegative = false;
+ if( bScientific )
+ {
+ dExponent = dExponent - static_cast<double>(nNoOfDigitsLeft-1);
+ nDigitPos = nMaxDigit;
+ nMaxExponentDigit = static_cast<short>(get_number_of_digits( dExponent ));
+ nExponentPos = nNoOfExponentDigits - 1 - nNoOfOptionalExponentDigits;
+ }
+ else
+ {
+ nDigitPos = nNoOfDigitsLeft - 1; // counting starts at 0, 10^0
+ // no exponent-data is needed here!
+ bDigitPosNegative = (nDigitPos < 0);
+ }
+ bFirstDigit = true;
+ bFirstExponentDigit = true;
+ nState = 0; // 0 --> mantissa; 1 --> exponent
+ bZeroSpaceOn = false;
+
+
+ InitScan( dNumber );
+ // scanning the format-string:
+ sal_Unicode cForce = 0;
+ for( i = 0; i < nLen; i++ )
+ {
+ sal_Unicode c;
+ if( cForce )
+ {
+ c = cForce;
+ cForce = 0;
+ }
+ else
+ {
+ c = sFormatStrg[ i ];
+ }
+ switch( c )
+ {
+ case '0':
+ case '#':
+ if( nState==0 )
+ {
+ // handling of the mantissa
+ if( bFirstDigit )
+ {
+ // remark: at bCurrency the negative
+ // leading sign shall be shown with ()
+ if( bIsNegative && !bCreateSign && !bSignHappend )
+ {
+ bSignHappend = true;
+ sReturnStrg.append('-');
+ }
+ // output redundant positions, i. e. those which
+ // are undocumented by the format-string
+ if( nMaxDigit > nDigitPos )
+ {
+ for( short j = nMaxDigit; j > nDigitPos; j-- )
+ {
+ short nTempDigit = GetDigitAtPosScan( j, bFoundFirstDigit );
+ AppendDigit( sReturnStrg, nTempDigit );
+ if( nTempDigit != NO_DIGIT_ )
+ {
+ bFirstDigit = false;
+ }
+ if( bGenerateThousandSeparator && c=='0' && j > 0 && (j % 3 == 0) )
+ {
+ sReturnStrg.append(cThousandSep );
+ }
+ }
+ }
+ }
+
+ if( nMaxDigit<nDigitPos && ( c=='0' || bZeroSpaceOn ) )
+ {
+ AppendDigit( sReturnStrg, 0 );
+ bFirstDigit = false;
+ bZeroSpaceOn = true;
+ // Remark: in Visual-Basic the first 0 turns on the 0 for
+ // all the following # (up to the decimal point),
+ // this behaviour is simulated here with the flag.
+ if (bGenerateThousandSeparator && c == '0' && nDigitPos > 0 && (nDigitPos % 3 == 0))
+ {
+ sReturnStrg.append(cThousandSep);
+ }
+ }
+ else
+ {
+ short nTempDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit ) ;
+ AppendDigit( sReturnStrg, nTempDigit );
+
+ if( nTempDigit != NO_DIGIT_ )
+ {
+ bFirstDigit = false;
+ }
+ if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit>=nDigitPos ) && nDigitPos>0 && (nDigitPos % 3 == 0) )
+ {
+ sReturnStrg.append(cThousandSep);
+ }
+ }
+ nDigitPos--;
+ }
+ else
+ {
+ // handling the exponent
+ if( bFirstExponentDigit )
+ {
+ // leading sign has been given out at e/E already
+ bFirstExponentDigit = false;
+ if( nMaxExponentDigit > nExponentPos )
+ // output redundant positions, i. e. those which
+ // are undocumented by the format-string
+ {
+ for( short j = nMaxExponentDigit; j > nExponentPos; j-- )
+ {
+ AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, j, bFoundFirstDigit ) );
+ }
+ }
+ }
+
+ if( nMaxExponentDigit < nExponentPos && c=='0' )
+ {
+ AppendDigit( sReturnStrg, 0 );
+ }
+ else
+ {
+ AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, nExponentPos, bFoundFirstDigit ) );
+ }
+ nExponentPos--;
+ }
+ break;
+ case '.':
+ if( bDigitPosNegative ) // #i13821: If no digits before .
+ {
+ bDigitPosNegative = false;
+ nDigitPos = 0;
+ cForce = '#';
+ i-=2;
+ break;
+ }
+ sReturnStrg.append(cDecPoint);
+ break;
+ case '%':
+ // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
+ ParseBack( sReturnStrg, sFormatStrg, i-1 );
+ sReturnStrg.append('%');
+ break;
+ case 'e':
+ case 'E':
+ // does mantissa have to be rounded, before the exponent is displayed?
+ {
+ // is there a mantissa at all?
+ if( bFirstDigit )
+ {
+ // apparently not, i. e. invalid format string, e. g. E000.00
+ // so ignore these e and E characters
+ // maybe output an error (like in Visual Basic)?
+
+ // #i13821: VB 6 behaviour
+ sReturnStrg.append(c);
+ break;
+ }
+
+ bool bOverflow = false;
+ short nNextDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit );
+ if( nNextDigit>=5 )
+ {
+ StrRoundDigit( sReturnStrg, sReturnStrg.getLength() - 1, bOverflow );
+ }
+ if( bOverflow )
+ {
+ // a leading 9 has been rounded
+ LeftShiftDecimalPoint( sReturnStrg );
+ sReturnStrg[sReturnStrg.getLength() - 1] = 0;
+ dExponent += 1.0;
+ }
+ // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
+ ParseBack( sReturnStrg, sFormatStrg, i-1 );
+ }
+ // change the scanner's condition
+ nState++;
+ // output exponent character
+ sReturnStrg.append(c);
+ // i++; // MANIPULATION of the loop-variable!
+ c = sFormatStrg[ ++i ];
+ // output leading sign / exponent
+ if( c != 0 )
+ {
+ if( c == '-' )
+ {
+ if( dExponent < 0.0 )
+ {
+ sReturnStrg.append('-');
+ }
+ }
+ else if( c == '+' )
+ {
+ if( dExponent < 0.0 )
+ {
+ sReturnStrg.append('-');
+ }
+ else
+ {
+ sReturnStrg.append('+');
+ }
+ }
+ }
+ break;
+ case ',':
+ break;
+ case ';':
+ break;
+ case '(':
+ case ')':
+ // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
+ ParseBack( sReturnStrg, sFormatStrg, i-1 );
+ if( bIsNegative )
+ {
+ sReturnStrg.append(c);
+ }
+ break;
+ case '$':
+ // append the string for the currency:
+ sReturnStrg.append(sCurrencyStrg);
+ break;
+ case ' ':
+ case '-':
+ case '+':
+ ParseBack( sReturnStrg, sFormatStrg, i-1 );
+ sReturnStrg.append(c);
+ break;
+ case '\\':
+ ParseBack( sReturnStrg, sFormatStrg, i-1 );
+ // special character found, output next
+ // character directly (if existing)
+ c = sFormatStrg[ ++i ];
+ if( c!=0 )
+ {
+ sReturnStrg.append(c);
+ }
+ break;
+ case CREATE_1000SEP_CHAR:
+ // ignore here, action has already been
+ // executed in AnalyseFormatString
+ break;
+ default:
+ // output characters and digits, too (like in Visual-Basic)
+ if( ( c>='a' && c<='z' ) ||
+ ( c>='A' && c<='Z' ) ||
+ ( c>='1' && c<='9' ) )
+ {
+ sReturnStrg.append(c);
+ }
+ }
+ }
+
+ // scan completed - rounding necessary?
+ if( !bScientific )
+ {
+ short nNextDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit );
+ if( nNextDigit>=5 )
+ {
+ StrRoundDigit( sReturnStrg, sReturnStrg.getLength() - 1 );
+ }
+ }
+
+ if( nNoOfDigitsRight>0 )
+ {
+ ParseBack( sReturnStrg, sFormatStrg, sFormatStrg.size()-1 );
+ }
+ sReturnStrgFinal = sReturnStrg.makeStringAndClear();
+}
+
+OUString SbxBasicFormater::BasicFormatNull( std::u16string_view sFormatStrg )
+{
+ bool bNullFormatFound;
+ OUString sNullFormatStrg = GetNullFormatString( sFormatStrg, bNullFormatFound );
+
+ if( bNullFormatFound )
+ {
+ return sNullFormatStrg;
+ }
+ return "null";
+}
+
+OUString SbxBasicFormater::BasicFormat( double dNumber, const OUString& _sFormatStrg )
+{
+ bool bPosFormatFound,bNegFormatFound,b0FormatFound;
+ OUString sFormatStrg = _sFormatStrg;
+
+ // analyse format-string concerning predefined formats:
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_GENERALNUMBER ) )
+ {
+ sFormatStrg = GENERALNUMBER_FORMAT;
+ }
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_CURRENCY ) )
+ {
+ sFormatStrg = sCurrencyFormatStrg;
+ }
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_FIXED ) )
+ {
+ sFormatStrg = FIXED_FORMAT;
+ }
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_STANDARD ) )
+ {
+ sFormatStrg = STANDARD_FORMAT;
+ }
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_PERCENT ) )
+ {
+ sFormatStrg = PERCENT_FORMAT;
+ }
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_SCIENTIFIC ) )
+ {
+ sFormatStrg = SCIENTIFIC_FORMAT;
+ }
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_YESNO ) )
+ {
+ return ( dNumber==0.0 ) ? sNoStrg : sYesStrg ;
+ }
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_TRUEFALSE ) )
+ {
+ return ( dNumber==0.0 ) ? sFalseStrg : sTrueStrg ;
+ }
+ if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_ONOFF ) )
+ {
+ return ( dNumber==0.0 ) ? sOffStrg : sOnStrg ;
+ }
+
+ // analyse format-string concerning ';', i. e. format-strings for
+ // positive-, negative- and 0-values
+ OUString sPosFormatStrg = GetPosFormatString( sFormatStrg, bPosFormatFound );
+ OUString sNegFormatStrg = GetNegFormatString( sFormatStrg, bNegFormatFound );
+ OUString s0FormatStrg = Get0FormatString( sFormatStrg, b0FormatFound );
+
+ OUString sReturnStrg;
+ OUString sTempStrg;
+
+ if( dNumber==0.0 )
+ {
+ sTempStrg = sFormatStrg;
+ if( b0FormatFound )
+ {
+ if( s0FormatStrg.isEmpty() && bPosFormatFound )
+ {
+ sTempStrg = sPosFormatStrg;
+ }
+ else
+ {
+ sTempStrg = s0FormatStrg;
+ }
+ }
+ else if( bPosFormatFound )
+ {
+ sTempStrg = sPosFormatStrg;
+ }
+ ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/false );
+ }
+ else
+ {
+ if( dNumber<0.0 )
+ {
+ if( bNegFormatFound )
+ {
+ if( sNegFormatStrg.isEmpty() && bPosFormatFound )
+ {
+ sTempStrg = "-" + sPosFormatStrg;
+ }
+ else
+ {
+ sTempStrg = sNegFormatStrg;
+ }
+ }
+ else
+ {
+ sTempStrg = sFormatStrg;
+ }
+ // if NO format-string especially for negative
+ // values is given, output the leading sign
+ ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/bNegFormatFound/*sNegFormatStrg!=EMPTYFORMATSTRING*/ );
+ }
+ else // if( dNumber>0.0 )
+ {
+ ScanFormatString( dNumber,
+ (/*sPosFormatStrg!=EMPTYFORMATSTRING*/bPosFormatFound ? sPosFormatStrg : sFormatStrg),
+ sReturnStrg,/*bCreateSign=*/false );
+ }
+ }
+ return sReturnStrg;
+}
+
+bool SbxBasicFormater::isBasicFormat( std::u16string_view sFormatStrg )
+{
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_GENERALNUMBER ) )
+ {
+ return true;
+ }
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_CURRENCY ) )
+ {
+ return true;
+ }
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_FIXED ) )
+ {
+ return true;
+ }
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_STANDARD ) )
+ {
+ return true;
+ }
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_PERCENT ) )
+ {
+ return true;
+ }
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_SCIENTIFIC ) )
+ {
+ return true;
+ }
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_YESNO ) )
+ {
+ return true;
+ }
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_TRUEFALSE ) )
+ {
+ return true;
+ }
+ if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_ONOFF ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxint.cxx b/basic/source/sbx/sbxint.cxx
new file mode 100644
index 0000000000..1b57239ddb
--- /dev/null
+++ b/basic/source/sbx/sbxint.cxx
@@ -0,0 +1,822 @@
+/* -*- 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>
+
+#include <o3tl/safeint.hxx>
+#include <comphelper/errcode.hxx>
+#include <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+
+#include <rtl/math.hxx>
+
+sal_Int16 ImpGetInteger( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_Int16 nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar; break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ nRes = p->nInteger; break;
+ case SbxERROR:
+ case SbxUSHORT:
+ if( p->nUShort > o3tl::make_unsigned(SbxMAXINT) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXINT;
+ }
+ else
+ nRes = static_cast<sal_Int16>(p->nUShort);
+ break;
+ case SbxLONG:
+ if( p->nLong > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXINT;
+ }
+ else if( p->nLong < SbxMININT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMININT;
+ }
+ else
+ nRes = static_cast<sal_Int16>(p->nLong);
+ break;
+ case SbxULONG:
+ if( p->nULong > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXINT;
+ }
+ else
+ nRes = static_cast<sal_Int16>(p->nULong);
+ break;
+ case SbxSINGLE:
+ nRes = ImpDoubleToInteger(p->nSingle);
+ break;
+ case SbxCURRENCY:
+ {
+ sal_Int64 tstVal = p->nInt64 / sal_Int64(CURRENCY_FACTOR);
+
+ if( tstVal > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXINT;
+ }
+ else if( tstVal < SbxMININT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMININT;
+ }
+ else
+ nRes = static_cast<sal_Int16>(tstVal);
+ break;
+ }
+ case SbxSALINT64:
+ if( p->nInt64 > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXINT;
+ }
+ else if( p->nInt64 < SbxMININT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMININT;
+ }
+ else
+ nRes = static_cast<sal_Int16>(p->nInt64);
+ break;
+ case SbxSALUINT64:
+ if( p->uInt64 > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXINT;
+ }
+ else
+ nRes = static_cast<sal_Int16>(p->uInt64);
+ break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ double dVal = 0.0;
+ if( p->eType == SbxDECIMAL )
+ {
+ if( p->pDecimal )
+ p->pDecimal->getDouble( dVal );
+ }
+ else
+ dVal = p->nDouble;
+
+ nRes = ImpDoubleToInteger(dVal);
+ break;
+ }
+ case SbxLPSTR:
+ case SbxSTRING:
+ case SbxBYREF | SbxSTRING:
+ if( !p->pOUString )
+ nRes = 0;
+ else
+ {
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else
+ nRes = ImpDoubleToInteger(d);
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetInteger();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar; break;
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte; break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ nRes = *p->pInteger; break;
+
+ // from here had to be tested
+ case SbxBYREF | SbxLONG:
+ aTmp.nLong = *p->pLong; goto ref;
+ case SbxBYREF | SbxULONG:
+ aTmp.nULong = *p->pULong; goto ref;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ aTmp.nUShort = *p->pUShort; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutInteger( SbxValues* p, sal_Int16 n )
+{
+ SbxValues aTmp;
+start:
+ switch( +p->eType )
+ {
+ // here had to be tested
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ case SbxSALUINT64:
+ aTmp.puInt64 = &p->uInt64; goto direct;
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ // from here no tests needed
+ case SbxINTEGER:
+ case SbxBOOL:
+ p->nInteger = n; break;
+ case SbxLONG:
+ p->nLong = n; break;
+ case SbxSINGLE:
+ p->nSingle = n; break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+ case SbxCURRENCY:
+ p->nInt64 = n * CURRENCY_FACTOR; break;
+ case SbxSALINT64:
+ p->nInt64 = n; break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ ImpCreateDecimal( p )->setInt( n );
+ break;
+
+ case SbxLPSTR:
+ case SbxSTRING:
+ case SbxBYREF | SbxSTRING:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ ImpCvtNum( static_cast<double>(n), 0, *p->pOUString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutInteger( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ if( n < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINCHAR;
+ }
+ *p->pChar = static_cast<char>(n); break;
+ case SbxBYREF | SbxBYTE:
+ if( n > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXBYTE;
+ }
+ else if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pByte = static_cast<sal_uInt8>(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ *p->pInteger = n; break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pUShort = static_cast<sal_uInt16>(n); break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = static_cast<sal_Int32>(n); break;
+ case SbxBYREF | SbxULONG:
+ if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pULong = static_cast<sal_uInt32>(n); break;
+ case SbxBYREF | SbxCURRENCY:
+ *p->pnInt64 = n * CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = n; break;
+ case SbxBYREF | SbxSALUINT64:
+ if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ *p->puInt64 = 0;
+ }
+ else
+ *p->puInt64 = n;
+ break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = static_cast<float>(n); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = static_cast<double>(n); break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+
+// sal_Int64 / hyper
+
+double ImpSalUInt64ToDouble( sal_uInt64 n )
+{
+ double d = 0.0;
+ if( n > SAL_MAX_INT64 )
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ else
+ d = static_cast<double>(static_cast<sal_Int64>(n));
+ return d;
+}
+
+
+sal_Int64 ImpGetInt64( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_Int64 nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar; break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ nRes = p->nInteger; break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = static_cast<sal_Int64>(p->nUShort); break;
+ case SbxLONG:
+ nRes = static_cast<sal_Int64>(p->nLong); break;
+ case SbxULONG:
+ nRes = static_cast<sal_Int64>(p->nULong); break;
+ case SbxSINGLE:
+ nRes = ImpDoubleToSalInt64(p->nSingle);
+ break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ nRes = ImpDoubleToSalInt64(p->nDouble);
+ break;
+ case SbxCURRENCY:
+ nRes = p->nInt64 / CURRENCY_FACTOR; break;
+ case SbxSALINT64:
+ nRes = p->nInt64; break;
+ case SbxSALUINT64:
+ if( p->uInt64 > SAL_MAX_INT64 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MAX_INT64;
+ }
+ else
+ nRes = static_cast<sal_Int64>(p->uInt64);
+ break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ nRes = 0;
+ else
+ {
+ nRes = p->pOUString->toInt64();
+ if( nRes == 0 )
+ {
+ // Check if really 0 or invalid conversion
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else
+ nRes = ImpDoubleToSalInt64(d);
+ }
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetInt64();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar; break;
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte; break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ nRes = *p->pInteger; break;
+ case SbxBYREF | SbxLONG:
+ nRes = *p->pLong; break;
+ case SbxBYREF | SbxULONG:
+ nRes = *p->pULong; break;
+ case SbxBYREF | SbxCURRENCY:
+ nRes = p->nInt64 / CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSALINT64:
+ nRes = *p->pnInt64; break;
+
+ // from here the values has to be checked
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ aTmp.nUShort = *p->pUShort; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutInt64( SbxValues* p, sal_Int64 n )
+{
+ SbxValues aTmp;
+
+start:
+ switch( +p->eType )
+ {
+ // Check necessary
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ case SbxLONG:
+ aTmp.pnInt64 = &p->nInt64; goto direct;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ aTmp.pnInt64 = &p->nInt64; goto direct;
+ case SbxSALUINT64:
+ aTmp.puInt64 = &p->uInt64; goto direct;
+
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ case SbxSINGLE:
+ p->nSingle = static_cast<float>(n); break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = static_cast<double>(n); break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ {
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+
+ (*p->pOUString) = OUString::number(n);
+ break;
+ }
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutInt64( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ if( n > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXCHAR;
+ }
+ else if( n < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINCHAR;
+ }
+ *p->pChar = static_cast<sal_Unicode>(n); break;
+ case SbxBYREF | SbxBYTE:
+ if( n > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXBYTE;
+ }
+ else if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pByte = static_cast<sal_uInt8>(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ if( n > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXINT;
+ }
+ else if( n < SbxMININT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMININT;
+ }
+ *p->pInteger = static_cast<sal_Int16>(n); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ if( n > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXUINT;
+ }
+ else if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pUShort = static_cast<sal_uInt16>(n); break;
+ case SbxBYREF | SbxLONG:
+ if( n > SbxMAXLNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXLNG;
+ }
+ else if( n < SbxMINLNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINLNG;
+ }
+ *p->pLong = static_cast<sal_Int32>(n); break;
+ case SbxBYREF | SbxULONG:
+ if( n > SbxMAXULNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXULNG;
+ }
+ else if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pULong = static_cast<sal_uInt32>(n); break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = static_cast<float>(n); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = static_cast<double>(n); break;
+ case SbxBYREF | SbxCURRENCY:
+ *p->pnInt64 = n * CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = n; break;
+ case SbxBYREF | SbxSALUINT64:
+ if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->puInt64 = n; break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+sal_uInt64 ImpGetUInt64( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_uInt64 nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar; break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ nRes = p->nInteger; break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = p->nUShort; break;
+ case SbxLONG:
+ nRes = p->nLong; break;
+ case SbxULONG:
+ nRes = static_cast<sal_uInt64>(p->nULong); break;
+ case SbxSINGLE:
+ nRes = ImpDoubleToSalUInt64(p->nSingle); break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ nRes = ImpDoubleToSalUInt64(p->nDouble);
+ break;
+ case SbxCURRENCY:
+ nRes = p->nInt64 * CURRENCY_FACTOR; break;
+ case SbxSALINT64:
+ if( p->nInt64 < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt64>(p->nInt64);
+ break;
+ case SbxSALUINT64:
+ nRes = p->uInt64; break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ nRes = 0;
+ else
+ {
+ nRes = p->pOUString->toUInt64();
+ if( nRes == 0 )
+ {
+ // Check if really 0 or invalid conversion
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else
+ nRes = ImpDoubleToSalUInt64(d);
+ }
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetUInt64();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar; break;
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte; break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ nRes = *p->pInteger; break;
+ case SbxBYREF | SbxLONG:
+ nRes = *p->pLong; break;
+ case SbxBYREF | SbxULONG:
+ nRes = *p->pULong; break;
+ case SbxBYREF | SbxSALUINT64:
+ nRes = *p->puInt64; break;
+
+ // from here on the value has to be checked
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ aTmp.nUShort = *p->pUShort; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutUInt64( SbxValues* p, sal_uInt64 n )
+{
+ SbxValues aTmp;
+
+start:
+ switch( +p->eType )
+ {
+ // Check necessary
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ case SbxLONG:
+ aTmp.pnInt64 = &p->nInt64; goto direct;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ aTmp.pnInt64 = &p->nInt64; goto direct;
+ case SbxSINGLE:
+ aTmp.pSingle = &p->nSingle; goto direct;
+ case SbxDATE:
+ case SbxDOUBLE:
+ aTmp.pDouble = &p->nDouble; goto direct;
+
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ // Check not necessary
+ case SbxSALUINT64:
+ p->uInt64 = n; break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ if( n > SAL_MAX_INT64 )
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ else
+ {
+ (*p->pOUString) = OUString::number(n);
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutUInt64( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ if( n > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXCHAR;
+ }
+ *p->pChar = static_cast<sal_Unicode>(n); break;
+ case SbxBYREF | SbxBYTE:
+ if( n > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXBYTE;
+ }
+ *p->pByte = static_cast<sal_uInt8>(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ if( n > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXINT;
+ }
+ *p->pInteger = static_cast<sal_Int16>(n); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ if( n > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXUINT;
+ }
+ *p->pUShort = static_cast<sal_uInt16>(n); break;
+ case SbxBYREF | SbxLONG:
+ if( n > SbxMAXLNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXLNG;
+ }
+ *p->pLong = static_cast<sal_Int32>(n); break;
+ case SbxBYREF | SbxULONG:
+ if( n > SbxMAXULNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXULNG;
+ }
+ *p->pULong = static_cast<sal_uInt32>(n); break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pDouble = static_cast<float>(ImpSalUInt64ToDouble( n )); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+
+ *p->pDouble = ImpSalUInt64ToDouble( n ); break;
+ case SbxBYREF | SbxCURRENCY:
+ if ( n > ( SAL_MAX_INT64 / CURRENCY_FACTOR ) )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ n = SAL_MAX_INT64;
+ }
+ *p->pnInt64 = static_cast<sal_Int64>( n * CURRENCY_FACTOR ); break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = n; break;
+ case SbxBYREF | SbxSALINT64:
+ if( n > SAL_MAX_INT64 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pnInt64 = static_cast<sal_Int64>(n); break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxlng.cxx b/basic/source/sbx/sbxlng.cxx
new file mode 100644
index 0000000000..e129406e2f
--- /dev/null
+++ b/basic/source/sbx/sbxlng.cxx
@@ -0,0 +1,292 @@
+/* -*- 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>
+
+#include <comphelper/errcode.hxx>
+#include <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+
+#include <rtl/math.hxx>
+
+sal_Int32 ImpGetLong( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_Int32 nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar; break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ nRes = p->nInteger; break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = p->nUShort; break;
+ case SbxLONG:
+ nRes = p->nLong; break;
+ case SbxULONG:
+ if( p->nULong > SbxMAXLNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXLNG;
+ }
+ else
+ nRes = static_cast<sal_Int32>(p->nULong);
+ break;
+ case SbxSINGLE:
+ nRes = ImpDoubleToLong(p->nSingle);
+ break;
+ case SbxSALINT64:
+ nRes = p->nInt64;
+ break;
+ case SbxSALUINT64:
+ nRes = p->uInt64;
+ break;
+ case SbxCURRENCY:
+ {
+ sal_Int64 tstVal = p->nInt64 / CURRENCY_FACTOR;
+ nRes = static_cast<sal_Int32>(tstVal);
+ if( tstVal < SbxMINLNG || SbxMAXLNG < tstVal ) SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ if( SbxMAXLNG < tstVal ) nRes = SbxMAXLNG;
+ if( tstVal < SbxMINLNG ) nRes = SbxMINLNG;
+ break;
+ }
+ case SbxDATE:
+ case SbxDOUBLE:
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ double dVal;
+ if( p->eType == SbxDECIMAL )
+ {
+ dVal = 0.0;
+ if( p->pDecimal )
+ p->pDecimal->getDouble( dVal );
+ }
+ else
+ dVal = p->nDouble;
+
+ nRes = ImpDoubleToLong(dVal);
+ break;
+ }
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ nRes = 0;
+ else
+ {
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else
+ nRes = ImpDoubleToLong(d);
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetLong();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar; break;
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte; break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ nRes = *p->pInteger; break;
+ case SbxBYREF | SbxLONG:
+ nRes = *p->pLong; break;
+
+ // from here had to be tested
+ case SbxBYREF | SbxULONG:
+ aTmp.nULong = *p->pULong; goto ref;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ aTmp.nUShort = *p->pUShort; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutLong( SbxValues* p, sal_Int32 n )
+{
+ SbxValues aTmp;
+
+start:
+ switch( +p->eType )
+ {
+ // From here had to be tested
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxSALUINT64:
+ aTmp.puInt64 = &p->uInt64; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort;
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ // from here no longer
+ case SbxLONG:
+ p->nLong = n; break;
+ case SbxSINGLE:
+ p->nSingle = static_cast<float>(n); break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+ case SbxCURRENCY:
+ p->nInt64 = n * CURRENCY_FACTOR; break;
+ case SbxSALINT64:
+ p->nInt64 = n; break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ ImpCreateDecimal( p )->setLong( n );
+ break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ ImpCvtNum( static_cast<double>(n), 0, *p->pOUString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutLong( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ if( n > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXCHAR;
+ }
+ else if( n < SbxMINCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMINCHAR;
+ }
+ *p->pChar = static_cast<sal_Unicode>(n); break;
+ case SbxBYREF | SbxBYTE:
+ if( n > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXBYTE;
+ }
+ else if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pByte = static_cast<sal_uInt8>(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ if( n > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXINT;
+ }
+ else if( n < SbxMININT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMININT;
+ }
+ *p->pInteger = static_cast<sal_Int16>(n); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ if( n > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXUINT;
+ }
+ else if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pUShort = static_cast<sal_uInt16>(n); break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = n; break;
+ case SbxBYREF | SbxULONG:
+ if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = 0;
+ }
+ *p->pULong = static_cast<sal_uInt32>(n); break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = n; break;
+ case SbxBYREF | SbxSALUINT64:
+ if( n < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); *p->puInt64 = 0;
+ }
+ else
+ *p->puInt64 = n;
+ break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = static_cast<float>(n); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = static_cast<double>(n); break;
+ case SbxBYREF | SbxCURRENCY:
+ *p->pnInt64 = static_cast<sal_Int64>(n) * sal_Int64(CURRENCY_FACTOR); break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxobj.cxx b/basic/source/sbx/sbxobj.cxx
new file mode 100644
index 0000000000..be1aec1639
--- /dev/null
+++ b/basic/source/sbx/sbxobj.cxx
@@ -0,0 +1,904 @@
+/* -*- 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>
+#include <sal/log.hxx>
+
+#include <iomanip>
+
+#include <tools/debug.hxx>
+#include <tools/stream.hxx>
+#include <basic/sbx.hxx>
+#include <basic/sberrors.hxx>
+#include <basic/sbxmeth.hxx>
+#include <sbxprop.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include "sbxdec.hxx"
+#include "sbxres.hxx"
+#include <filefmt.hxx>
+
+
+static OUString pNameProp; // Name-Property
+static OUString pParentProp; // Parent-Property
+
+static sal_uInt16 nNameHash = 0, nParentHash = 0;
+
+
+SbxObject::SbxObject( const OUString& rClass )
+ : SbxVariable( SbxOBJECT ), aClassName( rClass )
+{
+ aData.pObj = this;
+ if( !nNameHash )
+ {
+ pNameProp = GetSbxRes( StringId::NameProp );
+ pParentProp = GetSbxRes( StringId::ParentProp );
+ nNameHash = MakeHashCode( pNameProp );
+ nParentHash = MakeHashCode( pParentProp );
+ }
+ SbxObject::Clear();
+ SbxObject::SetName( rClass );
+}
+
+SbxObject::SbxObject( const SbxObject& rObj )
+ : SvRefBase( rObj ), SbxVariable( rObj.GetType() ),
+ SfxListener( rObj )
+{
+ *this = rObj;
+}
+
+SbxObject& SbxObject::operator=( const SbxObject& r )
+{
+ if( &r != this )
+ {
+ SbxVariable::operator=( r );
+ aClassName = r.aClassName;
+ pMethods = new SbxArray;
+ pProps = new SbxArray;
+ pObjs = new SbxArray( SbxOBJECT );
+ // The arrays were copied, the content taken over
+ *pMethods = *r.pMethods;
+ *pProps = *r.pProps;
+ *pObjs = *r.pObjs;
+ // Because the variables were taken over, this is OK
+ pDfltProp = r.pDfltProp;
+ SetName( r.GetName() );
+ SetFlags( r.GetFlags() );
+ SetModified( true );
+ }
+ return *this;
+}
+
+static void CheckParentsOnDelete( SbxObject* pObj, SbxArray* p )
+{
+ for (sal_uInt32 i = 0; i < p->Count(); i++)
+ {
+ SbxVariableRef& rRef = p->GetRef(i);
+ if( rRef->IsBroadcaster() )
+ {
+ pObj->EndListening( rRef->GetBroadcaster(), true );
+ }
+ // does the element have more than one reference and still a Listener?
+ if( rRef->GetRefCount() > 1 )
+ {
+ rRef->SetParent( nullptr );
+ SAL_INFO_IF(rRef->IsBroadcaster() && rRef->GetBroadcaster().GetListenerCount(), "basic.sbx", "Object element with dangling parent");
+ }
+ }
+}
+
+SbxObject::~SbxObject()
+{
+ CheckParentsOnDelete( this, pProps.get() );
+ CheckParentsOnDelete( this, pMethods.get() );
+ CheckParentsOnDelete( this, pObjs.get() );
+
+ // avoid handling in ~SbxVariable as SbxFlagBits::DimAsNew == SbxFlagBits::GlobalSearch
+ ResetFlag( SbxFlagBits::DimAsNew );
+}
+
+SbxDataType SbxObject::GetType() const
+{
+ return SbxOBJECT;
+}
+
+SbxClassType SbxObject::GetClass() const
+{
+ return SbxClassType::Object;
+}
+
+void SbxObject::Clear()
+{
+ pMethods = new SbxArray;
+ pProps = new SbxArray;
+ pObjs = new SbxArray( SbxOBJECT );
+ SbxVariable* p;
+ p = Make( pNameProp, SbxClassType::Property, SbxSTRING );
+ p->SetFlag( SbxFlagBits::DontStore );
+ p = Make( pParentProp, SbxClassType::Property, SbxOBJECT );
+ p->ResetFlag( SbxFlagBits::Write );
+ p->SetFlag( SbxFlagBits::DontStore );
+ pDfltProp = nullptr;
+ SetModified( false );
+}
+
+void SbxObject::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const SbxHint* p = dynamic_cast<const SbxHint*>(&rHint);
+ if( !p )
+ return;
+
+ const SfxHintId nId = p->GetId();
+ bool bRead = ( nId == SfxHintId::BasicDataWanted );
+ bool bWrite = ( nId == SfxHintId::BasicDataChanged );
+ SbxVariable* pVar = p->GetVar();
+ if( !(bRead || bWrite) )
+ return;
+
+ OUString aVarName( pVar->GetName() );
+ sal_uInt16 nHash_ = MakeHashCode( aVarName );
+ if( nHash_ == nNameHash && aVarName.equalsIgnoreAsciiCase( pNameProp ) )
+ {
+ if( bRead )
+ {
+ pVar->PutString( GetName() );
+ }
+ else
+ {
+ SetName( pVar->GetOUString() );
+ }
+ }
+ else if( nHash_ == nParentHash && aVarName.equalsIgnoreAsciiCase( pParentProp ) )
+ {
+ SbxObject* p_ = GetParent();
+ if( !p_ )
+ {
+ p_ = this;
+ }
+ pVar->PutObject( p_ );
+ }
+}
+
+bool SbxObject::IsClass( const OUString& rName ) const
+{
+ return aClassName.equalsIgnoreAsciiCase( rName );
+}
+
+SbxVariable* SbxObject::Find( const OUString& rName, SbxClassType t )
+{
+#ifdef DBG_UTIL
+ static int nLvl = 1;
+ static const char* pCls[] = { "DontCare","Array","Value","Variable","Method","Property","Object" };
+ SAL_INFO(
+ "basic.sbx",
+ "search" << std::setw(nLvl) << " "
+ << (t >= SbxClassType::DontCare && t <= SbxClassType::Object
+ ? pCls[static_cast<int>(t) - 1] : "Unknown class")
+ << " " << rName << " in " << SbxVariable::GetName());
+ ++nLvl;
+#endif
+
+ SbxVariable* pRes = nullptr;
+ pObjs->SetFlag( SbxFlagBits::ExtSearch );
+ if( t == SbxClassType::DontCare )
+ {
+ pRes = pMethods->Find( rName, SbxClassType::Method );
+ if( !pRes )
+ {
+ pRes = pProps->Find( rName, SbxClassType::Property );
+ }
+ if( !pRes )
+ {
+ pRes = pObjs->Find( rName, t );
+ }
+ }
+ else
+ {
+ SbxArray* pArray = nullptr;
+ switch( t )
+ {
+ case SbxClassType::Variable:
+ case SbxClassType::Property: pArray = pProps.get(); break;
+ case SbxClassType::Method: pArray = pMethods.get(); break;
+ case SbxClassType::Object: pArray = pObjs.get(); break;
+ default: SAL_WARN( "basic.sbx", "Invalid SBX-Class" ); break;
+ }
+ if( pArray )
+ {
+ pRes = pArray->Find( rName, t );
+ }
+ }
+ // Extended Search in the Object-Array?
+ // For objects and DontCare the array of objects has already been searched
+ if( !pRes && ( t == SbxClassType::Method || t == SbxClassType::Property ) )
+ pRes = pObjs->Find( rName, t );
+ // Search in the parents?
+ if( !pRes && IsSet( SbxFlagBits::GlobalSearch ) )
+ {
+ SbxObject* pCur = this;
+ while( !pRes && pCur->pParent )
+ {
+ // I myself was already searched!
+ SbxFlagBits nOwn = pCur->GetFlags();
+ pCur->ResetFlag( SbxFlagBits::ExtSearch );
+ // I search already global!
+ SbxFlagBits nPar = pCur->pParent->GetFlags();
+ pCur->pParent->ResetFlag( SbxFlagBits::GlobalSearch );
+ pRes = pCur->pParent->Find( rName, t );
+ pCur->SetFlags( nOwn );
+ pCur->pParent->SetFlags( nPar );
+ pCur = pCur->pParent;
+ }
+ }
+#ifdef DBG_UTIL
+ --nLvl;
+ SAL_INFO_IF(
+ pRes, "basic.sbx",
+ "found" << std::setw(nLvl) << " " << rName << " in "
+ << SbxVariable::GetName());
+#endif
+ return pRes;
+}
+
+// Abbreviated version: The parent-string will be searched
+// The whole thing recursive, because Call() might be overridden
+// Qualified names are allowed
+
+bool SbxObject::Call( const OUString& rName, SbxArray* pParam )
+{
+ SbxVariable* pMeth = FindQualified( rName, SbxClassType::DontCare);
+ if( dynamic_cast<const SbxMethod*>( pMeth) )
+ {
+ // tdf#149622 - clear return value of the method before calling it
+ pMeth->Clear();
+
+ // FindQualified() might have struck already!
+ if( pParam )
+ {
+ pMeth->SetParameters( pParam );
+ }
+ pMeth->Broadcast( SfxHintId::BasicDataWanted );
+ pMeth->SetParameters( nullptr );
+ return true;
+ }
+ SetError( ERRCODE_BASIC_NO_METHOD, rName );
+ return false;
+}
+
+SbxProperty* SbxObject::GetDfltProperty()
+{
+ if ( !pDfltProp && !aDfltPropName.isEmpty() )
+ {
+ pDfltProp = static_cast<SbxProperty*>( Find( aDfltPropName, SbxClassType::Property ) );
+ if( !pDfltProp )
+ {
+ pDfltProp = static_cast<SbxProperty*>( Make( aDfltPropName, SbxClassType::Property, SbxVARIANT ) );
+ }
+ }
+ return pDfltProp;
+}
+void SbxObject::SetDfltProperty( const OUString& rName )
+{
+ if ( rName != aDfltPropName )
+ {
+ pDfltProp = nullptr;
+ }
+ aDfltPropName = rName;
+ SetModified( true );
+}
+
+// Search of an already available variable. If it was located,
+// the index will be set, otherwise the Count of the Array will be returned.
+// In any case the correct Array will be returned.
+
+SbxArray* SbxObject::FindVar( SbxVariable const * pVar, sal_uInt32& nArrayIdx )
+{
+ SbxArray* pArray = nullptr;
+ if( pVar )
+ {
+ switch( pVar->GetClass() )
+ {
+ case SbxClassType::Variable:
+ case SbxClassType::Property: pArray = pProps.get(); break;
+ case SbxClassType::Method: pArray = pMethods.get(); break;
+ case SbxClassType::Object: pArray = pObjs.get(); break;
+ default: SAL_WARN( "basic.sbx", "Invalid SBX-Class" ); break;
+ }
+ }
+ if( pArray )
+ {
+ nArrayIdx = pArray->Count();
+ // Is the variable per name available?
+ pArray->ResetFlag( SbxFlagBits::ExtSearch );
+ SbxVariable* pOld = pArray->Find( pVar->GetName(), pVar->GetClass() );
+ if( pOld )
+ {
+ for (sal_uInt32 i = 0; i < pArray->Count(); i++)
+ {
+ SbxVariableRef& rRef = pArray->GetRef(i);
+ if( rRef.get() == pOld )
+ {
+ nArrayIdx = i; break;
+ }
+ }
+ }
+ }
+ return pArray;
+}
+
+// If a new object will be established, this object will be indexed,
+// if an object of this name exists already.
+
+SbxVariable* SbxObject::Make( const OUString& rName, SbxClassType ct, SbxDataType dt, bool bIsRuntimeFunction )
+{
+ // Is the object already available?
+ SbxArray* pArray = nullptr;
+ switch( ct )
+ {
+ case SbxClassType::Variable:
+ case SbxClassType::Property: pArray = pProps.get(); break;
+ case SbxClassType::Method: pArray = pMethods.get(); break;
+ case SbxClassType::Object: pArray = pObjs.get(); break;
+ default: SAL_WARN( "basic.sbx", "Invalid SBX-Class" ); break;
+ }
+ if( !pArray )
+ {
+ return nullptr;
+ }
+ // Collections may contain objects of the same name
+ if( ct != SbxClassType::Object || dynamic_cast<const SbxCollection*>( this ) == nullptr )
+ {
+ SbxVariable* pRes = pArray->Find( rName, ct );
+ if( pRes )
+ {
+ return pRes;
+ }
+ }
+ SbxVariableRef pVar;
+ switch( ct )
+ {
+ case SbxClassType::Variable:
+ case SbxClassType::Property:
+ pVar = new SbxProperty( rName, dt );
+ break;
+ case SbxClassType::Method:
+ pVar = new SbxMethod( rName, dt, bIsRuntimeFunction );
+ break;
+ case SbxClassType::Object:
+ pVar = CreateObject( rName ).get();
+ break;
+ default:
+ break;
+ }
+ pVar->SetParent( this );
+ pArray->Put(pVar.get(), pArray->Count());
+ SetModified( true );
+ // The object listen always
+ StartListening(pVar->GetBroadcaster(), DuplicateHandling::Prevent);
+ return pVar.get();
+}
+
+void SbxObject::Insert( SbxVariable* pVar )
+{
+ sal_uInt32 nIdx;
+ SbxArray* pArray = FindVar( pVar, nIdx );
+ if( !pArray )
+ return;
+
+ // Into with it. But you should pay attention at the Pointer!
+ if (nIdx < pArray->Count())
+ {
+ // Then this element exists already
+ // There are objects of the same name allowed at collections
+ if( pArray == pObjs.get() && dynamic_cast<const SbxCollection*>( this ) != nullptr )
+ {
+ nIdx = pArray->Count();
+ }
+ else
+ {
+ SbxVariable* pOld = pArray->Get(nIdx);
+ // already inside: overwrite
+ if( pOld == pVar )
+ {
+ return;
+ }
+ EndListening( pOld->GetBroadcaster(), true );
+ if( pVar->GetClass() == SbxClassType::Property )
+ {
+ if( pOld == pDfltProp )
+ {
+ pDfltProp = static_cast<SbxProperty*>(pVar);
+ }
+ }
+ }
+ }
+ StartListening(pVar->GetBroadcaster(), DuplicateHandling::Prevent);
+ pArray->Put(pVar, nIdx);
+ if( pVar->GetParent() != this )
+ {
+ pVar->SetParent( this );
+ }
+ SetModified( true );
+#ifdef DBG_UTIL
+ static const char* pCls[] =
+ { "DontCare","Array","Value","Variable","Method","Property","Object" };
+ OUString aVarName( pVar->GetName() );
+ if (const SbxObject *pSbxObj = aVarName.isEmpty() ? dynamic_cast<const SbxObject*>(pVar) : nullptr)
+ {
+ aVarName = pSbxObj->GetClassName();
+ }
+ SAL_INFO(
+ "basic.sbx",
+ "insert "
+ << ((pVar->GetClass() >= SbxClassType::DontCare
+ && pVar->GetClass() <= SbxClassType::Object)
+ ? pCls[static_cast<int>(pVar->GetClass()) - 1] : "Unknown class")
+ << " " << aVarName << " in " << SbxVariable::GetName());
+#endif
+}
+
+// Optimisation, Insertion without checking about
+// double entry and without broadcasts, will only be used in SO2/auto.cxx
+void SbxObject::QuickInsert( SbxVariable* pVar )
+{
+ SbxArray* pArray = nullptr;
+ if( pVar )
+ {
+ switch( pVar->GetClass() )
+ {
+ case SbxClassType::Variable:
+ case SbxClassType::Property: pArray = pProps.get(); break;
+ case SbxClassType::Method: pArray = pMethods.get(); break;
+ case SbxClassType::Object: pArray = pObjs.get(); break;
+ default: SAL_WARN( "basic.sbx", "Invalid SBX-Class" ); break;
+ }
+ }
+ if( !pArray )
+ return;
+
+ StartListening(pVar->GetBroadcaster(), DuplicateHandling::Prevent);
+ pArray->Put(pVar, pArray->Count());
+ if( pVar->GetParent() != this )
+ {
+ pVar->SetParent( this );
+ }
+ SetModified( true );
+#ifdef DBG_UTIL
+ static const char* pCls[] =
+ { "DontCare","Array","Value","Variable","Method","Property","Object" };
+ OUString aVarName( pVar->GetName() );
+ if (const SbxObject *pSbxObj = aVarName.isEmpty() ? dynamic_cast<const SbxObject*>(pVar) : nullptr)
+ {
+ aVarName = pSbxObj->GetClassName();
+ }
+ SAL_INFO(
+ "basic.sbx",
+ "insert "
+ << ((pVar->GetClass() >= SbxClassType::DontCare
+ && pVar->GetClass() <= SbxClassType::Object)
+ ? pCls[static_cast<int>(pVar->GetClass()) - 1] : "Unknown class")
+ << " " << aVarName << " in " << SbxVariable::GetName());
+#endif
+}
+
+void SbxObject::Remove( const OUString& rName, SbxClassType t )
+{
+ Remove( SbxObject::Find( rName, t ) );
+}
+
+void SbxObject::Remove( SbxVariable* pVar )
+{
+ sal_uInt32 nIdx;
+ SbxArray* pArray = FindVar( pVar, nIdx );
+ if (!(pArray && nIdx < pArray->Count()))
+ return;
+
+#ifdef DBG_UTIL
+ OUString aVarName( pVar->GetName() );
+ if (const SbxObject *pSbxObj = aVarName.isEmpty() ? dynamic_cast<const SbxObject*>(pVar) : nullptr)
+ {
+ aVarName = pSbxObj->GetClassName();
+ }
+ SAL_INFO(
+ "basic.sbx",
+ "remove " << aVarName << " in " << SbxVariable::GetName());
+#endif
+ SbxVariableRef pVar_ = pArray->Get(nIdx);
+ if( pVar_->IsBroadcaster() )
+ {
+ EndListening( pVar_->GetBroadcaster(), true );
+ }
+ if( pVar_.get() == pDfltProp )
+ {
+ pDfltProp = nullptr;
+ }
+ pArray->Remove( nIdx );
+ if( pVar_->GetParent() == this )
+ {
+ pVar_->SetParent( nullptr );
+ }
+ SetModified( true );
+}
+
+static bool LoadArray( SvStream& rStrm, SbxObject* pThis, SbxArray* pArray )
+{
+ SbxArrayRef p = static_cast<SbxArray*>( SbxBase::Load( rStrm ).get() );
+ if( !p.is() )
+ {
+ return false;
+ }
+ for (sal_uInt32 i = 0; i < p->Count(); i++)
+ {
+ SbxVariableRef& r = p->GetRef(i);
+ SbxVariable* pVar = r.get();
+ if( pVar )
+ {
+ pVar->SetParent( pThis );
+ pThis->StartListening(pVar->GetBroadcaster(), DuplicateHandling::Prevent);
+ }
+ }
+ pArray->Merge( p.get() );
+ return true;
+}
+
+// The load of an object is additive!
+
+bool SbxObject::LoadData( SvStream& rStrm, sal_uInt16 nVer )
+{
+ // Help for the read in of old objects: just return TRUE,
+ // LoadPrivateData() has to set the default status up
+ if( !nVer )
+ {
+ return true;
+ }
+ pDfltProp = nullptr;
+ if( !SbxVariable::LoadData( rStrm, nVer ) )
+ {
+ return false;
+ }
+ // If it contains no alien object, insert ourselves
+ if( aData.eType == SbxOBJECT && !aData.pObj )
+ {
+ aData.pObj = this;
+ }
+ sal_uInt32 nSize;
+ OUString aDfltProp;
+ aClassName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm, RTL_TEXTENCODING_ASCII_US);
+ aDfltProp = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm, RTL_TEXTENCODING_ASCII_US);
+ sal_uInt64 nPos = rStrm.Tell();
+ rStrm.ReadUInt32( nSize );
+ sal_uInt64 const nNewPos = rStrm.Tell();
+ nPos += nSize;
+ DBG_ASSERT( nPos >= nNewPos, "SBX: Loaded too much data" );
+ if( nPos != nNewPos )
+ {
+ rStrm.Seek( nPos );
+ }
+ if( !LoadArray( rStrm, this, pMethods.get() ) ||
+ !LoadArray( rStrm, this, pProps.get() ) ||
+ !LoadArray( rStrm, this, pObjs.get() ) )
+ {
+ return false;
+ }
+ // Set properties
+ if( !aDfltProp.isEmpty() )
+ {
+ pDfltProp = static_cast<SbxProperty*>( pProps->Find( aDfltProp, SbxClassType::Property ) );
+ }
+ SetModified( false );
+ return true;
+}
+
+std::pair<bool, sal_uInt32> SbxObject::StoreData( SvStream& rStrm ) const
+{
+ if( !SbxVariable::StoreData(rStrm).first )
+ {
+ return { false, 0 };
+ }
+ OUString aDfltProp;
+ if( pDfltProp )
+ {
+ aDfltProp = pDfltProp->GetName();
+ }
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, aClassName, RTL_TEXTENCODING_ASCII_US);
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, aDfltProp, RTL_TEXTENCODING_ASCII_US);
+ sal_uInt64 const nPos = rStrm.Tell();
+ rStrm.WriteUInt32( 0 );
+ sal_uInt64 const nNew = rStrm.Tell();
+ rStrm.Seek( nPos );
+ rStrm.WriteUInt32( nNew - nPos );
+ rStrm.Seek( nNew );
+ const auto& [bSuccess, nVersion] = pMethods->Store( rStrm );
+ if( !bSuccess )
+ {
+ return { false, 0 };
+ }
+ if( !pProps->Store( rStrm ).first )
+ {
+ return { false, 0 };
+ }
+ if( !pObjs->Store( rStrm ).first )
+ {
+ return { false, 0 };
+ }
+ const_cast<SbxObject*>(this)->SetModified( false );
+ return { true, nVersion };
+}
+
+static bool CollectAttrs( const SbxBase* p, OUString& rRes )
+{
+ OUString aAttrs;
+ if( p->IsHidden() )
+ {
+ aAttrs = "Hidden";
+ }
+ if( p->IsSet( SbxFlagBits::ExtSearch ) )
+ {
+ if( !aAttrs.isEmpty() )
+ {
+ aAttrs += ",";
+ }
+ aAttrs += "ExtSearch";
+ }
+ if( !p->IsVisible() )
+ {
+ if( !aAttrs.isEmpty() )
+ {
+ aAttrs += ",";
+ }
+ aAttrs += "Invisible";
+ }
+ if( p->IsSet( SbxFlagBits::DontStore ) )
+ {
+ if( !aAttrs.isEmpty() )
+ {
+ aAttrs += ",";
+ }
+ aAttrs += "DontStore";
+ }
+ if( !aAttrs.isEmpty() )
+ {
+ rRes = " (" + aAttrs + ")";
+ return true;
+ }
+ else
+ {
+ rRes.clear();
+ return false;
+ }
+}
+
+void SbxObject::Dump( SvStream& rStrm, bool bFill )
+{
+ // Shifting
+ static sal_uInt16 nLevel = 0;
+ if ( nLevel > 10 )
+ {
+ rStrm.WriteOString( "<too deep>" ) << endl;
+ return;
+ }
+ ++nLevel;
+ OUString aIndent("");
+ for ( sal_uInt16 n = 1; n < nLevel; ++n )
+ {
+ aIndent += " ";
+ }
+ // Output the data of the object itself
+ OString aNameStr(OUStringToOString(GetName(), RTL_TEXTENCODING_ASCII_US));
+ OString aClassNameStr(OUStringToOString(aClassName, RTL_TEXTENCODING_ASCII_US));
+ rStrm.WriteOString( "Object( " )
+ .WriteOString( OString::number(reinterpret_cast<sal_IntPtr>(this)) ).WriteOString( "=='" )
+ .WriteOString( aNameStr.isEmpty() ? "<unnamed>"_ostr : aNameStr ).WriteOString( "', " )
+ .WriteOString( "of class '" ).WriteOString( aClassNameStr ).WriteOString( "', " )
+ .WriteOString( "counts " )
+ .WriteOString( OString::number(GetRefCount()) )
+ .WriteOString( " refs, " );
+ if ( GetParent() )
+ {
+ OString aParentNameStr(OUStringToOString(GetName(), RTL_TEXTENCODING_ASCII_US));
+ rStrm.WriteOString( "in parent " )
+ .WriteOString( OString::number(reinterpret_cast<sal_IntPtr>(GetParent())) )
+ .WriteOString( "=='" ).WriteOString( aParentNameStr.isEmpty() ? "<unnamed>"_ostr : aParentNameStr ).WriteOString( "'" );
+ }
+ else
+ {
+ rStrm.WriteOString( "no parent " );
+ }
+ rStrm.WriteOString( " )" ) << endl;
+ OString aIndentNameStr(OUStringToOString(aIndent, RTL_TEXTENCODING_ASCII_US));
+ rStrm.WriteOString( aIndentNameStr ).WriteOString( "{" ) << endl;
+
+ // Flags
+ OUString aAttrs;
+ if( CollectAttrs( this, aAttrs ) )
+ {
+ OString aAttrStr(OUStringToOString(aAttrs, RTL_TEXTENCODING_ASCII_US));
+ rStrm.WriteOString( aIndentNameStr ).WriteOString( "- Flags: " ).WriteOString( aAttrStr ) << endl;
+ }
+
+ // Methods
+ rStrm.WriteOString( aIndentNameStr ).WriteOString( "- Methods:" ) << endl;
+ for (sal_uInt32 i = 0; i < pMethods->Count(); i++)
+ {
+ SbxVariableRef& r = pMethods->GetRef(i);
+ SbxVariable* pVar = r.get();
+ if( pVar )
+ {
+ OUString aLine = aIndent + " - " + pVar->GetName( SbxNameType::ShortTypes );
+ OUString aAttrs2;
+ if( CollectAttrs( pVar, aAttrs2 ) )
+ {
+ aLine += aAttrs2;
+ }
+ if( dynamic_cast<const SbxMethod *>(pVar) == nullptr )
+ {
+ aLine += " !! Not a Method !!";
+ }
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, aLine, RTL_TEXTENCODING_ASCII_US);
+
+ // Output also the object at object-methods
+ if ( pVar->GetValues_Impl().eType == SbxOBJECT &&
+ pVar->GetValues_Impl().pObj &&
+ pVar->GetValues_Impl().pObj != this &&
+ pVar->GetValues_Impl().pObj != GetParent() )
+ {
+ rStrm.WriteOString( " contains " );
+ static_cast<SbxObject*>(pVar->GetValues_Impl().pObj)->Dump( rStrm, bFill );
+ }
+ else
+ {
+ rStrm << endl;
+ }
+ }
+ }
+
+ // Properties
+ rStrm.WriteOString( aIndentNameStr ).WriteOString( "- Properties:" ) << endl;
+ {
+ for (sal_uInt32 i = 0; i < pProps->Count(); i++)
+ {
+ SbxVariableRef& r = pProps->GetRef(i);
+ SbxVariable* pVar = r.get();
+ if( pVar )
+ {
+ OUString aLine = aIndent + " - " + pVar->GetName( SbxNameType::ShortTypes );
+ OUString aAttrs3;
+ if( CollectAttrs( pVar, aAttrs3 ) )
+ {
+ aLine += aAttrs3;
+ }
+ if( dynamic_cast<const SbxProperty *>(pVar) == nullptr )
+ {
+ aLine += " !! Not a Property !!";
+ }
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, aLine, RTL_TEXTENCODING_ASCII_US);
+
+ // output also the object at object properties
+ if ( pVar->GetValues_Impl().eType == SbxOBJECT &&
+ pVar->GetValues_Impl().pObj &&
+ pVar->GetValues_Impl().pObj != this &&
+ pVar->GetValues_Impl().pObj != GetParent() )
+ {
+ rStrm.WriteOString( " contains " );
+ static_cast<SbxObject*>(pVar->GetValues_Impl().pObj)->Dump( rStrm, bFill );
+ }
+ else
+ {
+ rStrm << endl;
+ }
+ }
+ }
+ }
+
+ // Objects
+ rStrm.WriteOString( aIndentNameStr ).WriteOString( "- Objects:" ) << endl;
+ {
+ for (sal_uInt32 i = 0; i < pObjs->Count(); i++)
+ {
+ SbxVariableRef& r = pObjs->GetRef(i);
+ SbxVariable* pVar = r.get();
+ if ( pVar )
+ {
+ rStrm.WriteOString( aIndentNameStr ).WriteOString( " - Sub" );
+ if (SbxObject *pSbxObj = dynamic_cast<SbxObject*>(pVar))
+ {
+ pSbxObj->Dump(rStrm, bFill);
+ }
+ else
+ {
+ pVar->Dump(rStrm, bFill);
+ }
+ }
+ }
+ }
+
+ rStrm.WriteOString( aIndentNameStr ).WriteOString( "}" ) << endl << endl;
+ --nLevel;
+}
+
+SbxMethod::SbxMethod( const OUString& r, SbxDataType t, bool bIsRuntimeFunction )
+ : SbxVariable(t)
+ , mbIsRuntimeFunction(bIsRuntimeFunction)
+ , mbRuntimeFunctionReturnType(t)
+{
+ SetName(r);
+}
+
+SbxMethod::SbxMethod( const SbxMethod& r )
+ : SvRefBase(r)
+ , SbxVariable(r)
+ , mbIsRuntimeFunction(r.IsRuntimeFunction())
+ , mbRuntimeFunctionReturnType(r.GetRuntimeFunctionReturnType())
+{
+}
+
+SbxMethod::~SbxMethod()
+{
+}
+
+SbxClassType SbxMethod::GetClass() const
+{
+ return SbxClassType::Method;
+}
+
+void SbxMethod::Clear()
+{
+ // Release referenced data, and reset data type to the function return type
+ // Implementation similar to SbxValue::SetType
+ // tdf#143582: Don't take "read-only" flag into account, allow clearing method return value
+ switch (aData.eType)
+ {
+ case SbxSTRING:
+ delete aData.pOUString;
+ break;
+ case SbxOBJECT:
+ if (aData.pObj)
+ {
+ if (aData.pObj != this)
+ {
+ bool bParentProp = (GetUserData() & 0xFFFF) == 5345; // See sbxvalue.cxx
+ if (!bParentProp)
+ aData.pObj->ReleaseRef();
+ }
+ }
+ break;
+ case SbxDECIMAL:
+ releaseDecimalPtr(aData.pDecimal);
+ break;
+ default:
+ break;
+ }
+ aData.clear(IsFixed() ? aData.eType : SbxEMPTY);
+}
+
+SbxProperty::SbxProperty( const OUString& r, SbxDataType t )
+ : SbxVariable( t )
+{
+ SetName( r );
+}
+
+SbxProperty::~SbxProperty()
+{
+}
+
+SbxClassType SbxProperty::GetClass() const
+{
+ return SbxClassType::Property;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxres.cxx b/basic/source/sbx/sbxres.cxx
new file mode 100644
index 0000000000..125969e3ef
--- /dev/null
+++ b/basic/source/sbx/sbxres.cxx
@@ -0,0 +1,80 @@
+/* -*- 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 "sbxres.hxx"
+
+#include <rtl/ustring.hxx>
+
+static const char* pSbxRes[] = {
+ "Empty",
+ "Null",
+ "Integer",
+ "Long",
+ "Single",
+ "Double",
+ "Currency",
+ "Date",
+ "String",
+ "Object",
+ "Error",
+ "Boolean",
+ "Variant",
+ "Any",
+ "Type14",
+ "Type15",
+ "Char",
+ "Byte",
+ "UShort",
+ "ULong",
+ "Long64",
+ "ULong64",
+ "Int",
+ "UInt",
+ "Void",
+ "HResult",
+ "Pointer",
+ "DimArray",
+ "CArray",
+ "Any",
+ "LpStr",
+ "LpWStr",
+ " As ",
+ "Optional ",
+ "Byref ",
+
+ "Name",
+ "Parent",
+ "Application",
+ "Count",
+ "Add",
+ "Item",
+ "Remove",
+
+ "Error ", // with blank!
+ "False",
+ "True"
+};
+
+OUString GetSbxRes( StringId nId )
+{
+ return OUString::createFromAscii( ( nId > StringId::LastValue ) ? "???" : pSbxRes[ static_cast<int>( nId ) ] );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxres.hxx b/basic/source/sbx/sbxres.hxx
new file mode 100644
index 0000000000..185dad4089
--- /dev/null
+++ b/basic/source/sbx/sbxres.hxx
@@ -0,0 +1,51 @@
+/* -*- 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 <rtl/ustring.hxx>
+
+// Currently there are no resources provided in the SVTOOLS-Project.
+// Because it is non-critical resources (BASIC-Keywords),
+// we can work with dummies.
+
+enum class StringId {
+ Types = 0,
+ Any = 13,
+ As = 32,
+ Optional = 33,
+ ByRef = 34,
+
+ NameProp = 35,
+ ParentProp = 36,
+ CountProp = 38,
+ AddMeth = 39,
+ ItemMeth = 40,
+ RemoveMeth = 41,
+
+ ErrorMsg = 42,
+ False = 43,
+ True = 44,
+
+ LastValue = 44
+};
+
+OUString GetSbxRes( StringId );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxscan.cxx b/basic/source/sbx/sbxscan.cxx
new file mode 100644
index 0000000000..0d6949e913
--- /dev/null
+++ b/basic/source/sbx/sbxscan.cxx
@@ -0,0 +1,745 @@
+/* -*- 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>
+
+#include <string_view>
+
+#include <config_features.h>
+
+#include <comphelper/errcode.hxx>
+#include <unotools/resmgr.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+
+#include <unotools/syslocale.hxx>
+#include <unotools/charclass.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <math.h>
+
+#include <sbxbase.hxx>
+#include <sbintern.hxx>
+#include <sbxform.hxx>
+
+#include <date.hxx>
+#include <runtime.hxx>
+#include <strings.hrc>
+
+#include <rtl/character.hxx>
+#include <rtl/math.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <o3tl/string_view.hxx>
+
+
+void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep, sal_Unicode& rcDecimalSepAlt )
+{
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
+ rcDecimalSep = rData.getNumDecimalSep()[0];
+ rcThousandSep = rData.getNumThousandSep()[0];
+ rcDecimalSepAlt = rData.getNumDecimalSepAlt().toChar();
+}
+
+
+static bool ImpStrChr( std::u16string_view str, sal_Unicode c ) { return str.find(c) != std::u16string_view::npos; }
+
+
+// scanning a string according to BASIC-conventions
+// but exponent may also be a D, so data type is SbxDOUBLE
+// conversion error if data type is fixed and it doesn't fit
+
+ErrCode ImpScan( const OUString& rWSrc, double& nVal, SbxDataType& rType,
+ sal_uInt16* pLen, bool bOnlyIntntl )
+{
+ sal_Unicode cIntntlDecSep, cIntntlGrpSep, cIntntlDecSepAlt;
+ sal_Unicode cNonIntntlDecSep = '.';
+ if( bOnlyIntntl )
+ {
+ ImpGetIntntlSep( cIntntlDecSep, cIntntlGrpSep, cIntntlDecSepAlt );
+ cNonIntntlDecSep = cIntntlDecSep;
+ // Ensure that the decimal separator alternative is really one.
+ if (cIntntlDecSepAlt && cIntntlDecSepAlt == cNonIntntlDecSep)
+ cIntntlDecSepAlt = 0;
+ }
+ else
+ {
+ cIntntlDecSep = cNonIntntlDecSep;
+ cIntntlGrpSep = 0; // no group separator accepted in non-i18n
+ cIntntlDecSepAlt = 0;
+ }
+
+ const sal_Unicode* const pStart = rWSrc.getStr();
+ const sal_Unicode* p = pStart;
+ OUStringBuffer aBuf( rWSrc.getLength());
+ bool bRes = true;
+ bool bMinus = false;
+ nVal = 0;
+ SbxDataType eScanType = SbxSINGLE;
+ while( *p == ' ' || *p == '\t' )
+ p++;
+ if (*p == '+')
+ p++;
+ else if( *p == '-' )
+ {
+ p++;
+ bMinus = true;
+ }
+ if( rtl::isAsciiDigit( *p ) || ((*p == cNonIntntlDecSep || *p == cIntntlDecSep ||
+ (cIntntlDecSep && *p == cIntntlGrpSep) || (cIntntlDecSepAlt && *p == cIntntlDecSepAlt)) &&
+ rtl::isAsciiDigit( *(p+1) )))
+ {
+ // tdf#118442: Whitespace and minus are skipped; store the position to calculate index
+ const sal_Unicode* const pDigitsStart = p;
+ short exp = 0;
+ short decsep = 0;
+ short ndig = 0;
+ short ncdig = 0; // number of digits after decimal point
+ OUStringBuffer aSearchStr(OUString::Concat("0123456789DEde") + OUStringChar(cNonIntntlDecSep));
+ if( cIntntlDecSep != cNonIntntlDecSep )
+ aSearchStr.append(cIntntlDecSep);
+ if( cIntntlDecSepAlt && cIntntlDecSepAlt != cNonIntntlDecSep )
+ aSearchStr.append(cIntntlDecSepAlt);
+ if( bOnlyIntntl )
+ aSearchStr.append(cIntntlGrpSep);
+ const OUString pSearchStr = aSearchStr.makeStringAndClear();
+ static constexpr OUStringLiteral pDdEe = u"DdEe";
+ while( ImpStrChr( pSearchStr, *p ) )
+ {
+ aBuf.append( *p );
+ if( bOnlyIntntl && *p == cIntntlGrpSep )
+ {
+ p++;
+ continue;
+ }
+ if( *p == cNonIntntlDecSep || *p == cIntntlDecSep || (cIntntlDecSepAlt && *p == cIntntlDecSepAlt) )
+ {
+ // Use the separator that is passed to stringToDouble()
+ aBuf[p - pDigitsStart] = cIntntlDecSep;
+ p++;
+ if( ++decsep > 1 )
+ continue;
+ }
+ else if( ImpStrChr( pDdEe, *p ) )
+ {
+ if( ++exp > 1 )
+ {
+ p++;
+ continue;
+ }
+ if( *p == 'D' || *p == 'd' )
+ eScanType = SbxDOUBLE;
+ aBuf[p - pDigitsStart] = 'E';
+ p++;
+ if (*p == '+')
+ ++p;
+ else if (*p == '-')
+ {
+ aBuf.append('-');
+ ++p;
+ }
+ }
+ else
+ {
+ p++;
+ if( decsep && !exp )
+ ncdig++;
+ }
+ if( !exp )
+ ndig++;
+ }
+
+ if( decsep > 1 || exp > 1 )
+ bRes = false;
+
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ sal_Int32 nParseEnd = 0;
+ nVal = rtl::math::stringToDouble( aBuf, cIntntlDecSep, cIntntlGrpSep, &eStatus, &nParseEnd );
+ if( eStatus != rtl_math_ConversionStatus_Ok || nParseEnd != aBuf.getLength() )
+ bRes = false;
+
+ if( !decsep && !exp )
+ {
+ if( nVal >= SbxMININT && nVal <= SbxMAXINT )
+ eScanType = SbxINTEGER;
+ else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
+ eScanType = SbxLONG;
+ }
+
+ ndig = ndig - decsep;
+ // too many numbers for SINGLE?
+ if( ndig > 15 || ncdig > 6 )
+ eScanType = SbxDOUBLE;
+
+ // type detection?
+ static constexpr OUStringLiteral pTypes = u"%!&#";
+ if( ImpStrChr( pTypes, *p ) )
+ p++;
+ }
+ // hex/octal number? read in and convert:
+ else if( *p == '&' )
+ {
+ p++;
+ eScanType = SbxLONG;
+ OUString aCmp( "0123456789ABCDEF" );
+ char base = 16;
+ char ndig = 8;
+ switch( *p++ )
+ {
+ case 'O':
+ case 'o':
+ aCmp = "01234567";
+ base = 8;
+ ndig = 11;
+ break;
+ case 'H':
+ case 'h':
+ break;
+ default :
+ bRes = false;
+ }
+ while( rtl::isAsciiAlphanumeric( *p ) ) /* XXX: really munge all alnum also when error? */
+ {
+ sal_Unicode ch = rtl::toAsciiUpperCase(*p);
+ if( ImpStrChr( aCmp, ch ) )
+ aBuf.append( ch );
+ else
+ bRes = false;
+ p++;
+ }
+ OUString aBufStr( aBuf.makeStringAndClear());
+ sal_Int32 l = 0;
+ for( const sal_Unicode* q = aBufStr.getStr(); bRes && *q; q++ )
+ {
+ int i = *q - '0';
+ if( i > 9 )
+ i -= 7; // 'A'-'0' = 17 => 10, ...
+ l = ( l * base ) + i;
+ if( !ndig-- )
+ bRes = false;
+ }
+ if( *p == '&' )
+ p++;
+ nVal = static_cast<double>(l);
+ if( l >= SbxMININT && l <= SbxMAXINT )
+ eScanType = SbxINTEGER;
+ }
+#if HAVE_FEATURE_SCRIPTING
+ else if ( SbiRuntime::isVBAEnabled() )
+ {
+ return ERRCODE_BASIC_CONVERSION;
+ }
+#endif
+ // tdf#146672 - skip whitespaces and tabs at the end of the scanned string
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if( pLen )
+ *pLen = static_cast<sal_uInt16>( p - pStart );
+ if( !bRes )
+ return ERRCODE_BASIC_CONVERSION;
+ if( bMinus )
+ nVal = -nVal;
+ rType = eScanType;
+ return ERRCODE_NONE;
+}
+
+// port for CDbl in the Basic
+ErrCode SbxValue::ScanNumIntnl( const OUString& rSrc, double& nVal, bool bSingle )
+{
+ SbxDataType t;
+ sal_uInt16 nLen = 0;
+ ErrCode nRetError = ImpScan( rSrc, nVal, t, &nLen,
+ /*bOnlyIntntl*/true );
+ // read completely?
+ if( nRetError == ERRCODE_NONE && nLen != rSrc.getLength() )
+ {
+ nRetError = ERRCODE_BASIC_CONVERSION;
+ }
+ if( bSingle )
+ {
+ SbxValues aValues( nVal );
+ nVal = static_cast<double>(ImpGetSingle( &aValues )); // here error at overflow
+ }
+ return nRetError;
+}
+
+// The number is prepared unformattedly with the given number of
+// NK-positions. A leading minus is added if applicable.
+// This routine is public because it's also used by the Put-functions
+// in the class SbxImpSTRING.
+
+void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString )
+{
+ sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
+ ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
+ if( bCoreString )
+ cDecimalSep = '.';
+
+ // tdf#143575 - use rtl::math::doubleToUString to convert numbers to strings in basic
+ rRes = rtl::math::doubleToUString(nNum, rtl_math_StringFormat_Automatic, nPrec, cDecimalSep, true);
+}
+
+bool ImpConvStringExt( OUString& rSrc, SbxDataType eTargetType )
+{
+ bool bChanged = false;
+ OUString aNewString;
+
+ // only special cases are handled, nothing on default
+ switch( eTargetType )
+ {
+ // consider international for floating point
+ case SbxSINGLE:
+ case SbxDOUBLE:
+ case SbxCURRENCY:
+ {
+ sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
+ ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
+ aNewString = rSrc;
+
+ if( cDecimalSep != '.' || (cDecimalSepAlt && cDecimalSepAlt != '.') )
+ {
+ sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
+ if( nPos == -1 && cDecimalSepAlt )
+ nPos = aNewString.indexOf( cDecimalSepAlt );
+ if( nPos != -1 )
+ {
+ sal_Unicode* pStr = const_cast<sal_Unicode*>(aNewString.getStr());
+ pStr[nPos] = '.';
+ bChanged = true;
+ }
+ }
+ break;
+ }
+
+ // check as string in case of sal_Bool sal_True and sal_False
+ case SbxBOOL:
+ {
+ if( rSrc.equalsIgnoreAsciiCase("true") )
+ {
+ aNewString = OUString::number( SbxTRUE );
+ bChanged = true;
+ }
+ else if( rSrc.equalsIgnoreAsciiCase("false") )
+ {
+ aNewString = OUString::number( SbxFALSE );
+ bChanged = true;
+ }
+ break;
+ }
+ default: break;
+ }
+
+ if( bChanged )
+ rSrc = aNewString;
+ return bChanged;
+}
+
+
+// formatted number output
+// the return value is the number of characters used
+// from the format
+
+static sal_uInt16 printfmtstr( const OUString& rStr, OUString& rRes, const OUString& rFmt )
+{
+ OUStringBuffer aTemp;
+ const sal_Unicode* pStr = rStr.getStr();
+ const sal_Unicode* pFmtStart = rFmt.getStr();
+ const sal_Unicode* pFmt = pFmtStart;
+
+ switch( *pFmt )
+ {
+ case '!':
+ aTemp.append(*pStr++);
+ pFmt++;
+ break;
+ case '\\':
+ do
+ {
+ aTemp.append( *pStr ? *pStr++ : u' ');
+ pFmt++;
+ }
+ while( *pFmt && *pFmt != '\\' );
+ aTemp.append(*pStr ? *pStr++ : u' ');
+ pFmt++; break;
+ case '&':
+ aTemp = rStr;
+ pFmt++; break;
+ default:
+ aTemp = rStr;
+ break;
+ }
+ rRes = aTemp.makeStringAndClear();
+ return static_cast<sal_uInt16>( pFmt - pFmtStart );
+}
+
+
+bool SbxValue::Scan( const OUString& rSrc, sal_uInt16* pLen )
+{
+ ErrCode eRes = ERRCODE_NONE;
+ if( !CanWrite() )
+ {
+ eRes = ERRCODE_BASIC_PROP_READONLY;
+ }
+ else
+ {
+ double n;
+ SbxDataType t;
+ eRes = ImpScan( rSrc, n, t, pLen, !LibreOffice6FloatingPointMode() );
+ if( eRes == ERRCODE_NONE )
+ {
+ if( !IsFixed() )
+ {
+ SetType( t );
+ }
+ PutDouble( n );
+ }
+ }
+ if( eRes )
+ {
+ SetError( eRes );
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+std::locale BasResLocale()
+{
+ return Translate::Create("sb");
+}
+
+OUString BasResId(TranslateId aId)
+{
+ return Translate::get(aId, BasResLocale());
+}
+
+namespace
+{
+
+enum class VbaFormatType
+{
+ Offset, // standard number format
+ UserDefined, // user defined number format
+ Null
+};
+
+#if HAVE_FEATURE_SCRIPTING
+
+struct VbaFormatInfo
+{
+ VbaFormatType meType;
+ std::u16string_view mpVbaFormat; // Format string in vba
+ NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VbaFormatType::Offset
+ const char* mpOOoFormat; // if meType = VbaFormatType::UserDefined
+};
+
+const VbaFormatInfo pFormatInfoTable[] =
+{
+ { VbaFormatType::Offset, std::u16string_view(u"Long Date"), NF_DATE_SYSTEM_LONG, nullptr },
+ { VbaFormatType::UserDefined, std::u16string_view(u"Medium Date"), NF_NUMBER_STANDARD, "DD-MMM-YY" },
+ { VbaFormatType::Offset, std::u16string_view(u"Short Date"), NF_DATE_SYSTEM_SHORT, nullptr },
+ { VbaFormatType::UserDefined, std::u16string_view(u"Long Time"), NF_NUMBER_STANDARD, "H:MM:SS AM/PM" },
+ { VbaFormatType::Offset, std::u16string_view(u"Medium Time"), NF_TIME_HHMMAMPM, nullptr },
+ { VbaFormatType::Offset, std::u16string_view(u"Short Time"), NF_TIME_HHMM, nullptr },
+ { VbaFormatType::Offset, std::u16string_view(u"ddddd"), NF_DATE_SYSTEM_SHORT, nullptr },
+ { VbaFormatType::Offset, std::u16string_view(u"dddddd"), NF_DATE_SYSTEM_LONG, nullptr },
+ { VbaFormatType::UserDefined, std::u16string_view(u"ttttt"), NF_NUMBER_STANDARD, "H:MM:SS AM/PM" },
+ { VbaFormatType::Offset, std::u16string_view(u"ww"), NF_DATE_WW, nullptr },
+ { VbaFormatType::Null, std::u16string_view(u""), NF_INDEX_TABLE_ENTRIES, nullptr }
+};
+
+const VbaFormatInfo* getFormatInfo( std::u16string_view rFmt )
+{
+ const VbaFormatInfo* pInfo = pFormatInfoTable;
+ while( pInfo->meType != VbaFormatType::Null )
+ {
+ if( o3tl::equalsIgnoreAsciiCase( rFmt, pInfo->mpVbaFormat ) )
+ break;
+ ++pInfo;
+ }
+ return pInfo;
+}
+#endif
+
+} // namespace
+
+#if HAVE_FEATURE_SCRIPTING
+constexpr OUStringLiteral VBAFORMAT_GENERALDATE = u"General Date";
+constexpr OUStringLiteral VBAFORMAT_C = u"c";
+constexpr OUStringLiteral VBAFORMAT_N = u"n";
+constexpr OUString VBAFORMAT_NN = u"nn"_ustr;
+constexpr OUStringLiteral VBAFORMAT_W = u"w";
+constexpr OUStringLiteral VBAFORMAT_Y = u"y";
+constexpr OUStringLiteral VBAFORMAT_LOWERCASE = u"<";
+constexpr OUStringLiteral VBAFORMAT_UPPERCASE = u">";
+#endif
+
+void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
+{
+ short nComma = 0;
+ double d = 0;
+
+ // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
+ // the SvNumberFormatter output is mostly compatible with
+ // VBA output besides the OOo-basic output
+#if HAVE_FEATURE_SCRIPTING
+ if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
+ {
+ OUString aStr = GetOUString();
+
+ SvtSysLocale aSysLocale;
+ const CharClass& rCharClass = aSysLocale.GetCharClass();
+
+ if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_LOWERCASE ) )
+ {
+ rRes = rCharClass.lowercase( aStr );
+ return;
+ }
+ if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_UPPERCASE ) )
+ {
+ rRes = rCharClass.uppercase( aStr );
+ return;
+ }
+
+ LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
+ std::shared_ptr<SvNumberFormatter> pFormatter;
+ if (GetSbData()->pInst)
+ {
+ pFormatter = GetSbData()->pInst->GetNumberFormatter();
+ }
+ else
+ {
+ sal_uInt32 n; // Dummy
+ pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
+ }
+
+ // Passing an index of a locale switches IsNumberFormat() to use that
+ // locale in case the formatter wasn't default created with it.
+ sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
+ double nNumber;
+ const Color* pCol;
+
+ bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, nNumber );
+
+ // number format, use SvNumberFormatter to handle it.
+ if( bSuccess )
+ {
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ OUString aFmtStr = *pFmt;
+ const VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
+ if( pInfo->meType != VbaFormatType::Null )
+ {
+ if( pInfo->meType == VbaFormatType::Offset )
+ {
+ nIndex = pFormatter->GetFormatIndex( pInfo->meOffset, eLangType );
+ }
+ else
+ {
+ aFmtStr = OUString::createFromAscii(pInfo->mpOOoFormat);
+ pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
+ }
+ pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
+ }
+ else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_GENERALDATE )
+ || aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_C ))
+ {
+ if( nNumber <=-1.0 || nNumber >= 1.0 )
+ {
+ // short date
+ nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
+ pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
+
+ // long time
+ if( floor( nNumber ) != nNumber )
+ {
+ aFmtStr = "H:MM:SS AM/PM";
+ pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
+ OUString aTime;
+ pFormatter->GetOutputString( nNumber, nIndex, aTime, &pCol );
+ rRes += " " + aTime;
+ }
+ }
+ else
+ {
+ // long time only
+ aFmtStr = "H:MM:SS AM/PM";
+ pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
+ pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
+ }
+ }
+ else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_N ) ||
+ aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
+ {
+ sal_Int32 nMin = implGetMinute( nNumber );
+ if( nMin < 10 && aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
+ {
+ // Minute in two digits
+ sal_Unicode aBuf[2];
+ aBuf[0] = '0';
+ aBuf[1] = '0' + nMin;
+ rRes = OUString(aBuf, std::size(aBuf));
+ }
+ else
+ {
+ rRes = OUString::number(nMin);
+ }
+ }
+ else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_W ))
+ {
+ sal_Int32 nWeekDay = implGetWeekDay( nNumber );
+ rRes = OUString::number(nWeekDay);
+ }
+ else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_Y ))
+ {
+ sal_Int16 nYear = implGetDateYear( nNumber );
+ double dBaseDate;
+ implDateSerial( nYear, 1, 1, true, SbDateCorrection::None, dBaseDate );
+ sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
+ rRes = OUString::number(nYear32);
+ }
+ else
+ {
+ pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
+ pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
+ }
+
+ return;
+ }
+ }
+#endif
+
+ SbxDataType eType = GetType();
+ switch( eType )
+ {
+ case SbxCHAR:
+ case SbxBYTE:
+ case SbxINTEGER:
+ case SbxUSHORT:
+ case SbxLONG:
+ case SbxULONG:
+ case SbxINT:
+ case SbxUINT:
+ case SbxNULL: // #45929 NULL with a little cheating
+ nComma = 0; goto cvt;
+ case SbxSINGLE:
+ nComma = 6; goto cvt;
+ case SbxDOUBLE:
+ nComma = 14;
+
+ cvt:
+ if( eType != SbxNULL )
+ {
+ d = GetDouble();
+ }
+ // #45355 another point to jump in for isnumeric-String
+ cvt2:
+ if( pFmt )
+ {
+ SbxAppData& rAppData = GetSbxData_Impl();
+
+ LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
+ if( rAppData.pBasicFormater )
+ {
+ if( rAppData.eBasicFormaterLangType != eLangType )
+ {
+ rAppData.pBasicFormater.reset();
+ }
+ }
+ rAppData.eBasicFormaterLangType = eLangType;
+
+
+ if( !rAppData.pBasicFormater )
+ {
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
+ sal_Unicode cComma = rData.getNumDecimalSep()[0];
+ sal_Unicode c1000 = rData.getNumThousandSep()[0];
+ const OUString& aCurrencyStrg = rData.getCurrSymbol();
+
+ // initialize the Basic-formater help object:
+ // get resources for predefined output
+ // of the Format()-command, e. g. for "On/Off"
+ OUString aOnStrg = BasResId(STR_BASICKEY_FORMAT_ON);
+ OUString aOffStrg = BasResId(STR_BASICKEY_FORMAT_OFF);
+ OUString aYesStrg = BasResId(STR_BASICKEY_FORMAT_YES);
+ OUString aNoStrg = BasResId(STR_BASICKEY_FORMAT_NO);
+ OUString aTrueStrg = BasResId(STR_BASICKEY_FORMAT_TRUE);
+ OUString aFalseStrg = BasResId(STR_BASICKEY_FORMAT_FALSE);
+ OUString aCurrencyFormatStrg = BasResId(STR_BASICKEY_FORMAT_CURRENCY);
+
+ rAppData.pBasicFormater = std::make_unique<SbxBasicFormater>(
+ cComma,c1000,aOnStrg,aOffStrg,
+ aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
+ aCurrencyStrg,aCurrencyFormatStrg );
+ }
+ // Remark: For performance reasons there's only ONE BasicFormater-
+ // object created and 'stored', so that the expensive resource-
+ // loading is saved (for country-specific predefined outputs,
+ // e. g. "On/Off") and the continuous string-creation
+ // operations, too.
+ // BUT: therefore this code is NOT multithreading capable!
+
+ // here are problems with ;;;Null because this method is only
+ // called, if SbxValue is a number!!!
+ // in addition rAppData.pBasicFormater->BasicFormatNull( *pFmt ); could be called!
+ if( eType != SbxNULL )
+ {
+ rRes = rAppData.pBasicFormater->BasicFormat( d ,*pFmt );
+ }
+ else
+ {
+ rRes = SbxBasicFormater::BasicFormatNull( *pFmt );
+ }
+
+ }
+ else
+ ImpCvtNum( GetDouble(), nComma, rRes );
+ break;
+ case SbxSTRING:
+ if( pFmt )
+ {
+ // #45355 converting if numeric
+ if( IsNumericRTL() )
+ {
+ ScanNumIntnl( GetOUString(), d );
+ goto cvt2;
+ }
+ else
+ {
+ printfmtstr( GetOUString(), rRes, *pFmt );
+ }
+ }
+ else
+ {
+ rRes = GetOUString();
+ }
+ break;
+ default:
+ rRes = GetOUString();
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxsng.cxx b/basic/source/sbx/sbxsng.cxx
new file mode 100644
index 0000000000..2710997160
--- /dev/null
+++ b/basic/source/sbx/sbxsng.cxx
@@ -0,0 +1,280 @@
+/* -*- 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>
+
+#include <comphelper/errcode.hxx>
+#include <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+
+float ImpGetSingle( const SbxValues* p )
+{
+ SbxValues aTmp;
+ float nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar; break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ nRes = p->nInteger; break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = p->nUShort; break;
+ case SbxLONG:
+ nRes = static_cast<float>(p->nLong); break;
+ case SbxULONG:
+ nRes = static_cast<float>(p->nULong); break;
+ case SbxSINGLE:
+ nRes = p->nSingle; break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ if (!p->pDecimal || !p->pDecimal->getSingle(nRes))
+ nRes = 0.0;
+ break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ case SbxSALUINT64:
+ {
+ double dVal;
+ if( p->eType == SbxCURRENCY )
+ dVal = ImpCurrencyToDouble( p->nInt64 );
+ else if( p->eType == SbxSALINT64 )
+ dVal = static_cast<float>(p->nInt64);
+ else if( p->eType == SbxSALUINT64 )
+ dVal = static_cast<float>(p->uInt64);
+ else
+ dVal = p->nDouble;
+
+ if( dVal > SbxMAXSNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ nRes = static_cast< float >(SbxMAXSNG);
+ }
+ else if( dVal < SbxMINSNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ nRes = static_cast< float >(SbxMINSNG);
+ }
+ // tests for underflow - storing value too small for precision of single
+ else if( dVal > 0 && dVal < SbxMAXSNG2 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ nRes = static_cast< float >(SbxMAXSNG2);
+ }
+ else if( dVal < 0 && dVal > SbxMINSNG2 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ nRes = static_cast< float >(SbxMINSNG2);
+ }
+ else
+ nRes = static_cast<float>(dVal);
+ break;
+ }
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ nRes = 0;
+ else
+ {
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else if( d > SbxMAXSNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ nRes = static_cast< float >(SbxMAXSNG);
+ }
+ else if( d < SbxMINSNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ nRes = static_cast< float >(SbxMINSNG);
+ }
+ else
+ nRes = static_cast<float>(d);
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetSingle();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ nRes = *p->pChar; break;
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte; break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ nRes = *p->pInteger; break;
+ case SbxBYREF | SbxLONG:
+ nRes = static_cast<float>(*p->pLong); break;
+ case SbxBYREF | SbxULONG:
+ nRes = static_cast<float>(*p->pULong); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ nRes = *p->pUShort; break;
+ case SbxBYREF | SbxSINGLE:
+ nRes = *p->pSingle; break;
+ // from here had to be tested
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxSALINT64:
+ case SbxBYREF | SbxCURRENCY:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutSingle( SbxValues* p, float n )
+{
+ SbxValues aTmp;
+start:
+ switch( +p->eType )
+ {
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger; goto direct;
+ case SbxLONG:
+ aTmp.pLong = &p->nLong; goto direct;
+ case SbxULONG:
+ aTmp.pULong = &p->nULong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ aTmp.pnInt64 = &p->nInt64; goto direct;
+ case SbxSALUINT64:
+ aTmp.puInt64 = &p->uInt64; goto direct;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ SbxDecimal* pDec = ImpCreateDecimal( p );
+ if( !pDec->setSingle( n ) )
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ // from here no tests
+ case SbxSINGLE:
+ p->nSingle = n; break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ {
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ // tdf#107953 - show 9 significant digits
+ ImpCvtNum( static_cast<double>(n), 9, *p->pOUString );
+ break;
+ }
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutSingle( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ *p->pChar = ImpDoubleToChar(n); break;
+ case SbxBYREF | SbxBYTE:
+ *p->pByte = ImpDoubleToByte(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ *p->pInteger = ImpDoubleToInteger(n); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ *p->pUShort = ImpDoubleToUShort(n); break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = ImpDoubleToLong(n); break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = ImpDoubleToULong(n); break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = n; break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = static_cast<double>(n); break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = ImpDoubleToSalInt64(n); break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = ImpDoubleToSalUInt64(n); break;
+ case SbxBYREF | SbxCURRENCY:
+ double d;
+ if( n > SbxMAXCURR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); d = SbxMAXCURR;
+ }
+ else if( n < SbxMINCURR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); d = SbxMINCURR;
+ }
+ else
+ {
+ d = n;
+ }
+ *p->pnInt64 = ImpDoubleToCurrency( d ); break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxstr.cxx b/basic/source/sbx/sbxstr.cxx
new file mode 100644
index 0000000000..4e447bb600
--- /dev/null
+++ b/basic/source/sbx/sbxstr.cxx
@@ -0,0 +1,331 @@
+/* -*- 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 <basic/sbx.hxx>
+#include "sbxconv.hxx"
+#include "sbxres.hxx"
+#include <runtime.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <memory>
+
+// The conversion of an item onto String was handled via the Put-Methods
+// of the several data types to avoid duplicated code.
+
+OUString ImpGetString( const SbxValues* p )
+{
+ SbxValues aTmp;
+ OUString aRes;
+ aTmp.eType = SbxSTRING;
+ aTmp.pOUString = &aRes;
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ break;
+ case SbxCHAR:
+ ImpPutChar( &aTmp, p->nChar ); break;
+ case SbxBYTE:
+ ImpPutByte( &aTmp, p->nByte ); break;
+ case SbxINTEGER:
+ ImpPutInteger( &aTmp, p->nInteger ); break;
+ case SbxBOOL:
+ ImpPutBool( &aTmp, p->nUShort ); break;
+ case SbxUSHORT:
+ ImpPutUShort( &aTmp, p->nUShort ); break;
+ case SbxLONG:
+ ImpPutLong( &aTmp, p->nLong ); break;
+ case SbxULONG:
+ ImpPutULong( &aTmp, p->nULong ); break;
+ case SbxSINGLE:
+ ImpPutSingle( &aTmp, p->nSingle ); break;
+ case SbxDOUBLE:
+ ImpPutDouble( &aTmp, p->nDouble ); break;
+ case SbxCURRENCY:
+ ImpPutCurrency( &aTmp, p->nInt64 ); break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ ImpPutDecimal( &aTmp, p->pDecimal ); break;
+ case SbxSALINT64:
+ ImpPutInt64( &aTmp, p->nInt64 ); break;
+ case SbxSALUINT64:
+ ImpPutUInt64( &aTmp, p->uInt64 ); break;
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if ( p->pOUString )
+ {
+ *aTmp.pOUString = *p->pOUString;
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ {
+ aRes = pVal->GetOUString();
+ }
+ else if( p->pObj && p->pObj->IsFixed()
+ && (p->pObj->GetType() == (SbxARRAY | SbxBYTE )) )
+ {
+ // convert byte array to string
+ SbxArray* pArr = dynamic_cast<SbxArray*>( p->pObj );
+ if( pArr )
+ {
+ aRes = ByteArrayToString( pArr );
+ }
+ }
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ }
+ break;
+ }
+ case SbxERROR:
+ // Here the String "Error n" is generated
+ aRes = GetSbxRes( StringId::ErrorMsg ) + OUString::number(p->nUShort);
+ break;
+ case SbxDATE:
+ ImpPutDate( &aTmp, p->nDouble ); break;
+
+ case SbxBYREF | SbxCHAR:
+ ImpPutChar( &aTmp, *p->pChar ); break;
+ case SbxBYREF | SbxBYTE:
+ ImpPutByte( &aTmp, *p->pByte ); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ ImpPutInteger( &aTmp, *p->pInteger ); break;
+ case SbxBYREF | SbxLONG:
+ ImpPutLong( &aTmp, *p->pLong ); break;
+ case SbxBYREF | SbxULONG:
+ ImpPutULong( &aTmp, *p->pULong ); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ ImpPutUShort( &aTmp, *p->pUShort ); break;
+ case SbxBYREF | SbxSINGLE:
+ ImpPutSingle( &aTmp, *p->pSingle ); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ ImpPutDouble( &aTmp, *p->pDouble ); break;
+ case SbxBYREF | SbxCURRENCY:
+ ImpPutCurrency( &aTmp, *p->pnInt64 ); break;
+ case SbxBYREF | SbxSALINT64:
+ ImpPutInt64( &aTmp, *p->pnInt64 ); break;
+ case SbxBYREF | SbxSALUINT64:
+ ImpPutUInt64( &aTmp, *p->puInt64 ); break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+ return aRes;
+}
+
+// From 1997-04-10, new function for SbxValue::GetCoreString()
+OUString ImpGetCoreString( const SbxValues* p )
+{
+ // For now only for double
+ if( ( p->eType & (~SbxBYREF) ) == SbxDOUBLE )
+ {
+ SbxValues aTmp;
+ OUString aRes;
+ aTmp.eType = SbxSTRING;
+ aTmp.pOUString = &aRes;
+ if( p->eType == SbxDOUBLE )
+ ImpPutDouble( &aTmp, p->nDouble, true ); // true = bCoreString
+ else
+ ImpPutDouble( &aTmp, *p->pDouble, true ); // true = bCoreString
+ return aRes;
+ }
+ else
+ return ImpGetString( p );
+}
+
+void ImpPutString( SbxValues* p, const OUString* n )
+{
+ SbxValues aTmp;
+ aTmp.eType = SbxSTRING;
+ std::unique_ptr<OUString> pTmp;
+ // as a precaution, if a NULL-Ptr appears
+ if( !n )
+ {
+ pTmp.reset(new OUString);
+ n = pTmp.get();
+ }
+ aTmp.pOUString = const_cast<OUString*>(n);
+ switch( +p->eType )
+ {
+ case SbxCHAR:
+ p->nChar = ImpGetChar( &aTmp ); break;
+ case SbxBYTE:
+ p->nByte = ImpGetByte( &aTmp ); break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ p->nInteger = ImpGetInteger( &aTmp ); break;
+ case SbxLONG:
+ p->nLong = ImpGetLong( &aTmp ); break;
+ case SbxULONG:
+ p->nULong = ImpGetULong( &aTmp ); break;
+ case SbxERROR:
+ case SbxUSHORT:
+ p->nUShort = ImpGetUShort( &aTmp ); break;
+ case SbxSINGLE:
+ p->nSingle = ImpGetSingle( &aTmp ); break;
+ case SbxDATE:
+ p->nDouble = ImpGetDate( &aTmp ); break;
+ case SbxDOUBLE:
+ p->nDouble = ImpGetDouble( &aTmp ); break;
+ case SbxCURRENCY:
+ p->nInt64 = ImpGetCurrency( &aTmp ); break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ releaseDecimalPtr( p->pDecimal );
+ p->pDecimal = ImpGetDecimal( &aTmp ); break;
+ case SbxSALINT64:
+ p->nInt64 = ImpGetInt64( &aTmp ); break;
+ case SbxSALUINT64:
+ p->uInt64 = ImpGetUInt64( &aTmp ); break;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !n->isEmpty() )
+ {
+ if( !p->pOUString )
+ p->pOUString = new OUString( *n );
+ else
+ *p->pOUString = *n;
+ }
+ else
+ {
+ delete p->pOUString;
+ p->pOUString = nullptr;
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutString( *n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ *p->pChar = ImpGetChar( p ); break;
+ case SbxBYREF | SbxBYTE:
+ *p->pByte = ImpGetByte( p ); break;
+ case SbxBYREF | SbxINTEGER:
+ *p->pInteger = ImpGetInteger( p ); break;
+ case SbxBYREF | SbxBOOL:
+ *p->pUShort = sal::static_int_cast< sal_uInt16 >( ImpGetBool( p ) );
+ break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ *p->pUShort = ImpGetUShort( p ); break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = ImpGetLong( p ); break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = ImpGetULong( p ); break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = ImpGetSingle( p ); break;
+ case SbxBYREF | SbxDATE:
+ *p->pDouble = ImpGetDate( p ); break;
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = ImpGetDouble( p ); break;
+ case SbxBYREF | SbxCURRENCY:
+ *p->pnInt64 = ImpGetCurrency( p ); break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = ImpGetInt64( p ); break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = ImpGetUInt64( p ); break;
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+
+// Convert string to an array of bytes, preserving unicode (2bytes per character)
+SbxArray* StringToByteArray(const OUString& rStr)
+{
+ sal_Int32 nArraySize = rStr.getLength() * 2;
+ const sal_Unicode* pSrc = rStr.getStr();
+ SbxDimArray* pArray = new SbxDimArray(SbxBYTE);
+ if( nArraySize )
+ {
+#if !HAVE_FEATURE_SCRIPTING
+ bool bIncIndex = false;
+#else
+ bool bIncIndex = IsBaseIndexOne();
+#endif
+ if( bIncIndex )
+ pArray->AddDim(1, nArraySize);
+ else
+ pArray->AddDim(0, nArraySize - 1);
+ }
+ else
+ {
+ pArray->unoAddDim(0, -1);
+ }
+
+ for( sal_Int32 i=0; i< nArraySize; i++)
+ {
+ SbxVariable* pNew = new SbxVariable( SbxBYTE );
+ sal_uInt8 aByte = static_cast< sal_uInt8 >( (i%2) ? ((*pSrc) >> 8) & 0xff : (*pSrc) & 0xff );
+ pNew->PutByte( aByte );
+ pNew->SetFlag( SbxFlagBits::Write );
+ pArray->Put(pNew, i);
+ if( i%2 )
+ pSrc++;
+ }
+ return pArray;
+}
+
+// Convert an array of bytes to string (2bytes per character)
+OUString ByteArrayToString(SbxArray* pArr)
+{
+ sal_uInt32 nCount = pArr->Count();
+ OUStringBuffer aStrBuf((nCount + 1) / 2);
+ sal_Unicode aChar = 0;
+ for( sal_uInt32 i = 0 ; i < nCount ; i++ )
+ {
+ sal_Unicode aTempChar = pArr->Get(i)->GetByte();
+ if( i%2 )
+ {
+ aChar = (aTempChar << 8 ) | aChar;
+ aStrBuf.append(aChar);
+ aChar = 0;
+ }
+ else
+ {
+ aChar = aTempChar;
+ }
+ }
+
+ if( nCount%2 )
+ {
+ aStrBuf.append(aChar);
+ }
+
+ return aStrBuf.makeStringAndClear();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxuint.cxx b/basic/source/sbx/sbxuint.cxx
new file mode 100644
index 0000000000..d536ec99e2
--- /dev/null
+++ b/basic/source/sbx/sbxuint.cxx
@@ -0,0 +1,291 @@
+/* -*- 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>
+
+#include <comphelper/errcode.hxx>
+#include <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+
+sal_uInt16 ImpGetUShort( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_uInt16 nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar;
+ break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ if( p->nInteger < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = p->nInteger;
+ break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = p->nUShort;
+ break;
+ case SbxLONG:
+ if( p->nLong > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXUINT;
+ }
+ else if( p->nLong < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt16>(p->nLong);
+ break;
+ case SbxULONG:
+ if( p->nULong > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXUINT;
+ }
+ else
+ nRes = static_cast<sal_uInt16>(p->nULong);
+ break;
+ case SbxCURRENCY:
+ if( p->nInt64 / CURRENCY_FACTOR > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXUINT;
+ }
+ else if( p->nInt64 < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt16>(p->nInt64 / CURRENCY_FACTOR);
+ break;
+ case SbxSALINT64:
+ if( p->nInt64 > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXUINT;
+ }
+ else if( p->nInt64 < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = static_cast<sal_uInt16>(p->nInt64);
+ break;
+ case SbxSALUINT64:
+ if( p->uInt64 > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXUINT;
+ }
+ else
+ nRes = static_cast<sal_uInt16>(p->uInt64);
+ break;
+ case SbxSINGLE:
+ nRes = ImpDoubleToUShort(p->nSingle);
+ break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ double dVal;
+ if( p->eType == SbxDECIMAL )
+ {
+ dVal = 0.0;
+ if( p->pDecimal )
+ p->pDecimal->getDouble( dVal );
+ }
+ else
+ dVal = p->nDouble;
+
+ nRes = ImpDoubleToUShort(dVal);
+ break;
+ }
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ nRes = 0;
+ else
+ {
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else
+ nRes = ImpDoubleToUShort(d);
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetUShort();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte; break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ nRes = *p->pUShort; break;
+
+ // from here on will be tested
+ case SbxBYREF | SbxCHAR:
+ aTmp.nChar = *p->pChar; goto ref;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ aTmp.nInteger = *p->pInteger; goto ref;
+ case SbxBYREF | SbxLONG:
+ aTmp.nLong = *p->pLong; goto ref;
+ case SbxBYREF | SbxULONG:
+ aTmp.nULong = *p->pULong; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutUShort( SbxValues* p, sal_uInt16 n )
+{
+ SbxValues aTmp;
+
+start:
+ switch( +p->eType )
+ {
+ case SbxERROR:
+ case SbxUSHORT:
+ p->nUShort = n; break;
+ case SbxLONG:
+ p->nLong = n; break;
+ case SbxULONG:
+ p->nULong = n; break;
+ case SbxSINGLE:
+ p->nSingle = n; break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+ case SbxCURRENCY:
+ p->nInt64 = n * CURRENCY_FACTOR; break;
+ case SbxSALINT64:
+ p->nInt64 = n; break;
+ case SbxSALUINT64:
+ p->uInt64 = n; break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ ImpCreateDecimal( p )->setUInt( n );
+ break;
+
+ // from here on tests
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxBYTE:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger;
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ ImpCvtNum( static_cast<double>(n), 0, *p->pOUString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutUShort( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+
+ case SbxBYREF | SbxCHAR:
+ *p->pChar = static_cast<sal_Unicode>(n); break;
+ case SbxBYREF | SbxBYTE:
+ if( n > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXBYTE;
+ }
+ *p->pByte = static_cast<sal_uInt8>(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ if( n > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXINT;
+ }
+ *p->pInteger = static_cast<sal_Int16>(n); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ *p->pUShort = n; break;
+ case SbxBYREF | SbxLONG:
+ *p->pLong = n; break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = n; break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = n; break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = n; break;
+ case SbxBYREF | SbxCURRENCY:
+ *p->pnInt64 = n * CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = n; break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = n; break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxulng.cxx b/basic/source/sbx/sbxulng.cxx
new file mode 100644
index 0000000000..68ae314570
--- /dev/null
+++ b/basic/source/sbx/sbxulng.cxx
@@ -0,0 +1,267 @@
+/* -*- 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>
+
+#include <comphelper/errcode.hxx>
+#include <basic/sberrors.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+
+sal_uInt32 ImpGetULong( const SbxValues* p )
+{
+ SbxValues aTmp;
+ sal_uInt32 nRes;
+start:
+ switch( +p->eType )
+ {
+ case SbxNULL:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ [[fallthrough]];
+ case SbxEMPTY:
+ nRes = 0; break;
+ case SbxCHAR:
+ nRes = p->nChar;
+ break;
+ case SbxBYTE:
+ nRes = p->nByte; break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ if( p->nInteger < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = p->nInteger;
+ break;
+ case SbxERROR:
+ case SbxUSHORT:
+ nRes = p->nUShort;
+ break;
+ case SbxLONG:
+ if( p->nLong < 0 )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
+ }
+ else
+ nRes = p->nLong;
+ break;
+ case SbxULONG:
+ nRes = p->nULong; break;
+ case SbxSINGLE:
+ nRes = ImpDoubleToULong(p->nSingle);
+ break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ case SbxSALINT64:
+ case SbxSALUINT64:
+ case SbxCURRENCY:
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ {
+ double dVal;
+ if( p->eType == SbxCURRENCY )
+ dVal = ImpCurrencyToDouble( p->nInt64 );
+ else if( p->eType == SbxSALINT64 )
+ dVal = static_cast< double >(p->nInt64);
+ else if( p->eType == SbxSALUINT64 )
+ dVal = ImpSalUInt64ToDouble( p->uInt64 );
+ else if( p->eType == SbxDECIMAL )
+ {
+ dVal = 0.0;
+ if( p->pDecimal )
+ p->pDecimal->getDouble( dVal );
+ }
+ else
+ dVal = p->nDouble;
+
+ nRes = ImpDoubleToULong(dVal);
+ break;
+ }
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ nRes = 0;
+ else
+ {
+ double d;
+ SbxDataType t;
+ if( ImpScan( *p->pOUString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ nRes = 0;
+ else
+ nRes = ImpDoubleToULong(d);
+ }
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ nRes = pVal->GetULong();
+ else
+ {
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); nRes = 0;
+ }
+ break;
+ }
+
+ case SbxBYREF | SbxBYTE:
+ nRes = *p->pByte; break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ nRes = *p->pUShort; break;
+ case SbxBYREF | SbxULONG:
+ nRes = *p->pULong; break;
+
+ // from here on tests
+ case SbxBYREF | SbxCHAR:
+ aTmp.nChar = *p->pChar; goto ref;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ aTmp.nInteger = *p->pInteger; goto ref;
+ case SbxBYREF | SbxLONG:
+ aTmp.nLong = *p->pLong; goto ref;
+ case SbxBYREF | SbxSINGLE:
+ aTmp.nSingle = *p->pSingle; goto ref;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ aTmp.nDouble = *p->pDouble; goto ref;
+ case SbxBYREF | SbxCURRENCY:
+ case SbxBYREF | SbxSALINT64:
+ aTmp.nInt64 = *p->pnInt64; goto ref;
+ case SbxBYREF | SbxSALUINT64:
+ aTmp.uInt64 = *p->puInt64; goto ref;
+ ref:
+ aTmp.eType = SbxDataType( p->eType & 0x0FFF );
+ p = &aTmp; goto start;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); nRes = 0;
+ }
+ return nRes;
+}
+
+void ImpPutULong( SbxValues* p, sal_uInt32 n )
+{
+ SbxValues aTmp;
+start:
+ switch( +p->eType )
+ {
+ case SbxULONG:
+ p->nULong = n; break;
+ case SbxSINGLE:
+ p->nSingle = static_cast<float>(n); break;
+ case SbxDATE:
+ case SbxDOUBLE:
+ p->nDouble = n; break;
+ case SbxCURRENCY:
+ case SbxSALINT64:
+ aTmp.pnInt64 = &p->nInt64; goto direct;
+ case SbxSALUINT64:
+ p->uInt64 = n; break;
+ case SbxDECIMAL:
+ case SbxBYREF | SbxDECIMAL:
+ ImpCreateDecimal( p )->setULong( n );
+ break;
+
+ // from here on tests
+ case SbxCHAR:
+ aTmp.pChar = &p->nChar; goto direct;
+ case SbxUINT:
+ aTmp.pByte = &p->nByte; goto direct;
+ case SbxINTEGER:
+ case SbxBOOL:
+ aTmp.pInteger = &p->nInteger; goto direct;
+ case SbxLONG:
+ aTmp.pLong = &p->nLong; goto direct;
+ case SbxERROR:
+ case SbxUSHORT:
+ aTmp.pUShort = &p->nUShort; goto direct;
+ direct:
+ aTmp.eType = SbxDataType( p->eType | SbxBYREF );
+ p = &aTmp; goto start;
+
+ case SbxBYREF | SbxSTRING:
+ case SbxSTRING:
+ case SbxLPSTR:
+ if( !p->pOUString )
+ p->pOUString = new OUString;
+ ImpCvtNum( static_cast<double>(n), 0, *p->pOUString );
+ break;
+ case SbxOBJECT:
+ {
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj );
+ if( pVal )
+ pVal->PutULong( n );
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+ break;
+ }
+ case SbxBYREF | SbxCHAR:
+ if( n > SbxMAXCHAR )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXCHAR;
+ }
+ *p->pChar = static_cast<sal_Unicode>(n); break;
+ case SbxBYREF | SbxBYTE:
+ if( n > SbxMAXBYTE )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXBYTE;
+ }
+ *p->pByte = static_cast<sal_uInt8>(n); break;
+ case SbxBYREF | SbxINTEGER:
+ case SbxBYREF | SbxBOOL:
+ if( n > SbxMAXINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXINT;
+ }
+ *p->pInteger = static_cast<sal_Int16>(n); break;
+ case SbxBYREF | SbxERROR:
+ case SbxBYREF | SbxUSHORT:
+ if( n > SbxMAXUINT )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXUINT;
+ }
+ *p->pUShort = static_cast<sal_uInt16>(n); break;
+ case SbxBYREF | SbxLONG:
+ if( n > SbxMAXLNG )
+ {
+ SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); n = SbxMAXLNG;
+ }
+ *p->pLong = static_cast<sal_Int32>(n); break;
+ case SbxBYREF | SbxULONG:
+ *p->pULong = n; break;
+ case SbxBYREF | SbxSINGLE:
+ *p->pSingle = static_cast<float>(n); break;
+ case SbxBYREF | SbxDATE:
+ case SbxBYREF | SbxDOUBLE:
+ *p->pDouble = n; break;
+ case SbxBYREF | SbxCURRENCY:
+ *p->pnInt64 = n * CURRENCY_FACTOR; break;
+ case SbxBYREF | SbxSALINT64:
+ *p->pnInt64 = n; break;
+ case SbxBYREF | SbxSALUINT64:
+ *p->puInt64 = n; break;
+
+ default:
+ SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxvalue.cxx b/basic/source/sbx/sbxvalue.cxx
new file mode 100644
index 0000000000..d644ca0c3c
--- /dev/null
+++ b/basic/source/sbx/sbxvalue.cxx
@@ -0,0 +1,1569 @@
+/* -*- 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 <math.h>
+#include <string_view>
+
+#include <o3tl/float_int_conversion.hxx>
+#include <tools/debug.hxx>
+#include <tools/stream.hxx>
+#include <sal/log.hxx>
+
+#include <basic/sbx.hxx>
+#include <sbunoobj.hxx>
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+#include <runtime.hxx>
+#include <filefmt.hxx>
+
+
+///////////////////////////// constructors
+
+SbxValue::SbxValue()
+{
+ aData.eType = SbxEMPTY;
+}
+
+SbxValue::SbxValue( SbxDataType t )
+{
+ int n = t & 0x0FFF;
+
+ if( n == SbxVARIANT )
+ n = SbxEMPTY;
+ else
+ SetFlag( SbxFlagBits::Fixed );
+ aData.clear(SbxDataType( n ));
+}
+
+SbxValue::SbxValue( const SbxValue& r )
+ : SvRefBase( r ), SbxBase( r )
+{
+ if( !r.CanRead() )
+ {
+ SetError( ERRCODE_BASIC_PROP_WRITEONLY );
+ if( !IsFixed() )
+ aData.eType = SbxNULL;
+ }
+ else
+ {
+ const_cast<SbxValue*>(&r)->Broadcast( SfxHintId::BasicDataWanted );
+ aData = r.aData;
+ // Copy pointer, increment references
+ switch( aData.eType )
+ {
+ case SbxSTRING:
+ if( aData.pOUString )
+ aData.pOUString = new OUString( *aData.pOUString );
+ break;
+ case SbxOBJECT:
+ if( aData.pObj )
+ aData.pObj->AddFirstRef();
+ break;
+ case SbxDECIMAL:
+ if( aData.pDecimal )
+ aData.pDecimal->addRef();
+ break;
+ default: break;
+ }
+ }
+}
+
+SbxValue& SbxValue::operator=( const SbxValue& r )
+{
+ if( &r != this )
+ {
+ if( !CanWrite() )
+ SetError( ERRCODE_BASIC_PROP_READONLY );
+ else
+ {
+ // string -> byte array
+ if( IsFixed() && (aData.eType == SbxOBJECT)
+ && aData.pObj && ( aData.pObj->GetType() == (SbxARRAY | SbxBYTE) )
+ && (r.aData.eType == SbxSTRING) )
+ {
+ OUString aStr = r.GetOUString();
+ SbxArray* pArr = StringToByteArray(aStr);
+ PutObject(pArr);
+ return *this;
+ }
+ // byte array -> string
+ if( r.IsFixed() && (r.aData.eType == SbxOBJECT)
+ && r.aData.pObj && ( r.aData.pObj->GetType() == (SbxARRAY | SbxBYTE) )
+ && (aData.eType == SbxSTRING) )
+ {
+ SbxBase* pObj = r.GetObject();
+ SbxArray* pArr = dynamic_cast<SbxArray*>( pObj );
+ if( pArr )
+ {
+ OUString aStr = ByteArrayToString( pArr );
+ PutString(aStr);
+ return *this;
+ }
+ }
+ // Readout the content of the variables
+ SbxValues aNew;
+ if( IsFixed() )
+ // then the type has to match
+ aNew.eType = aData.eType;
+ else if( r.IsFixed() )
+ // Source fixed: copy the type
+ aNew.eType = SbxDataType( r.aData.eType & 0x0FFF );
+ else
+ // both variant: then don't care
+ aNew.eType = SbxVARIANT;
+ if( r.Get( aNew ) )
+ Put( aNew );
+ }
+ }
+ return *this;
+}
+
+SbxValue::~SbxValue()
+{
+ SetFlag( SbxFlagBits::Write );
+ // cid#1486004 silence Uncaught exception
+ suppress_fun_call_w_exception(SbxValue::Clear());
+}
+
+void SbxValue::Clear()
+{
+ switch( aData.eType )
+ {
+ case SbxNULL:
+ case SbxEMPTY:
+ case SbxVOID:
+ break;
+ case SbxSTRING:
+ delete aData.pOUString; aData.pOUString = nullptr;
+ break;
+ case SbxOBJECT:
+ if( aData.pObj )
+ {
+ if( aData.pObj != this )
+ {
+ SAL_INFO("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
+ SbxVariable *pThisVar = dynamic_cast<SbxVariable*>( this );
+ bool bParentProp = pThisVar && (pThisVar->GetUserData() & 0xFFFF) == 5345;
+ if ( !bParentProp )
+ aData.pObj->ReleaseRef();
+ }
+ aData.pObj = nullptr;
+ }
+ break;
+ case SbxDECIMAL:
+ releaseDecimalPtr( aData.pDecimal );
+ break;
+ case SbxDATAOBJECT:
+ aData.pData = nullptr; break;
+ default:
+ {
+ SbxValues aEmpty;
+ aEmpty.clear(GetType());
+ Put( aEmpty );
+ }
+ }
+}
+
+// Dummy
+
+void SbxValue::Broadcast( SfxHintId )
+{}
+
+//////////////////////////// Readout data
+
+// Detect the "right" variables. If it is an object, will be addressed either
+// the object itself or its default property.
+// If the variable contain a variable or an object, this will be
+// addressed.
+
+SbxValue* SbxValue::TheRealValue( bool bObjInObjError ) const
+{
+ SbxValue* p = const_cast<SbxValue*>(this);
+ for( ;; )
+ {
+ SbxDataType t = SbxDataType( p->aData.eType & 0x0FFF );
+ if( t == SbxOBJECT )
+ {
+ // The block contains an object or a variable
+ SbxObject* pObj = dynamic_cast<SbxObject*>( p->aData.pObj );
+ if( pObj )
+ {
+ // Has the object a default property?
+ SbxVariable* pDflt = pObj->GetDfltProperty();
+
+ // If this is an object and contains itself,
+ // we cannot access on it
+ // The old condition to set an error is not correct,
+ // because e.g. a regular variant variable with an object
+ // could be affected if another value should be assigned.
+ // Therefore with flag.
+ if( bObjInObjError && !pDflt &&
+ static_cast<SbxValue*>(pObj)->aData.eType == SbxOBJECT &&
+ static_cast<SbxValue*>(pObj)->aData.pObj == pObj )
+ {
+#if !HAVE_FEATURE_SCRIPTING
+ const bool bSuccess = false;
+#else
+ bool bSuccess = handleToStringForCOMObjects( pObj, p );
+#endif
+ if( !bSuccess )
+ {
+ SetError( ERRCODE_BASIC_BAD_PROP_VALUE );
+ p = nullptr;
+ }
+ }
+ else if( pDflt )
+ p = pDflt;
+ break;
+ }
+ // Did we have an array?
+ SbxArray* pArray = dynamic_cast<SbxArray*>( p->aData.pObj );
+ if( pArray )
+ {
+ // When indicated get the parameter
+ SbxArray* pPar = nullptr;
+ SbxVariable* pVar = dynamic_cast<SbxVariable*>( p );
+ if( pVar )
+ pPar = pVar->GetParameters();
+ if( pPar )
+ {
+ // Did we have a dimensioned array?
+ SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( p->aData.pObj );
+ if( pDimArray )
+ p = pDimArray->Get( pPar );
+ else
+ p = pArray->Get(pPar->Get(1)->GetInteger());
+ break;
+ }
+ }
+ // Otherwise guess a SbxValue
+ SbxValue* pVal = dynamic_cast<SbxValue*>( p->aData.pObj );
+ if( pVal )
+ p = pVal;
+ else
+ break;
+ }
+ else
+ break;
+ }
+ return p;
+}
+
+bool SbxValue::Get( SbxValues& rRes ) const
+{
+ bool bRes = false;
+ ErrCode eOld = GetError();
+ if( eOld != ERRCODE_NONE )
+ ResetError();
+ if( !CanRead() )
+ {
+ SetError( ERRCODE_BASIC_PROP_WRITEONLY );
+ rRes.pObj = nullptr;
+ }
+ else
+ {
+ // If an object or a VARIANT is requested, don't search the real values
+ SbxValue* p = const_cast<SbxValue*>(this);
+ if( rRes.eType != SbxOBJECT && rRes.eType != SbxVARIANT )
+ p = TheRealValue( true );
+ if( p )
+ {
+ p->Broadcast( SfxHintId::BasicDataWanted );
+ switch( rRes.eType )
+ {
+ case SbxEMPTY:
+ case SbxVOID:
+ case SbxNULL: break;
+ case SbxVARIANT: rRes = p->aData; break;
+ case SbxINTEGER: rRes.nInteger = ImpGetInteger( &p->aData ); break;
+ case SbxLONG: rRes.nLong = ImpGetLong( &p->aData ); break;
+ case SbxSALINT64: rRes.nInt64 = ImpGetInt64( &p->aData ); break;
+ case SbxSALUINT64: rRes.uInt64 = ImpGetUInt64( &p->aData ); break;
+ case SbxSINGLE: rRes.nSingle = ImpGetSingle( &p->aData ); break;
+ case SbxDOUBLE: rRes.nDouble = ImpGetDouble( &p->aData ); break;
+ case SbxCURRENCY:rRes.nInt64 = ImpGetCurrency( &p->aData ); break;
+ case SbxDECIMAL: rRes.pDecimal = ImpGetDecimal( &p->aData ); break;
+ case SbxDATE: rRes.nDouble = ImpGetDate( &p->aData ); break;
+ case SbxBOOL:
+ rRes.nUShort = sal::static_int_cast< sal_uInt16 >(
+ ImpGetBool( &p->aData ) );
+ break;
+ case SbxCHAR: rRes.nChar = ImpGetChar( &p->aData ); break;
+ case SbxBYTE: rRes.nByte = ImpGetByte( &p->aData ); break;
+ case SbxUSHORT: rRes.nUShort = ImpGetUShort( &p->aData ); break;
+ case SbxULONG: rRes.nULong = ImpGetULong( &p->aData ); break;
+ case SbxLPSTR:
+ case SbxSTRING: p->aPic = ImpGetString( &p->aData );
+ rRes.pOUString = &p->aPic; break;
+ case SbxCoreSTRING: p->aPic = ImpGetCoreString( &p->aData );
+ rRes.pOUString = &p->aPic; break;
+ case SbxINT:
+ rRes.nInt = static_cast<int>(ImpGetLong( &p->aData ));
+ break;
+ case SbxUINT:
+ rRes.nUInt = static_cast<int>(ImpGetULong( &p->aData ));
+ break;
+ case SbxOBJECT:
+ if( p->aData.eType == SbxOBJECT )
+ rRes.pObj = p->aData.pObj;
+ else
+ {
+ SetError( ERRCODE_BASIC_NO_OBJECT );
+ rRes.pObj = nullptr;
+ }
+ break;
+ default:
+ if( p->aData.eType == rRes.eType )
+ rRes = p->aData;
+ else
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ rRes.pObj = nullptr;
+ }
+ }
+ }
+ else
+ {
+ // Object contained itself
+ SbxDataType eTemp = rRes.eType;
+ rRes.clear(eTemp);
+ }
+ }
+ if( !IsError() )
+ {
+ bRes = true;
+ if( eOld != ERRCODE_NONE )
+ SetError( eOld );
+ }
+ return bRes;
+}
+
+SbxValues SbxValue::Get(SbxDataType t) const
+{
+ SbxValues aRes(t);
+ Get(aRes);
+ return aRes;
+}
+
+const OUString& SbxValue::GetCoreString() const
+{
+ SbxValues aRes(SbxCoreSTRING);
+ if( Get( aRes ) )
+ {
+ const_cast<SbxValue*>(this)->aToolString = *aRes.pOUString;
+ }
+ else
+ {
+ const_cast<SbxValue*>(this)->aToolString.clear();
+ }
+ return aToolString;
+}
+
+OUString SbxValue::GetOUString() const
+{
+ OUString aResult;
+ SbxValues aRes(SbxSTRING);
+ if( Get( aRes ) )
+ {
+ aResult = *aRes.pOUString;
+ }
+ return aResult;
+}
+
+//////////////////////////// Write data
+
+bool SbxValue::Put( const SbxValues& rVal )
+{
+ bool bRes = false;
+ ErrCode eOld = GetError();
+ if( eOld != ERRCODE_NONE )
+ ResetError();
+ if( !CanWrite() )
+ SetError( ERRCODE_BASIC_PROP_READONLY );
+ else if( rVal.eType & 0xF000 )
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ else
+ {
+ // If an object is requested, don't search the real values
+ SbxValue* p = this;
+ if( rVal.eType != SbxOBJECT )
+ p = TheRealValue( false ); // Don't allow an error here
+ if( p )
+ {
+ if( !p->CanWrite() )
+ SetError( ERRCODE_BASIC_PROP_READONLY );
+ else if( p->IsFixed() || p->SetType( static_cast<SbxDataType>( rVal.eType & 0x0FFF ) ) )
+ switch( rVal.eType & 0x0FFF )
+ {
+ case SbxEMPTY:
+ case SbxVOID:
+ case SbxNULL: break;
+ case SbxINTEGER: ImpPutInteger( &p->aData, rVal.nInteger ); break;
+ case SbxLONG: ImpPutLong( &p->aData, rVal.nLong ); break;
+ case SbxSALINT64: ImpPutInt64( &p->aData, rVal.nInt64 ); break;
+ case SbxSALUINT64: ImpPutUInt64( &p->aData, rVal.uInt64 ); break;
+ case SbxSINGLE: ImpPutSingle( &p->aData, rVal.nSingle ); break;
+ case SbxDOUBLE: ImpPutDouble( &p->aData, rVal.nDouble ); break;
+ case SbxCURRENCY: ImpPutCurrency( &p->aData, rVal.nInt64 ); break;
+ case SbxDECIMAL: ImpPutDecimal( &p->aData, rVal.pDecimal ); break;
+ case SbxDATE: ImpPutDate( &p->aData, rVal.nDouble ); break;
+ case SbxBOOL: ImpPutBool( &p->aData, rVal.nInteger ); break;
+ case SbxCHAR: ImpPutChar( &p->aData, rVal.nChar ); break;
+ case SbxBYTE: ImpPutByte( &p->aData, rVal.nByte ); break;
+ case SbxUSHORT: ImpPutUShort( &p->aData, rVal.nUShort ); break;
+ case SbxULONG: ImpPutULong( &p->aData, rVal.nULong ); break;
+ case SbxLPSTR:
+ case SbxSTRING: ImpPutString( &p->aData, rVal.pOUString ); break;
+ case SbxINT:
+ ImpPutLong( &p->aData, static_cast<sal_Int32>(rVal.nInt) );
+ break;
+ case SbxUINT:
+ ImpPutULong( &p->aData, static_cast<sal_uInt32>(rVal.nUInt) );
+ break;
+ case SbxOBJECT:
+ if( !p->IsFixed() || p->aData.eType == SbxOBJECT )
+ {
+ // is already inside
+ if( p->aData.eType == SbxOBJECT && p->aData.pObj == rVal.pObj )
+ break;
+
+ // Delete only the value part!
+ p->SbxValue::Clear();
+
+ // real assignment
+ p->aData.pObj = rVal.pObj;
+
+ // if necessary increment Ref-Count
+ if( p->aData.pObj && p->aData.pObj != p )
+ {
+ if ( p != this )
+ {
+ OSL_FAIL( "TheRealValue" );
+ }
+ SAL_INFO("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
+ SbxVariable *pThisVar = dynamic_cast<SbxVariable*>( this );
+ bool bParentProp = pThisVar && (pThisVar->GetUserData() & 0xFFFF) == 5345;
+ if ( !bParentProp )
+ p->aData.pObj->AddFirstRef();
+ }
+ }
+ else
+ SetError( ERRCODE_BASIC_CONVERSION );
+ break;
+ default:
+ if( p->aData.eType == rVal.eType )
+ p->aData = rVal;
+ else
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ if( !p->IsFixed() )
+ p->aData.eType = SbxNULL;
+ }
+ }
+ if( !IsError() )
+ {
+ p->SetModified( true );
+ p->Broadcast( SfxHintId::BasicDataChanged );
+ if( eOld != ERRCODE_NONE )
+ SetError( eOld );
+ bRes = true;
+ }
+ }
+ }
+ return bRes;
+}
+
+// From 1996-03-28:
+// Method to execute a pretreatment of the strings at special types.
+// In particular necessary for BASIC-IDE, so that
+// the output in the Watch-Window can be written back with PutStringExt,
+// if Float were declared with ',' as the decimal separator or BOOl
+// explicit with "TRUE" or "FALSE".
+// Implementation in ImpConvStringExt (SBXSCAN.CXX)
+void SbxValue::PutStringExt( const OUString& r )
+{
+ // Copy; if it is Unicode convert it immediately
+ OUString aStr( r );
+
+ // Identify the own type (not as in Put() with TheRealValue(),
+ // Objects are not handled anyway)
+ SbxDataType eTargetType = SbxDataType( aData.eType & 0x0FFF );
+
+ // tinker a Source-Value
+ SbxValues aRes(SbxSTRING);
+
+ // Only if really something was converted, take the copy,
+ // otherwise take the original (Unicode remains)
+ if( ImpConvStringExt( aStr, eTargetType ) )
+ aRes.pOUString = &aStr;
+ else
+ aRes.pOUString = const_cast<OUString*>(&r);
+
+ // #34939: For Strings which contain a number, and if this has a Num-Type,
+ // set a Fixed flag so that the type will not be changed
+ SbxFlagBits nFlags_ = GetFlags();
+ if( ( eTargetType >= SbxINTEGER && eTargetType <= SbxCURRENCY ) ||
+ ( eTargetType >= SbxCHAR && eTargetType <= SbxUINT ) ||
+ eTargetType == SbxBOOL )
+ {
+ SbxValue aVal;
+ aVal.Put( aRes );
+ if( aVal.IsNumeric() )
+ SetFlag( SbxFlagBits::Fixed );
+ }
+
+ const bool bRet = Put(aRes);
+
+ // If FIXED resulted in an error, set it back
+ // (UI-Action should not result in an error, but simply fail)
+ if( !bRet )
+ ResetError();
+
+ SetFlags( nFlags_ );
+}
+
+bool SbxValue::PutBool( bool b )
+{
+ SbxValues aRes(SbxBOOL);
+ aRes.nUShort = sal::static_int_cast< sal_uInt16 >(b ? SbxTRUE : SbxFALSE);
+ return Put(aRes);
+}
+
+bool SbxValue::PutEmpty()
+{
+ bool bRet = SetType( SbxEMPTY );
+ SetModified( true );
+ return bRet;
+}
+
+void SbxValue::PutNull()
+{
+ bool bRet = SetType( SbxNULL );
+ if( bRet )
+ SetModified( true );
+}
+
+
+// Special decimal methods
+void SbxValue::PutDecimal( css::bridge::oleautomation::Decimal const & rAutomationDec )
+{
+ SbxValue::Clear();
+ aData.pDecimal = new SbxDecimal( rAutomationDec );
+ aData.pDecimal->addRef();
+ aData.eType = SbxDECIMAL;
+}
+
+void SbxValue::fillAutomationDecimal
+ ( css::bridge::oleautomation::Decimal& rAutomationDec ) const
+{
+ SbxDecimal* pDecimal = GetDecimal();
+ if( pDecimal != nullptr )
+ {
+ pDecimal->fillAutomationDecimal( rAutomationDec );
+ }
+}
+
+
+bool SbxValue::PutString( const OUString& r )
+{
+ SbxValues aRes(SbxSTRING);
+ aRes.pOUString = const_cast<OUString*>(&r);
+ return Put(aRes);
+}
+
+
+#define PUT( p, e, t, m ) \
+bool SbxValue::p( t n ) \
+{ SbxValues aRes(e); aRes.m = n; return Put(aRes); }
+
+void SbxValue::PutDate( double n )
+{ SbxValues aRes(SbxDATE); aRes.nDouble = n; Put( aRes ); }
+void SbxValue::PutErr( sal_uInt16 n )
+{ SbxValues aRes(SbxERROR); aRes.nUShort = n; Put( aRes ); }
+
+PUT( PutByte, SbxBYTE, sal_uInt8, nByte )
+PUT( PutChar, SbxCHAR, sal_Unicode, nChar )
+PUT( PutCurrency, SbxCURRENCY, sal_Int64, nInt64 )
+PUT( PutDouble, SbxDOUBLE, double, nDouble )
+PUT( PutInteger, SbxINTEGER, sal_Int16, nInteger )
+PUT( PutLong, SbxLONG, sal_Int32, nLong )
+PUT( PutObject, SbxOBJECT, SbxBase*, pObj )
+PUT( PutSingle, SbxSINGLE, float, nSingle )
+PUT( PutULong, SbxULONG, sal_uInt32, nULong )
+PUT( PutUShort, SbxUSHORT, sal_uInt16, nUShort )
+PUT( PutInt64, SbxSALINT64, sal_Int64, nInt64 )
+PUT( PutUInt64, SbxSALUINT64, sal_uInt64, uInt64 )
+PUT( PutDecimal, SbxDECIMAL, SbxDecimal*, pDecimal )
+
+////////////////////////// Setting of the data type
+
+bool SbxValue::IsFixed() const
+{
+ return (GetFlags() & SbxFlagBits::Fixed) || ((aData.eType & SbxBYREF) != 0);
+}
+
+// A variable is numeric, if it is EMPTY or really numeric
+// or if it contains a complete convertible String
+
+// #41692, implement it for RTL and Basic-Core separately
+bool SbxValue::IsNumeric() const
+{
+ return ImpIsNumeric( /*bOnlyIntntl*/false );
+}
+
+bool SbxValue::IsNumericRTL() const
+{
+ return ImpIsNumeric( /*bOnlyIntntl*/true );
+}
+
+bool SbxValue::ImpIsNumeric( bool bOnlyIntntl ) const
+{
+
+ if( !CanRead() )
+ {
+ SetError( ERRCODE_BASIC_PROP_WRITEONLY );
+ return false;
+ }
+ // Test downcast!!!
+ if( auto pSbxVar = dynamic_cast<const SbxVariable*>( this) )
+ const_cast<SbxVariable*>(pSbxVar)->Broadcast( SfxHintId::BasicDataWanted );
+ SbxDataType t = GetType();
+ if( t == SbxSTRING )
+ {
+ if( aData.pOUString )
+ {
+ OUString s( *aData.pOUString );
+ double n;
+ SbxDataType t2;
+ sal_uInt16 nLen = 0;
+ if( ImpScan( s, n, t2, &nLen, bOnlyIntntl ) == ERRCODE_NONE )
+ return nLen == s.getLength();
+ }
+ return false;
+ }
+ else
+ return t == SbxEMPTY
+ || ( t >= SbxINTEGER && t <= SbxCURRENCY )
+ || ( t >= SbxCHAR && t <= SbxUINT );
+}
+
+SbxDataType SbxValue::GetType() const
+{
+ return SbxDataType( aData.eType & 0x0FFF );
+}
+
+
+bool SbxValue::SetType( SbxDataType t )
+{
+ DBG_ASSERT( !( t & 0xF000 ), "SetType of BYREF|ARRAY is forbidden!" );
+ if( ( t == SbxEMPTY && aData.eType == SbxVOID )
+ || ( aData.eType == SbxEMPTY && t == SbxVOID ) )
+ return true;
+ if( ( t & 0x0FFF ) == SbxVARIANT )
+ {
+ // Try to set the data type to Variant
+ ResetFlag( SbxFlagBits::Fixed );
+ if( IsFixed() )
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ return false;
+ }
+ t = SbxEMPTY;
+ }
+ if( ( t & 0x0FFF ) == ( aData.eType & 0x0FFF ) )
+ return true;
+
+ if( !CanWrite() || IsFixed() )
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ return false;
+ }
+ else
+ {
+ // De-allocate potential objects
+ switch( aData.eType )
+ {
+ case SbxSTRING:
+ delete aData.pOUString;
+ break;
+ case SbxOBJECT:
+ if( aData.pObj && aData.pObj != this )
+ {
+ SAL_WARN("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
+ SbxVariable *pThisVar = dynamic_cast<SbxVariable*>( this );
+ sal_uInt32 nSlotId = pThisVar
+ ? pThisVar->GetUserData() & 0xFFFF
+ : 0;
+ DBG_ASSERT( nSlotId != 5345 || pThisVar->GetName() == "Parent",
+ "SID_PARENTOBJECT is not named 'Parent'" );
+ bool bParentProp = nSlotId == 5345;
+ if ( !bParentProp )
+ aData.pObj->ReleaseRef();
+ }
+ break;
+ default: break;
+ }
+ aData.clear(t);
+ }
+ return true;
+}
+
+bool SbxValue::Convert( SbxDataType eTo )
+{
+ eTo = SbxDataType( eTo & 0x0FFF );
+ if( ( aData.eType & 0x0FFF ) == eTo )
+ return true;
+ if( !CanWrite() )
+ return false;
+ if( eTo == SbxVARIANT )
+ {
+ // Trial to set the data type to Variant
+ ResetFlag( SbxFlagBits::Fixed );
+ if( IsFixed() )
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ return false;
+ }
+ else
+ return true;
+ }
+ // Converting from null doesn't work. Once null, always null!
+ if( aData.eType == SbxNULL )
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ return false;
+ }
+
+ // Conversion of the data:
+ SbxValues aNew(eTo);
+ if( Get( aNew ) )
+ {
+ // The data type could be converted. It ends here with fixed elements,
+ // because the data had not to be taken over
+ if( !IsFixed() )
+ {
+ SetType( eTo );
+ Put( aNew );
+ SetModified( true );
+ }
+ return true;
+ }
+ else
+ return false;
+}
+////////////////////////////////// Calculating
+
+bool SbxValue::Compute( SbxOperator eOp, const SbxValue& rOp )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ const bool bVBAInterop = false;
+#else
+ bool bVBAInterop = SbiRuntime::isVBAEnabled();
+#endif
+ SbxDataType eThisType = GetType();
+ SbxDataType eOpType = rOp.GetType();
+ ErrCode eOld = GetError();
+ if( eOld != ERRCODE_NONE )
+ ResetError();
+ if( !CanWrite() )
+ SetError( ERRCODE_BASIC_PROP_READONLY );
+ else if( !rOp.CanRead() )
+ SetError( ERRCODE_BASIC_PROP_WRITEONLY );
+ // Special rule 1: If one operand is null, the result is null
+ else if( eThisType == SbxNULL || eOpType == SbxNULL )
+ SetType( SbxNULL );
+ else
+ {
+ SbxValues aL, aR;
+ bool bDecimal = false;
+ if( bVBAInterop && ( ( eThisType == SbxSTRING && eOpType != SbxSTRING && eOpType != SbxEMPTY ) ||
+ ( eThisType != SbxSTRING && eThisType != SbxEMPTY && eOpType == SbxSTRING ) ) &&
+ ( eOp == SbxMUL || eOp == SbxDIV || eOp == SbxPLUS || eOp == SbxMINUS ) )
+ {
+ goto Lbl_OpIsDouble;
+ }
+ else if( eThisType == SbxSTRING || eOp == SbxCAT || ( bVBAInterop && ( eOpType == SbxSTRING ) && ( eOp == SbxPLUS ) ) )
+ {
+ if( eOp == SbxCAT || eOp == SbxPLUS )
+ {
+ // From 1999-11-5, keep OUString in mind
+ aL.eType = aR.eType = SbxSTRING;
+ rOp.Get( aR );
+ // From 1999-12-8, #70399: Here call GetType() again, Get() can change the type!
+ if( rOp.GetType() == SbxEMPTY )
+ goto Lbl_OpIsEmpty; // concatenate empty, *this stays lhs as result
+ Get( aL );
+
+ // #30576: To begin with test, if the conversion worked
+ if( aL.pOUString != nullptr && aR.pOUString != nullptr )
+ {
+ // tdf#108039: catch possible bad_alloc
+ try {
+ *aL.pOUString += *aR.pOUString;
+ }
+ catch (const std::bad_alloc&) {
+ SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ }
+ }
+ // Not even Left OK?
+ else if( aL.pOUString == nullptr )
+ {
+ aL.pOUString = new OUString();
+ }
+ }
+ else
+ SetError( ERRCODE_BASIC_CONVERSION );
+ }
+ else if( eOpType == SbxSTRING && rOp.IsFixed() )
+ { // Numeric: there is no String allowed on the right side
+ SetError( ERRCODE_BASIC_CONVERSION );
+ // falls all the way out
+ }
+ else if( ( eOp >= SbxIDIV && eOp <= SbxNOT ) || eOp == SbxMOD )
+ {
+ if( GetType() == eOpType )
+ {
+ if( GetType() == SbxSALUINT64 || GetType() == SbxSALINT64
+ || GetType() == SbxCURRENCY || GetType() == SbxULONG )
+ aL.eType = aR.eType = GetType();
+ // tdf#145960 - return type of boolean operators should be of type boolean
+ else if ( eOpType == SbxBOOL && eOp != SbxMOD && eOp != SbxIDIV )
+ aL.eType = aR.eType = SbxBOOL;
+ else
+ aL.eType = aR.eType = SbxLONG;
+ }
+ else
+ aL.eType = aR.eType = SbxLONG;
+
+ if( rOp.Get( aR ) ) // re-do Get after type assigns above
+ {
+ if( Get( aL ) ) switch( eOp )
+ {
+ /* TODO: For SbxEMPTY operands with boolean operators use
+ * the VBA Nothing definition of Comparing Nullable Types?
+ * https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/nullable-value-types
+ */
+ /* TODO: it is unclear yet whether this also should be done
+ * for the non-bVBAInterop case or not, or at all, consider
+ * user defined spreadsheet functions where an empty cell
+ * is SbxEMPTY and usually is treated as 0 zero or "" empty
+ * string.
+ */
+ case SbxIDIV:
+ if( aL.eType == SbxCURRENCY )
+ if( !aR.nInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
+ else {
+ aL.nInt64 /= aR.nInt64;
+ aL.nInt64 *= CURRENCY_FACTOR;
+ }
+ else if( aL.eType == SbxSALUINT64 )
+ if( !aR.uInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.uInt64 /= aR.uInt64;
+ else if( aL.eType == SbxSALINT64 )
+ if( !aR.nInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.nInt64 /= aR.nInt64;
+ else if( aL.eType == SbxLONG )
+ if( !aR.nLong ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.nLong /= aR.nLong;
+ else
+ if( !aR.nULong ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.nULong /= aR.nULong;
+ break;
+ case SbxMOD:
+ if( aL.eType == SbxCURRENCY || aL.eType == SbxSALINT64 )
+ if( !aR.nInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.nInt64 %= aR.nInt64;
+ else if( aL.eType == SbxSALUINT64 )
+ if( !aR.uInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.uInt64 %= aR.uInt64;
+ else if( aL.eType == SbxLONG )
+ if( !aR.nLong ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.nLong %= aR.nLong;
+ else
+ if( !aR.nULong ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.nULong %= aR.nULong;
+ break;
+ case SbxAND:
+ if( aL.eType != SbxLONG && aL.eType != SbxULONG )
+ aL.nInt64 &= aR.nInt64;
+ else
+ aL.nLong &= aR.nLong;
+ break;
+ case SbxOR:
+ if( aL.eType != SbxLONG && aL.eType != SbxULONG )
+ aL.nInt64 |= aR.nInt64;
+ else
+ aL.nLong |= aR.nLong;
+ break;
+ case SbxXOR:
+ if( aL.eType != SbxLONG && aL.eType != SbxULONG )
+ aL.nInt64 ^= aR.nInt64;
+ else
+ aL.nLong ^= aR.nLong;
+ break;
+ case SbxEQV:
+ if( aL.eType != SbxLONG && aL.eType != SbxULONG )
+ aL.nInt64 = (aL.nInt64 & aR.nInt64) | (~aL.nInt64 & ~aR.nInt64);
+ else
+ aL.nLong = (aL.nLong & aR.nLong) | (~aL.nLong & ~aR.nLong);
+ break;
+ case SbxIMP:
+ if( aL.eType != SbxLONG && aL.eType != SbxULONG )
+ aL.nInt64 = ~aL.nInt64 | aR.nInt64;
+ else
+ aL.nLong = ~aL.nLong | aR.nLong;
+ break;
+ case SbxNOT:
+ if( aL.eType != SbxLONG && aL.eType != SbxULONG )
+ {
+ if ( aL.eType != SbxBOOL )
+ aL.nInt64 = ~aL.nInt64;
+ else
+ aL.nLong = ~aL.nLong;
+ }
+ else
+ aL.nLong = ~aL.nLong;
+ break;
+ default: break;
+ }
+ }
+ }
+ else if( ( GetType() == SbxDECIMAL || rOp.GetType() == SbxDECIMAL )
+ && ( eOp == SbxMUL || eOp == SbxDIV || eOp == SbxPLUS || eOp == SbxMINUS || eOp == SbxNEG ) )
+ {
+ aL.eType = aR.eType = SbxDECIMAL;
+ bDecimal = true;
+ if( rOp.Get( aR ) && Get( aL ) )
+ {
+ if( aL.pDecimal && aR.pDecimal )
+ {
+ bool bOk = true;
+ switch( eOp )
+ {
+ case SbxMUL:
+ bOk = ( *(aL.pDecimal) *= *(aR.pDecimal) );
+ break;
+ case SbxDIV:
+ if( aR.pDecimal->isZero() )
+ SetError( ERRCODE_BASIC_ZERODIV );
+ else
+ bOk = ( *(aL.pDecimal) /= *(aR.pDecimal) );
+ break;
+ case SbxPLUS:
+ bOk = ( *(aL.pDecimal) += *(aR.pDecimal) );
+ break;
+ case SbxMINUS:
+ bOk = ( *(aL.pDecimal) -= *(aR.pDecimal) );
+ break;
+ case SbxNEG:
+ bOk = ( aL.pDecimal->neg() );
+ break;
+ default:
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ if( !bOk )
+ SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ }
+ else
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ }
+ }
+ }
+ else if( GetType() == SbxCURRENCY || rOp.GetType() == SbxCURRENCY )
+ {
+ aL.eType = SbxCURRENCY;
+ aR.eType = SbxCURRENCY;
+
+ if( rOp.Get( aR ) )
+ {
+ if( Get( aL ) ) switch( eOp )
+ {
+ case SbxMUL:
+ {
+ // first overflow check: see if product will fit - test real value of product (hence 2 curr factors)
+ double dTest = static_cast<double>(aL.nInt64) * static_cast<double>(aR.nInt64) / double(CURRENCY_FACTOR_SQUARE);
+ if( dTest < SbxMINCURR || SbxMAXCURR < dTest)
+ {
+ aL.nInt64 = SAL_MAX_INT64;
+ if( dTest < SbxMINCURR ) aL.nInt64 = SAL_MIN_INT64;
+ SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ // second overflow check: see if unscaled product overflows - if so use doubles
+ dTest = static_cast<double>(aL.nInt64) * static_cast<double>(aR.nInt64);
+ if( !(o3tl::convertsToAtLeast(dTest, SAL_MIN_INT64)
+ && o3tl::convertsToAtMost(dTest, SAL_MAX_INT64)))
+ {
+ aL.nInt64 = static_cast<sal_Int64>( dTest / double(CURRENCY_FACTOR) );
+ break;
+ }
+ // precise calc: multiply then scale back (move decimal pt)
+ aL.nInt64 *= aR.nInt64;
+ aL.nInt64 /= CURRENCY_FACTOR;
+ break;
+ }
+
+ case SbxDIV:
+ {
+ if( !aR.nInt64 )
+ {
+ SetError( ERRCODE_BASIC_ZERODIV );
+ break;
+ }
+ // first overflow check: see if quotient will fit - calc real value of quotient (curr factors cancel)
+ double dTest = static_cast<double>(aL.nInt64) / static_cast<double>(aR.nInt64);
+ if( dTest < SbxMINCURR || SbxMAXCURR < dTest)
+ {
+ SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ // second overflow check: see if scaled dividend overflows - if so use doubles
+ dTest = static_cast<double>(aL.nInt64) * double(CURRENCY_FACTOR);
+ if( !(o3tl::convertsToAtLeast(dTest, SAL_MIN_INT64)
+ && o3tl::convertsToAtMost(dTest, SAL_MAX_INT64)))
+ {
+ aL.nInt64 = static_cast<sal_Int64>(dTest / static_cast<double>(aR.nInt64));
+ break;
+ }
+ // precise calc: scale (move decimal pt) then divide
+ aL.nInt64 *= CURRENCY_FACTOR;
+ aL.nInt64 /= aR.nInt64;
+ break;
+ }
+
+ case SbxPLUS:
+ {
+ double dTest = ( static_cast<double>(aL.nInt64) + static_cast<double>(aR.nInt64) ) / double(CURRENCY_FACTOR);
+ if( dTest < SbxMINCURR || SbxMAXCURR < dTest)
+ {
+ SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ aL.nInt64 += aR.nInt64;
+ break;
+ }
+
+ case SbxMINUS:
+ {
+ double dTest = ( static_cast<double>(aL.nInt64) - static_cast<double>(aR.nInt64) ) / double(CURRENCY_FACTOR);
+ if( dTest < SbxMINCURR || SbxMAXCURR < dTest)
+ {
+ SetError( ERRCODE_BASIC_MATH_OVERFLOW );
+ break;
+ }
+ aL.nInt64 -= aR.nInt64;
+ break;
+ }
+ case SbxNEG:
+ aL.nInt64 = -aL.nInt64;
+ break;
+ default:
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ }
+ }
+ else
+Lbl_OpIsDouble:
+ { // other types and operators including Date, Double and Single
+ aL.eType = aR.eType = SbxDOUBLE;
+ if( rOp.Get( aR ) )
+ {
+ if( Get( aL ) )
+ {
+ switch( eOp )
+ {
+ case SbxEXP:
+ aL.nDouble = pow( aL.nDouble, aR.nDouble );
+ break;
+ case SbxMUL:
+ aL.nDouble *= aR.nDouble; break;
+ case SbxDIV:
+ if( !aR.nDouble ) SetError( ERRCODE_BASIC_ZERODIV );
+ else aL.nDouble /= aR.nDouble;
+ break;
+ case SbxPLUS:
+ aL.nDouble += aR.nDouble; break;
+ case SbxMINUS:
+ aL.nDouble -= aR.nDouble; break;
+ case SbxNEG:
+ aL.nDouble = -aL.nDouble; break;
+ default:
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ // Date with "+" or "-" needs special handling that
+ // forces the Date type. If the operation is '+' the
+ // result is always a Date, if '-' the result is only
+ // a Date if one of lhs or rhs ( but not both ) is already
+ // a Date
+ if( GetType() == SbxDATE || rOp.GetType() == SbxDATE )
+ {
+ if( eOp == SbxPLUS || ( ( eOp == SbxMINUS ) && ( GetType() != rOp.GetType() ) ) )
+ aL.eType = SbxDATE;
+ }
+
+ }
+ }
+
+ }
+ if( !IsError() )
+ Put( aL );
+ if( bDecimal )
+ {
+ releaseDecimalPtr( aL.pDecimal );
+ releaseDecimalPtr( aR.pDecimal );
+ }
+ }
+Lbl_OpIsEmpty:
+
+ bool bRes = !IsError();
+ if( bRes && eOld != ERRCODE_NONE )
+ SetError( eOld );
+ return bRes;
+}
+
+// The comparison routine deliver TRUE or FALSE.
+
+bool SbxValue::Compare( SbxOperator eOp, const SbxValue& rOp ) const
+{
+#if !HAVE_FEATURE_SCRIPTING
+ const bool bVBAInterop = false;
+#else
+ bool bVBAInterop = SbiRuntime::isVBAEnabled();
+#endif
+
+ bool bRes = false;
+ ErrCode eOld = GetError();
+ if( eOld != ERRCODE_NONE )
+ ResetError();
+ if( !CanRead() || !rOp.CanRead() )
+ SetError( ERRCODE_BASIC_PROP_WRITEONLY );
+ else if( GetType() == SbxNULL && rOp.GetType() == SbxNULL && !bVBAInterop )
+ {
+ bRes = true;
+ }
+ else if( GetType() == SbxEMPTY && rOp.GetType() == SbxEMPTY )
+ bRes = !bVBAInterop || ( eOp == SbxEQ );
+ // Special rule 1: If an operand is null, the result is FALSE
+ else if( GetType() == SbxNULL || rOp.GetType() == SbxNULL )
+ bRes = false;
+ // Special rule 2: If both are variant and one is numeric
+ // and the other is a String, num is < str
+ else if( !IsFixed() && !rOp.IsFixed()
+ && ( rOp.GetType() == SbxSTRING && GetType() != SbxSTRING && IsNumeric() ) && !bVBAInterop
+ )
+ bRes = eOp == SbxLT || eOp == SbxLE || eOp == SbxNE;
+ else if( !IsFixed() && !rOp.IsFixed()
+ && ( GetType() == SbxSTRING && rOp.GetType() != SbxSTRING && rOp.IsNumeric() )
+&& !bVBAInterop
+ )
+ bRes = eOp == SbxGT || eOp == SbxGE || eOp == SbxNE;
+ else
+ {
+ SbxValues aL, aR;
+ // If one of the operands is a String,
+ // a String comparing take place
+ if( GetType() == SbxSTRING || rOp.GetType() == SbxSTRING )
+ {
+ aL.eType = aR.eType = SbxSTRING;
+ if( Get( aL ) && rOp.Get( aR ) ) switch( eOp )
+ {
+ case SbxEQ:
+ bRes = ( *aL.pOUString == *aR.pOUString ); break;
+ case SbxNE:
+ bRes = ( *aL.pOUString != *aR.pOUString ); break;
+ case SbxLT:
+ bRes = ( *aL.pOUString < *aR.pOUString ); break;
+ case SbxGT:
+ bRes = ( *aL.pOUString > *aR.pOUString ); break;
+ case SbxLE:
+ bRes = ( *aL.pOUString <= *aR.pOUString ); break;
+ case SbxGE:
+ bRes = ( *aL.pOUString >= *aR.pOUString ); break;
+ default:
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ }
+ // From 1995-12-19: If SbxSINGLE participate, then convert to SINGLE,
+ // otherwise it shows a numeric error
+ else if( GetType() == SbxSINGLE || rOp.GetType() == SbxSINGLE )
+ {
+ aL.eType = aR.eType = SbxSINGLE;
+ if( Get( aL ) && rOp.Get( aR ) )
+ switch( eOp )
+ {
+ case SbxEQ:
+ bRes = ( aL.nSingle == aR.nSingle ); break;
+ case SbxNE:
+ bRes = ( aL.nSingle != aR.nSingle ); break;
+ case SbxLT:
+ bRes = ( aL.nSingle < aR.nSingle ); break;
+ case SbxGT:
+ bRes = ( aL.nSingle > aR.nSingle ); break;
+ case SbxLE:
+ bRes = ( aL.nSingle <= aR.nSingle ); break;
+ case SbxGE:
+ bRes = ( aL.nSingle >= aR.nSingle ); break;
+ default:
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ }
+ else if( GetType() == SbxDECIMAL && rOp.GetType() == SbxDECIMAL )
+ {
+ aL.eType = aR.eType = SbxDECIMAL;
+ Get( aL );
+ rOp.Get( aR );
+ if( aL.pDecimal && aR.pDecimal )
+ {
+ SbxDecimal::CmpResult eRes = compare( *aL.pDecimal, *aR.pDecimal );
+ switch( eOp )
+ {
+ case SbxEQ:
+ bRes = ( eRes == SbxDecimal::CmpResult::EQ ); break;
+ case SbxNE:
+ bRes = ( eRes != SbxDecimal::CmpResult::EQ ); break;
+ case SbxLT:
+ bRes = ( eRes == SbxDecimal::CmpResult::LT ); break;
+ case SbxGT:
+ bRes = ( eRes == SbxDecimal::CmpResult::GT ); break;
+ case SbxLE:
+ bRes = ( eRes != SbxDecimal::CmpResult::GT ); break;
+ case SbxGE:
+ bRes = ( eRes != SbxDecimal::CmpResult::LT ); break;
+ default:
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ }
+ else
+ {
+ SetError( ERRCODE_BASIC_CONVERSION );
+ }
+ releaseDecimalPtr( aL.pDecimal );
+ releaseDecimalPtr( aR.pDecimal );
+ }
+ // Everything else comparing on a SbxDOUBLE-Basis
+ else
+ {
+ aL.eType = aR.eType = SbxDOUBLE;
+ bool bGetL = Get( aL );
+ bool bGetR = rOp.Get( aR );
+ if( bGetL && bGetR )
+ switch( eOp )
+ {
+ case SbxEQ:
+ bRes = ( aL.nDouble == aR.nDouble ); break;
+ case SbxNE:
+ bRes = ( aL.nDouble != aR.nDouble ); break;
+ case SbxLT:
+ bRes = ( aL.nDouble < aR.nDouble ); break;
+ case SbxGT:
+ bRes = ( aL.nDouble > aR.nDouble ); break;
+ case SbxLE:
+ bRes = ( aL.nDouble <= aR.nDouble ); break;
+ case SbxGE:
+ bRes = ( aL.nDouble >= aR.nDouble ); break;
+ default:
+ SetError( ERRCODE_BASIC_BAD_ARGUMENT );
+ }
+ // at least one value was got
+ // if this is VBA then a conversion error for one
+ // side will yield a false result of an equality test
+ else if ( bGetR || bGetL )
+ {
+ if ( bVBAInterop && eOp == SbxEQ && GetError() == ERRCODE_BASIC_CONVERSION )
+ {
+#ifndef IOS
+ ResetError();
+ bRes = false;
+#endif
+ }
+ }
+ }
+ }
+ if( eOld != ERRCODE_NONE )
+ SetError( eOld );
+ return bRes;
+}
+
+///////////////////////////// Reading/Writing
+
+bool SbxValue::LoadData( SvStream& r, sal_uInt16 )
+{
+ // #TODO see if these types are really dumped to any stream
+ // more than likely this is functionality used in the binfilter alone
+ SbxValue::Clear();
+ sal_uInt16 nType;
+ r.ReadUInt16( nType );
+ aData.eType = SbxDataType( nType );
+ switch( nType )
+ {
+ case SbxBOOL:
+ case SbxINTEGER:
+ r.ReadInt16( aData.nInteger ); break;
+ case SbxLONG:
+ r.ReadInt32( aData.nLong ); break;
+ case SbxSINGLE:
+ {
+ // Floats as ASCII
+ OUString aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(r,
+ RTL_TEXTENCODING_ASCII_US);
+ double d;
+ SbxDataType t;
+ if( ImpScan( aVal, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE || t == SbxDOUBLE )
+ {
+ aData.nSingle = 0.0F;
+ return false;
+ }
+ aData.nSingle = static_cast<float>(d);
+ break;
+ }
+ case SbxDATE:
+ case SbxDOUBLE:
+ {
+ // Floats as ASCII
+ OUString aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(r,
+ RTL_TEXTENCODING_ASCII_US);
+ SbxDataType t;
+ if( ImpScan( aVal, aData.nDouble, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ {
+ aData.nDouble = 0.0;
+ return false;
+ }
+ break;
+ }
+ case SbxSALINT64:
+ r.ReadInt64(aData.nInt64);
+ break;
+ case SbxSALUINT64:
+ r.ReadUInt64( aData.uInt64 );
+ break;
+ case SbxCURRENCY:
+ {
+ sal_uInt32 tmpHi = 0;
+ sal_uInt32 tmpLo = 0;
+ r.ReadUInt32( tmpHi ).ReadUInt32( tmpLo );
+ aData.nInt64 = (static_cast<sal_Int64>(tmpHi) << 32);
+ aData.nInt64 |= static_cast<sal_Int64>(tmpLo);
+ break;
+ }
+ case SbxSTRING:
+ {
+ OUString aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(r,
+ RTL_TEXTENCODING_ASCII_US);
+ if( !aVal.isEmpty() )
+ aData.pOUString = new OUString( aVal );
+ else
+ aData.pOUString = nullptr; // JSM 1995-09-22
+ break;
+ }
+ case SbxERROR:
+ case SbxUSHORT:
+ r.ReadUInt16( aData.nUShort ); break;
+ case SbxOBJECT:
+ {
+ sal_uInt8 nMode;
+ r.ReadUChar( nMode );
+ switch( nMode )
+ {
+ case 0:
+ aData.pObj = nullptr;
+ break;
+ case 1:
+ {
+ auto ref = SbxBase::Load( r );
+ aData.pObj = ref.get();
+ // if necessary increment Ref-Count
+ if (aData.pObj)
+ aData.pObj->AddFirstRef();
+ return ( aData.pObj != nullptr );
+ }
+ case 2:
+ aData.pObj = this;
+ break;
+ }
+ break;
+ }
+ case SbxCHAR:
+ {
+ char c;
+ r.ReadChar( c );
+ aData.nChar = c;
+ break;
+ }
+ case SbxBYTE:
+ r.ReadUChar( aData.nByte ); break;
+ case SbxULONG:
+ r.ReadUInt32( aData.nULong ); break;
+ case SbxINT:
+ {
+ sal_uInt8 n;
+ r.ReadUChar( n );
+ // Match the Int on this system?
+ if( n > SAL_TYPES_SIZEOFINT )
+ {
+ r.ReadInt32( aData.nLong );
+ aData.eType = SbxLONG;
+ }
+ else {
+ sal_Int32 nInt;
+ r.ReadInt32( nInt );
+ aData.nInt = nInt;
+ }
+ break;
+ }
+ case SbxUINT:
+ {
+ sal_uInt8 n;
+ r.ReadUChar( n );
+ // Match the UInt on this system?
+ if( n > SAL_TYPES_SIZEOFINT )
+ {
+ r.ReadUInt32( aData.nULong );
+ aData.eType = SbxULONG;
+ }
+ else {
+ sal_uInt32 nUInt;
+ r.ReadUInt32( nUInt );
+ aData.nUInt = nUInt;
+ }
+ break;
+ }
+ case SbxEMPTY:
+ case SbxNULL:
+ case SbxVOID:
+ break;
+ case SbxDATAOBJECT:
+ r.ReadInt32( aData.nLong );
+ break;
+ // #78919 For backwards compatibility
+ case SbxWSTRING:
+ case SbxWCHAR:
+ break;
+ default:
+ aData.clear(SbxNULL);
+ ResetFlag(SbxFlagBits::Fixed);
+ SAL_WARN( "basic.sbx", "Loaded a non-supported data type" );
+
+ return false;
+ }
+ return true;
+}
+
+ std::pair<bool, sal_uInt32> SbxValue::StoreData( SvStream& r ) const
+ {
+ sal_uInt16 nType = sal::static_int_cast< sal_uInt16 >(aData.eType);
+ r.WriteUInt16( nType );
+ switch( nType & 0x0FFF )
+ {
+ case SbxBOOL:
+ case SbxINTEGER:
+ r.WriteInt16( aData.nInteger ); break;
+ case SbxLONG:
+ r.WriteInt32( aData.nLong ); break;
+ case SbxDATE:
+ // #49935: Save as double, otherwise an error during the read in
+ const_cast<SbxValue*>(this)->aData.eType = static_cast<SbxDataType>( ( nType & 0xF000 ) | SbxDOUBLE );
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(r, GetCoreString(), RTL_TEXTENCODING_ASCII_US);
+ const_cast<SbxValue*>(this)->aData.eType = static_cast<SbxDataType>(nType);
+ break;
+ case SbxSINGLE:
+ case SbxDOUBLE:
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(r, GetCoreString(), RTL_TEXTENCODING_ASCII_US);
+ break;
+ case SbxSALUINT64:
+ case SbxSALINT64:
+ // see comment in SbxValue::StoreData
+ r.WriteUInt64( aData.uInt64 );
+ break;
+ case SbxCURRENCY:
+ {
+ sal_Int32 tmpHi = ( (aData.nInt64 >> 32) & 0xFFFFFFFF );
+ sal_Int32 tmpLo = static_cast<sal_Int32>(aData.nInt64);
+ r.WriteInt32( tmpHi ).WriteInt32( tmpLo );
+ break;
+ }
+ case SbxSTRING:
+ if( aData.pOUString )
+ {
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(r, *aData.pOUString, RTL_TEXTENCODING_ASCII_US);
+ }
+ else
+ {
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(r, std::u16string_view(), RTL_TEXTENCODING_ASCII_US);
+ }
+ break;
+ case SbxERROR:
+ case SbxUSHORT:
+ r.WriteUInt16( aData.nUShort ); break;
+ case SbxOBJECT:
+ // to save itself as Objectptr does not work!
+ if( aData.pObj )
+ {
+ if( dynamic_cast<SbxValue*>( aData.pObj) != this )
+ {
+ r.WriteUChar( 1 );
+ return aData.pObj->Store( r );
+ }
+ else
+ r.WriteUChar( 2 );
+ }
+ else
+ r.WriteUChar( 0 );
+ break;
+ case SbxCHAR:
+ {
+ char c = sal::static_int_cast< char >(aData.nChar);
+ r.WriteChar( c );
+ break;
+ }
+ case SbxBYTE:
+ r.WriteUChar( aData.nByte ); break;
+ case SbxULONG:
+ r.WriteUInt32( aData.nULong ); break;
+ case SbxINT:
+ {
+ r.WriteUChar( SAL_TYPES_SIZEOFINT ).WriteInt32( aData.nInt );
+ break;
+ }
+ case SbxUINT:
+ {
+ r.WriteUChar( SAL_TYPES_SIZEOFINT ).WriteUInt32( aData.nUInt );
+ break;
+ }
+ case SbxEMPTY:
+ case SbxNULL:
+ case SbxVOID:
+ break;
+ case SbxDATAOBJECT:
+ r.WriteInt32( aData.nLong );
+ break;
+ // #78919 For backwards compatibility
+ case SbxWSTRING:
+ case SbxWCHAR:
+ break;
+ default:
+ SAL_WARN( "basic.sbx", "Saving a non-supported data type" );
+ return { false, 0 };
+ }
+ return { true, B_IMG_VERSION_12 };
+ }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/sbx/sbxvar.cxx b/basic/source/sbx/sbxvar.cxx
new file mode 100644
index 0000000000..cc57a804bc
--- /dev/null
+++ b/basic/source/sbx/sbxvar.cxx
@@ -0,0 +1,604 @@
+/* -*- 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/stream.hxx>
+#include <svl/SfxBroadcaster.hxx>
+
+#include <basic/sbx.hxx>
+#include <runtime.hxx>
+#include "sbxres.hxx"
+#include "sbxconv.hxx"
+#include <rtlproto.hxx>
+#include <sbunoobj.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <global.hxx>
+#include <unotools/transliterationwrapper.hxx>
+
+#include <com/sun/star/uno/XInterface.hpp>
+#include <utility>
+#include <filefmt.hxx>
+using namespace com::sun::star::uno;
+
+// SbxVariable
+
+SbxVariable::SbxVariable()
+{
+}
+
+SbxVariable::SbxVariable( const SbxVariable& r )
+ : SvRefBase( r ),
+ SbxValue( r ),
+ m_aDeclareClassName( r.m_aDeclareClassName ),
+ m_xComListener( r.m_xComListener),
+ mpPar( r.mpPar ),
+ pInfo( r.pInfo )
+{
+#if HAVE_FEATURE_SCRIPTING
+ if( r.m_xComListener.is() )
+ {
+ registerComListenerVariableForBasic( this, r.m_pComListenerParentBasic );
+ }
+#endif
+ if( r.CanRead() )
+ {
+ pParent = r.pParent;
+ nUserData = r.nUserData;
+ maName = r.maName;
+ nHash = r.nHash;
+ }
+}
+
+SbxEnsureParentVariable::SbxEnsureParentVariable(const SbxVariable& r)
+ : SbxVariable(r)
+ , xParent(const_cast<SbxVariable&>(r).GetParent())
+{
+ assert(GetParent() == xParent.get());
+}
+
+void SbxEnsureParentVariable::SetParent(SbxObject* p)
+{
+ assert(GetParent() == xParent.get());
+ SbxVariable::SetParent(p);
+ xParent = SbxObjectRef(p);
+ assert(GetParent() == xParent.get());
+}
+
+SbxVariable::SbxVariable( SbxDataType t ) : SbxValue( t )
+{
+}
+
+SbxVariable::~SbxVariable()
+{
+#if HAVE_FEATURE_SCRIPTING
+ if( IsSet( SbxFlagBits::DimAsNew ))
+ {
+ removeDimAsNewRecoverItem( this );
+ }
+#endif
+ mpBroadcaster.reset();
+}
+
+// Broadcasting
+
+SfxBroadcaster& SbxVariable::GetBroadcaster()
+{
+ if( !mpBroadcaster )
+ {
+ mpBroadcaster.reset( new SfxBroadcaster );
+ }
+ return *mpBroadcaster;
+}
+
+SbxArray* SbxVariable::GetParameters() const
+{
+ return mpPar.get();
+}
+
+
+// Perhaps some day one could cut the parameter 0.
+// Then the copying will be dropped...
+
+void SbxVariable::Broadcast( SfxHintId nHintId )
+{
+ if( !mpBroadcaster || IsSet( SbxFlagBits::NoBroadcast ) )
+ return;
+
+ // Because the method could be called from outside, check the
+ // rights here again
+ if( nHintId == SfxHintId::BasicDataWanted )
+ {
+ if( !CanRead() )
+ {
+ return;
+ }
+ }
+ if( nHintId == SfxHintId::BasicDataChanged )
+ {
+ if( !CanWrite() )
+ {
+ return;
+ }
+ }
+
+ //fdo#86843 Add a ref during the following block to guard against
+ //getting deleted before completing this method
+ SbxVariableRef aBroadcastGuard(this);
+
+ // Avoid further broadcasting
+ std::unique_ptr<SfxBroadcaster> pSave = std::move(mpBroadcaster);
+ SbxFlagBits nSaveFlags = GetFlags();
+ SetFlag( SbxFlagBits::ReadWrite );
+ if( mpPar.is() )
+ {
+ // Register this as element 0, but don't change over the parent!
+ mpPar->GetRef(0) = this;
+ }
+ pSave->Broadcast( SbxHint( nHintId, this ) );
+ mpBroadcaster = std::move(pSave);
+ SetFlags( nSaveFlags );
+}
+
+SbxInfo* SbxVariable::GetInfo()
+{
+ if( !pInfo.is() )
+ {
+ Broadcast( SfxHintId::BasicInfoWanted );
+ if( pInfo.is() )
+ {
+ SetModified( true );
+ }
+ }
+ return pInfo.get();
+}
+
+void SbxVariable::SetInfo( SbxInfo* p )
+{
+ pInfo = p;
+}
+
+void SbxVariable::SetParameters( SbxArray* p )
+{
+ mpPar = p;
+}
+
+
+// Name of the variables
+
+// static
+OUString SbxVariable::NameToCaseInsensitiveName(const OUString& rName)
+{
+ return SbGlobal::GetTransliteration().transliterate(rName, 0, rName.getLength());
+}
+
+void SbxVariable::SetName( const OUString& rName )
+{
+ maName = rName;
+ nHash = MakeHashCode( rName );
+ maNameCI.clear();
+}
+
+const OUString& SbxVariable::GetName( SbxNameType t ) const
+{
+ static const char cSuffixes[] = " %&!#@ $";
+ if( t == SbxNameType::NONE )
+ {
+ return maName;
+ }
+ if (t == SbxNameType::CaseInsensitive)
+ {
+ if (maNameCI.isEmpty() && !maName.isEmpty())
+ maNameCI = NameToCaseInsensitiveName(maName);
+ return maNameCI;
+ }
+ // Request parameter-information (not for objects)
+ const_cast<SbxVariable*>(this)->GetInfo();
+ // Append nothing, if it is a simple property (no empty brackets)
+ if (!pInfo.is() || (pInfo->m_Params.empty() && GetClass() == SbxClassType::Property))
+ {
+ return maName;
+ }
+ sal_Unicode cType = ' ';
+ OUStringBuffer aTmp( maName );
+ // short type? Then fetch it, possible this is 0.
+ SbxDataType et = GetType();
+ if( t == SbxNameType::ShortTypes )
+ {
+ if( et <= SbxSTRING )
+ {
+ cType = cSuffixes[ et ];
+ }
+ if( cType != ' ' )
+ {
+ aTmp.append(cType);
+ }
+ }
+ aTmp.append("(");
+
+ for (SbxParams::const_iterator iter = pInfo->m_Params.begin(); iter != pInfo->m_Params.end(); ++iter)
+ {
+ auto const& i = *iter;
+ int nt = i->eType & 0x0FFF;
+ if (iter != pInfo->m_Params.begin())
+ {
+ aTmp.append(",");
+ }
+ if( i->nFlags & SbxFlagBits::Optional )
+ {
+ aTmp.append( GetSbxRes( StringId::Optional ) );
+ }
+ if( i->eType & SbxBYREF )
+ {
+ aTmp.append( GetSbxRes( StringId::ByRef ) );
+ }
+ aTmp.append( i->aName );
+ cType = ' ';
+ // short type? Then fetch it, possible this is 0.
+ if( t == SbxNameType::ShortTypes )
+ {
+ if( nt <= SbxSTRING )
+ {
+ cType = cSuffixes[ nt ];
+ }
+ }
+ if( cType != ' ' )
+ {
+ aTmp.append(cType);
+ if( i->eType & SbxARRAY )
+ {
+ aTmp.append("()");
+ }
+ }
+ else
+ {
+ if( i->eType & SbxARRAY )
+ {
+ aTmp.append("()");
+ }
+ // long type?
+ aTmp.append(GetSbxRes( StringId::As ));
+ if( nt < 32 )
+ {
+ aTmp.append(GetSbxRes( static_cast<StringId>( static_cast<int>( StringId::Types ) + nt ) ));
+ }
+ else
+ {
+ aTmp.append(GetSbxRes( StringId::Any ));
+ }
+ }
+ }
+ aTmp.append(")");
+ const_cast<SbxVariable*>(this)->aToolString = aTmp.makeStringAndClear();
+ return aToolString;
+}
+
+// Operators
+
+SbxVariable& SbxVariable::operator=( const SbxVariable& r )
+{
+ if (this != &r)
+ {
+ SbxValue::operator=( r );
+ // tdf#144353 - copy information about a missing parameter. See SbiRuntime::SetIsMissing.
+ // We cannot unconditionally assign the data about a variable because we would overwrite
+ // the information about parameters (name, type, flags, and ids). For instance, in the case
+ // where a method will be initialized with a literal.
+ if (!pInfo)
+ pInfo = r.pInfo;
+ m_aDeclareClassName = r.m_aDeclareClassName;
+ m_xComListener = r.m_xComListener;
+ m_pComListenerParentBasic = r.m_pComListenerParentBasic;
+#if HAVE_FEATURE_SCRIPTING
+ if( m_xComListener.is() )
+ {
+ registerComListenerVariableForBasic( this, m_pComListenerParentBasic );
+ }
+#endif
+ }
+ return *this;
+}
+
+// Conversion
+
+SbxDataType SbxVariable::GetType() const
+{
+ if( aData.eType == SbxOBJECT )
+ {
+ return aData.pObj ? aData.pObj->GetType() : SbxOBJECT;
+ }
+ else if( aData.eType == SbxVARIANT )
+ {
+ return aData.pObj ? aData.pObj->GetType() : SbxVARIANT;
+ }
+ else
+ {
+ return aData.eType;
+ }
+}
+
+SbxClassType SbxVariable::GetClass() const
+{
+ return SbxClassType::Variable;
+}
+
+void SbxVariable::SetModified( bool b )
+{
+ if( IsSet( SbxFlagBits::NoModify ) )
+ {
+ return;
+ }
+ SbxBase::SetModified( b );
+ if( pParent && pParent != this ) //??? HotFix: Recursion out here MM
+ {
+ pParent->SetModified( b );
+ }
+}
+
+void SbxVariable::SetParent( SbxObject* p )
+{
+#ifdef DBG_UTIL
+ // Will the parent of a SbxObject be set?
+ if (p && dynamic_cast<SbxObject*>(this))
+ {
+ // then this had to be a child of the new parent
+ bool bFound = false;
+ SbxArray *pChildren = p->GetObjects();
+ if ( pChildren )
+ {
+ for (sal_uInt32 nIdx = 0; !bFound && nIdx < pChildren->Count(); ++nIdx)
+ {
+ bFound = (this == pChildren->Get(nIdx));
+ }
+ }
+ SAL_INFO_IF(
+ !bFound, "basic.sbx",
+ "dangling: [" << GetName() << "].SetParent([" << p->GetName()
+ << "])");
+ }
+#endif
+
+ pParent = p;
+}
+
+const OUString& SbxVariable::GetDeclareClassName() const
+{
+ return m_aDeclareClassName;
+}
+
+void SbxVariable::SetDeclareClassName( const OUString& rDeclareClassName )
+{
+ m_aDeclareClassName = rDeclareClassName;
+}
+
+void SbxVariable::SetComListener( const css::uno::Reference< css::uno::XInterface >& xComListener,
+ StarBASIC* pParentBasic )
+{
+ m_xComListener = xComListener;
+ m_pComListenerParentBasic = pParentBasic;
+#if HAVE_FEATURE_SCRIPTING
+ registerComListenerVariableForBasic( this, pParentBasic );
+#endif
+}
+
+void SbxVariable::ClearComListener()
+{
+ m_xComListener.clear();
+}
+
+
+// Loading/Saving
+
+bool SbxVariable::LoadData( SvStream& rStrm, sal_uInt16 nVer )
+{
+ sal_uInt8 cMark;
+ rStrm.ReadUChar( cMark );
+ if( cMark == 0xFF )
+ {
+ if( !SbxValue::LoadData( rStrm, nVer ) )
+ {
+ return false;
+ }
+ maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
+ RTL_TEXTENCODING_ASCII_US);
+ sal_uInt32 nTemp;
+ rStrm.ReadUInt32( nTemp );
+ nUserData = nTemp;
+ }
+ else
+ {
+ sal_uInt16 nType;
+ rStrm.SeekRel( -1 );
+ rStrm.ReadUInt16( nType );
+ maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
+ RTL_TEXTENCODING_ASCII_US);
+ sal_uInt32 nTemp;
+ rStrm.ReadUInt32( nTemp );
+ nUserData = nTemp;
+ // correction: old methods have instead of SbxNULL now SbxEMPTY
+ if( nType == SbxNULL && GetClass() == SbxClassType::Method )
+ {
+ nType = SbxEMPTY;
+ }
+ SbxValues aTmp;
+ OUString aTmpString;
+ OUString aVal;
+ aTmp.eType = aData.eType = static_cast<SbxDataType>(nType);
+ aTmp.pOUString = &aVal;
+ switch( nType )
+ {
+ case SbxBOOL:
+ case SbxERROR:
+ case SbxINTEGER:
+ rStrm.ReadInt16( aTmp.nInteger ); break;
+ case SbxLONG:
+ rStrm.ReadInt32( aTmp.nLong ); break;
+ case SbxSINGLE:
+ {
+ // Floats as ASCII
+ aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(
+ rStrm, RTL_TEXTENCODING_ASCII_US);
+ double d;
+ SbxDataType t;
+ if( ImpScan( aTmpString, d, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE || t == SbxDOUBLE )
+ {
+ aTmp.nSingle = 0;
+ return false;
+ }
+ aTmp.nSingle = static_cast<float>(d);
+ break;
+ }
+ case SbxDATE:
+ case SbxDOUBLE:
+ {
+ // Floats as ASCII
+ aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
+ RTL_TEXTENCODING_ASCII_US);
+ SbxDataType t;
+ if( ImpScan( aTmpString, aTmp.nDouble, t, nullptr, !LibreOffice6FloatingPointMode() ) != ERRCODE_NONE )
+ {
+ aTmp.nDouble = 0;
+ return false;
+ }
+ break;
+ }
+ case SbxSTRING:
+ aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
+ RTL_TEXTENCODING_ASCII_US);
+ break;
+ case SbxEMPTY:
+ case SbxNULL:
+ break;
+ default:
+ aData.eType = SbxNULL;
+ SAL_WARN( "basic.sbx", "Loaded a non-supported data type" );
+ return false;
+ }
+ // putt value
+ if( nType != SbxNULL && nType != SbxEMPTY && !Put( aTmp ) )
+ {
+ return false;
+ }
+ }
+ rStrm.ReadUChar( cMark );
+ // cMark is also a version number!
+ // 1: initial version
+ // 2: with nUserData
+ if( cMark )
+ {
+ if( cMark > 2 )
+ {
+ return false;
+ }
+ pInfo = new SbxInfo;
+ pInfo->LoadData( rStrm, static_cast<sal_uInt16>(cMark) );
+ }
+ Broadcast( SfxHintId::BasicDataChanged );
+ nHash = MakeHashCode( maName );
+ SetModified( true );
+ return true;
+}
+
+std::pair<bool, sal_uInt32> SbxVariable::StoreData( SvStream& rStrm ) const
+{
+ rStrm.WriteUChar( 0xFF ); // Marker
+ bool bValStore;
+ if( dynamic_cast<const SbxMethod *>(this) != nullptr )
+ {
+ // #50200 Avoid that objects , which during the runtime
+ // as return-value are saved in the method as a value were saved
+ SbxVariable* pThis = const_cast<SbxVariable*>(this);
+ SbxFlagBits nSaveFlags = GetFlags();
+ pThis->SetFlag( SbxFlagBits::Write );
+ pThis->SbxValue::Clear();
+ pThis->SetFlags( nSaveFlags );
+
+ // So that the method will not be executed in any case!
+ // CAST, to avoid const!
+ pThis->SetFlag( SbxFlagBits::NoBroadcast );
+ bValStore = SbxValue::StoreData( rStrm ).first;
+ pThis->ResetFlag( SbxFlagBits::NoBroadcast );
+ }
+ else
+ {
+ bValStore = SbxValue::StoreData( rStrm ).first;
+ }
+ if( !bValStore )
+ {
+ return { false, 0 };
+ }
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, maName,
+ RTL_TEXTENCODING_ASCII_US);
+ rStrm.WriteUInt32( nUserData );
+ if( pInfo.is() )
+ {
+ rStrm.WriteUChar( 2 ); // Version 2: with UserData!
+ pInfo->StoreData( rStrm );
+ }
+ else
+ {
+ rStrm.WriteUChar( 0 );
+ }
+ return { true, B_IMG_VERSION_12 };
+}
+
+// SbxInfo
+
+SbxInfo::SbxInfo()
+ : nHelpId(0)
+{}
+
+SbxInfo::SbxInfo( OUString a, sal_uInt32 n )
+ : aHelpFile(std::move( a )), nHelpId( n )
+{}
+
+void SbxVariable::Dump( SvStream& rStrm, bool bFill )
+{
+ OString aBNameStr(OUStringToOString(GetName( SbxNameType::ShortTypes ), RTL_TEXTENCODING_ASCII_US));
+ rStrm.WriteOString( "Variable( " )
+ .WriteOString( OString::number(reinterpret_cast<sal_IntPtr>(this)) ).WriteOString( "==" )
+ .WriteOString( aBNameStr );
+ OString aBParentNameStr(OUStringToOString(GetParent()->GetName(), RTL_TEXTENCODING_ASCII_US));
+ if ( GetParent() )
+ {
+ rStrm.WriteOString( " in parent '" ).WriteOString( aBParentNameStr ).WriteOString( "'" );
+ }
+ else
+ {
+ rStrm.WriteOString( " no parent" );
+ }
+ rStrm.WriteOString( " ) " );
+
+ // output also the object at object-vars
+ if ( GetValues_Impl().eType == SbxOBJECT &&
+ GetValues_Impl().pObj &&
+ GetValues_Impl().pObj != this &&
+ GetValues_Impl().pObj != GetParent() )
+ {
+ rStrm.WriteOString( " contains " );
+ static_cast<SbxObject*>(GetValues_Impl().pObj)->Dump( rStrm, bFill );
+ }
+ else
+ {
+ rStrm << endl;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */