diff options
Diffstat (limited to 'basic/source/sbx')
26 files changed, 11744 insertions, 0 deletions
diff --git a/basic/source/sbx/sbxarray.cxx b/basic/source/sbx/sbxarray.cxx new file mode 100644 index 000000000..ec95e5e10 --- /dev/null +++ b/basic/source/sbx/sbxarray.cxx @@ -0,0 +1,576 @@ +/* -*- 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> + +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; +} + +bool 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 ); + 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 ); + if (!rEntry.mpVar->Store(rStrm)) + return false; + } + } + return true; +} + +// #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 ); +} + +bool 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 000000000..3b70307ec --- /dev/null +++ b/basic/source/sbx/sbxbase.cxx @@ -0,0 +1,340 @@ +/* -*- 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> + +// AppData-Structure for SBX: + + +SbxAppData::SbxAppData() + : eErrCode(ERRCODE_NONE) + , 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; +} + +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; +} + +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; +} + +bool 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 ); + bool bRes = 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; + } + else + return true; +} + +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 000000000..0b5f2111e --- /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 <vcl/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 000000000..189216712 --- /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 <vcl/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 000000000..36bd8d60d --- /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 <vcl/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 000000000..328115d4f --- /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; +} + +bool SbxStdCollection::StoreData( SvStream& rStrm ) const +{ + bool bRes = SbxCollection::StoreData( rStrm ); + if( bRes ) + { + write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, aElemClass, + RTL_TEXTENCODING_ASCII_US); + rStrm.WriteBool( bAddRemoveOk ); + } + return bRes; +} + +/* 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 000000000..a3837d9a3 --- /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 000000000..ad558f228 --- /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 = sNormalisedNumString.makeStringAndClear().toInt64(); + + 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 000000000..057e16f09 --- /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 000000000..206a835b4 --- /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 <vcl/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 000000000..cad5601f2 --- /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 000000000..756fdc9f9 --- /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 000000000..d830061f0 --- /dev/null +++ b/basic/source/sbx/sbxexec.cxx @@ -0,0 +1,381 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <basic/sbx.hxx> +#include <basic/sberrors.hxx> +#include <rtl/character.hxx> +#include <rtl/ustrbuf.hxx> + + +static SbxVariableRef Element + ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, + SbxClassType ); + +static const sal_Unicode* SkipWhitespace( const sal_Unicode* p ) +{ + while( *p && ( *p == ' ' || *p == '\t' ) ) + 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 ) +{ + 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( !rtl::isAsciiAlpha( *p ) && *p != '_' ) + { + SbxBase::SetError( ERRCODE_BASIC_SYNTAX ); + } + else + { + rSym = p; + // The it can contain alphabetic characters, numbers or underlines + while( *p && (rtl::isAsciiAlphanumeric( *p ) || *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 ) +{ + + SbxVariableRef refVar; + const sal_Unicode* p = SkipWhitespace( *ppBuf ); + if( rtl::isAsciiAlpha( *p ) || *p == '_' || *p == '[' ) + { + // Read in the element + refVar = Element( pObj, pGbl, &p, t ); + 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 ); + } + } + 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 ) +{ + 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 ); + } + *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 ) +{ + const sal_Unicode* p = *ppBuf; + SbxVariableRef refVar( Operand( pObj, pGbl, &p, false ) ); + p = SkipWhitespace( p ); + while( refVar.is() && ( *p == '*' || *p == '/' ) ) + { + sal_Unicode cOp = *p++; + SbxVariableRef refVar2( Operand( pObj, pGbl, &p, false ) ); + 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 ) +{ + const sal_Unicode* p = *ppBuf; + SbxVariableRef refVar( MulDiv( pObj, pGbl, &p ) ); + p = SkipWhitespace( p ); + while( refVar.is() && ( *p == '+' || *p == '-' ) ) + { + sal_Unicode cOp = *p++; + SbxVariableRef refVar2( MulDiv( pObj, pGbl, &p ) ); + 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 ) +{ + const sal_Unicode* p = *ppBuf; + SbxVariableRef refVar( Operand( pObj, pGbl, &p, true ) ); + 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 ) ); + 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 ) +{ + OUString aSym; + const sal_Unicode* p = Symbol( *ppBuf, aSym ); + 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 ); + 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 ); + } + *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 ); + 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 ); + p = SkipWhitespace( p ); + if( *p ) + { + SetError( ERRCODE_BASIC_SYNTAX ); + } + return pVar.get(); +} + +/* 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 000000000..bda6da33e --- /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/string_view.hxx> +#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 OUStringLiteral BASICFORMAT_GENERALNUMBER = u"General Number"; +constexpr OUStringLiteral BASICFORMAT_CURRENCY = u"Currency"; +constexpr OUStringLiteral BASICFORMAT_FIXED = u"Fixed"; +constexpr OUStringLiteral BASICFORMAT_STANDARD = u"Standard"; +constexpr OUStringLiteral BASICFORMAT_PERCENT = u"Percent"; +constexpr OUStringLiteral BASICFORMAT_SCIENTIFIC = u"Scientific"; +constexpr OUStringLiteral BASICFORMAT_YESNO = u"Yes/No"; +constexpr OUStringLiteral BASICFORMAT_TRUEFALSE = u"True/False"; +constexpr OUStringLiteral BASICFORMAT_ONOFF = u"On/Off"; + +// 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 =*/ sprintf( sBuffer,"%+22.15lE",dNum ); + sSciNumStrg = OUString::createFromAscii( sBuffer ); +} + + +void SbxBasicFormater::InitExp( double _dNewExp ) +{ + char sBuffer[ MAX_DOUBLE_BUFFER_LENGTH ]; + nNumExp = static_cast<short>(_dNewExp); + /*int nCount =*/ sprintf( sBuffer,"%+i",nNumExp ); + sNumExpStrg = OUString::createFromAscii( sBuffer ); + 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( const OUString& 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.getLength(); + 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.indexOf( ',' ) >= 0; + 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, + const OUString& 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.getLength(); + 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; + } + // coverity[copy_paste_error : FALSE] - this is correct and nDigitPos should not be j + if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit >= nDigitPos ) && 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.getLength()-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 000000000..432aaf9b9 --- /dev/null +++ b/basic/source/sbx/sbxint.cxx @@ -0,0 +1,824 @@ +/* -*- 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 <vcl/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; + + ::OString aOStr = OString::number( n ); + (*p->pOUString) = ::OStringToOUString( aOStr, RTL_TEXTENCODING_ASCII_US ); + 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 + { + ::OString aOStr = OString::number( n ); + (*p->pOUString) = ::OStringToOUString( aOStr, RTL_TEXTENCODING_ASCII_US ); + } + 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 000000000..7011a13b0 --- /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 <vcl/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 000000000..7f3560a62 --- /dev/null +++ b/basic/source/sbx/sbxobj.cxx @@ -0,0 +1,868 @@ +/* -*- 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 "sbxres.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) ) + { + // FindQualified() might have struck already! + if( pParam ) + { + pMeth->SetParameters( pParam ); + } + pMeth->Broadcast( SfxHintId::BasicDataWanted ); + pMeth->SetParameters( nullptr ); + return true; + } + SetError( ERRCODE_BASIC_NO_METHOD ); + 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; +} + +bool SbxObject::StoreData( SvStream& rStrm ) const +{ + if( !SbxVariable::StoreData( rStrm ) ) + { + return false; + } + 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 ); + if( !pMethods->Store( rStrm ) ) + { + return false; + } + if( !pProps->Store( rStrm ) ) + { + return false; + } + if( !pObjs->Store( rStrm ) ) + { + return false; + } + const_cast<SbxObject*>(this)->SetModified( false ); + return true; +} + +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.WriteCharPtr( "<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.WriteCharPtr( "Object( " ) + .WriteOString( OString::number(reinterpret_cast<sal_Int64>(this)) ).WriteCharPtr( "=='" ) + .WriteCharPtr( aNameStr.isEmpty() ? "<unnamed>" : aNameStr.getStr() ).WriteCharPtr( "', " ) + .WriteCharPtr( "of class '" ).WriteOString( aClassNameStr ).WriteCharPtr( "', " ) + .WriteCharPtr( "counts " ) + .WriteOString( OString::number(GetRefCount()) ) + .WriteCharPtr( " refs, " ); + if ( GetParent() ) + { + OString aParentNameStr(OUStringToOString(GetName(), RTL_TEXTENCODING_ASCII_US)); + rStrm.WriteCharPtr( "in parent " ) + .WriteOString( OString::number(reinterpret_cast<sal_Int64>(GetParent())) ) + .WriteCharPtr( "=='" ).WriteCharPtr( aParentNameStr.isEmpty() ? "<unnamed>" : aParentNameStr.getStr() ).WriteCharPtr( "'" ); + } + else + { + rStrm.WriteCharPtr( "no parent " ); + } + rStrm.WriteCharPtr( " )" ) << endl; + OString aIndentNameStr(OUStringToOString(aIndent, RTL_TEXTENCODING_ASCII_US)); + rStrm.WriteOString( aIndentNameStr ).WriteCharPtr( "{" ) << endl; + + // Flags + OUString aAttrs; + if( CollectAttrs( this, aAttrs ) ) + { + OString aAttrStr(OUStringToOString(aAttrs, RTL_TEXTENCODING_ASCII_US)); + rStrm.WriteOString( aIndentNameStr ).WriteCharPtr( "- Flags: " ).WriteOString( aAttrStr ) << endl; + } + + // Methods + rStrm.WriteOString( aIndentNameStr ).WriteCharPtr( "- 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.WriteCharPtr( " contains " ); + static_cast<SbxObject*>(pVar->GetValues_Impl().pObj)->Dump( rStrm, bFill ); + } + else + { + rStrm << endl; + } + } + } + + // Properties + rStrm.WriteOString( aIndentNameStr ).WriteCharPtr( "- 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.WriteCharPtr( " contains " ); + static_cast<SbxObject*>(pVar->GetValues_Impl().pObj)->Dump( rStrm, bFill ); + } + else + { + rStrm << endl; + } + } + } + } + + // Objects + rStrm.WriteOString( aIndentNameStr ).WriteCharPtr( "- 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 ).WriteCharPtr( " - Sub" ); + if (SbxObject *pSbxObj = dynamic_cast<SbxObject*>(pVar)) + { + pSbxObj->Dump(rStrm, bFill); + } + else + { + pVar->Dump(rStrm, bFill); + } + } + } + } + + rStrm.WriteOString( aIndentNameStr ).WriteCharPtr( "}" ) << 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; +} + +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 000000000..125969e3e --- /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 000000000..185dad408 --- /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 000000000..ec2783b1b --- /dev/null +++ b/basic/source/sbx/sbxscan.cxx @@ -0,0 +1,746 @@ +/* -*- 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 <vcl/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("0123456789DEde"); + aSearchStr.append(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 const 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 const 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 OUStringLiteral VBAFORMAT_NN = u"nn"; +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 000000000..ba5a54287 --- /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 <vcl/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 000000000..4e447bb60 --- /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 000000000..1a8013c1c --- /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 <vcl/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 000000000..a14f3b028 --- /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 <vcl/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 000000000..dee531ea2 --- /dev/null +++ b/basic/source/sbx/sbxvalue.cxx @@ -0,0 +1,1568 @@ +/* -*- 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> + + +///////////////////////////// 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; +} + + bool 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; + } + return true; + } + +/* 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 000000000..947b24ff9 --- /dev/null +++ b/basic/source/sbx/sbxvar.cxx @@ -0,0 +1,603 @@ +/* -*- 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> +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; +} + +bool 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 ); + pThis->ResetFlag( SbxFlagBits::NoBroadcast ); + } + else + { + bValStore = SbxValue::StoreData( rStrm ); + } + if( !bValStore ) + { + return false; + } + 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; +} + +// 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.WriteCharPtr( "Variable( " ) + .WriteOString( OString::number(reinterpret_cast<sal_Int64>(this)) ).WriteCharPtr( "==" ) + .WriteOString( aBNameStr ); + OString aBParentNameStr(OUStringToOString(GetParent()->GetName(), RTL_TEXTENCODING_ASCII_US)); + if ( GetParent() ) + { + rStrm.WriteCharPtr( " in parent '" ).WriteOString( aBParentNameStr ).WriteCharPtr( "'" ); + } + else + { + rStrm.WriteCharPtr( " no parent" ); + } + rStrm.WriteCharPtr( " ) " ); + + // output also the object at object-vars + if ( GetValues_Impl().eType == SbxOBJECT && + GetValues_Impl().pObj && + GetValues_Impl().pObj != this && + GetValues_Impl().pObj != GetParent() ) + { + rStrm.WriteCharPtr( " contains " ); + static_cast<SbxObject*>(GetValues_Impl().pObj)->Dump( rStrm, bFill ); + } + else + { + rStrm << endl; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |