Files
libreoffice/basic/source/runtime/methods.cxx
Stephan Bergmann cc20344010 tdf#111313: Honor bWriteNoLenParam in !bCompatibility, too
e8deba22e8 "INTEGRATION: CWS ab26" had added
the bCompatibility case with all the argument checking ("2006/05/04 08:33:46 ab
1.66.10.3: #111951# Changed Mid runtime behaviour only for
CompatibilityMode(true)"), and it was probably an oversight that, for
!bCompatibility, it left the bWriteNoLenParam case (triggered by Basic code like

  s = "abc"
  Mid(s,1) = "d"

) calling OUStringBuffer::remove with an illegal argument of len=-1.

Change that so that only setting ERRCODE_BASIC_BAD_ARGUMENT is controlled by
bCompatibility, while all the other checks (that are probably all necessary to
not call rtl string functions with illegal arguments) are done in both modes.

Also, the check

  nStartPos + 1 > nArgLen

should probably be

  nStartPos > nArgLen

instead, as nStartPos has already been decremented from the one-based Basic
index to the zero-baesd rtl string index.

Change-Id: I75deec0acf75b8677aa89f91897c06c1caa5614d
Reviewed-on: https://gerrit.libreoffice.org/52500
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2018-04-06 14:56:20 +02:00

4674 lines
132 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <config_features.h>
#include <tools/date.hxx>
#include <basic/sbxvar.hxx>
#include <basic/sbuno.hxx>
#include <osl/process.h>
#include <vcl/dibtools.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/sound.hxx>
#include <tools/wintypes.hxx>
#include <vcl/button.hxx>
#include <vcl/weld.hxx>
#include <basic/sbx.hxx>
#include <svl/zforlist.hxx>
#include <rtl/character.hxx>
#include <rtl/math.hxx>
#include <tools/urlobj.hxx>
#include <osl/time.h>
#include <unotools/charclass.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <tools/wldcrd.hxx>
#include <i18nlangtag/lang.h>
#include <rtl/string.hxx>
#include <rtl/strbuf.hxx>
#include <runtime.hxx>
#include <sbunoobj.hxx>
#include <osl/file.hxx>
#include <errobject.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/util/DateTime.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
#include <com/sun/star/script/XErrorQuery.hpp>
#include <ooo/vba/XHelperInterface.hpp>
#include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp>
#include <memory>
#include <random>
#include <o3tl/make_unique.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
using namespace comphelper;
using namespace osl;
using namespace com::sun::star;
using namespace com::sun::star::lang;
using namespace com::sun::star::uno;
#include <date.hxx>
#include <stdobj.hxx>
#include <sbstdobj.hxx>
#include <rtlproto.hxx>
#include <image.hxx>
#include <iosys.hxx>
#include "ddectrl.hxx"
#include <sbintern.hxx>
#include <basic/vbahelper.hxx>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sbobjmod.hxx>
#include <sbxmod.hxx>
#ifdef _WIN32
#include <prewin.h>
#include <direct.h>
#include <io.h>
#include <postwin.h>
#else
#include <unistd.h>
#endif
#if HAVE_FEATURE_SCRIPTING
static void FilterWhiteSpace( OUString& rStr )
{
if (rStr.isEmpty())
{
return;
}
OUStringBuffer aRet;
for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
{
sal_Unicode cChar = rStr[i];
if ((cChar != ' ') && (cChar != '\t') &&
(cChar != '\n') && (cChar != '\r'))
{
aRet.append(cChar);
}
}
rStr = aRet.makeStringAndClear();
}
static long GetDayDiff( const Date& rDate );
static const CharClass& GetCharClass()
{
static bool bNeedsInit = true;
static LanguageTag aLanguageTag( LANGUAGE_SYSTEM);
if( bNeedsInit )
{
bNeedsInit = false;
aLanguageTag = Application::GetSettings().GetLanguageTag();
}
static CharClass aCharClass( aLanguageTag );
return aCharClass;
}
static inline bool isFolder( FileStatus::Type aType )
{
return ( aType == FileStatus::Directory || aType == FileStatus::Volume );
}
//*** UCB file access ***
// Converts possibly relative paths to absolute paths
// according to the setting done by ChDir/ChDrive
OUString getFullPath( const OUString& aRelPath )
{
OUString aFileURL;
// #80204 Try first if it already is a valid URL
INetURLObject aURLObj( aRelPath );
aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
if( aFileURL.isEmpty() )
{
File::getFileURLFromSystemPath( aRelPath, aFileURL );
}
return aFileURL;
}
// TODO: -> SbiGlobals
static uno::Reference< ucb::XSimpleFileAccess3 > const & getFileAccess()
{
static uno::Reference< ucb::XSimpleFileAccess3 > xSFI;
if( !xSFI.is() )
{
xSFI = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
}
return xSFI;
}
// Properties and methods lie down the return value at the Get (bPut = sal_False) in the
// element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True)
// CreateObject( class )
void SbRtl_CreateObject(StarBASIC * pBasic, SbxArray & rPar, bool)
{
OUString aClass( rPar.Get( 1 )->GetOUString() );
SbxObjectRef p = SbxBase::CreateObject( aClass );
if( !p.is() )
StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD );
else
{
// Convenience: enter BASIC as parent
p->SetParent( pBasic );
rPar.Get( 0 )->PutObject( p.get() );
}
}
// Error( n )
void SbRtl_Error(StarBASIC * pBasic, SbxArray & rPar, bool)
{
if( !pBasic )
StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
else
{
OUString aErrorMsg;
ErrCode nErr = ERRCODE_NONE;
sal_Int32 nCode = 0;
if( rPar.Count() == 1 )
{
nErr = StarBASIC::GetErrBasic();
aErrorMsg = StarBASIC::GetErrorMsg();
}
else
{
nCode = rPar.Get( 1 )->GetLong();
if( nCode > 65535 )
{
StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
}
else
{
nErr = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nCode) );
}
}
bool bVBA = SbiRuntime::isVBAEnabled();
OUString tmpErrMsg;
if( bVBA && !aErrorMsg.isEmpty())
{
tmpErrMsg = aErrorMsg;
}
else
{
StarBASIC::MakeErrorText( nErr, aErrorMsg );
tmpErrMsg = StarBASIC::GetErrorText();
}
// If this rtlfunc 'Error' passed a errcode the same as the active Err Objects's
// current err then return the description for the error message if it is set
// ( complicated isn't it ? )
if ( bVBA && rPar.Count() > 1 )
{
uno::Reference< ooo::vba::XErrObject > xErrObj( SbxErrObject::getUnoErrObject() );
if ( xErrObj.is() && xErrObj->getNumber() == nCode && !xErrObj->getDescription().isEmpty() )
{
tmpErrMsg = xErrObj->getDescription();
}
}
rPar.Get( 0 )->PutString( tmpErrMsg );
}
}
// Sinus
void SbRtl_Sin(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
else
{
SbxVariableRef pArg = rPar.Get( 1 );
rPar.Get( 0 )->PutDouble( sin( pArg->GetDouble() ) );
}
}
void SbRtl_Cos(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
else
{
SbxVariableRef pArg = rPar.Get( 1 );
rPar.Get( 0 )->PutDouble( cos( pArg->GetDouble() ) );
}
}
void SbRtl_Atn(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
else
{
SbxVariableRef pArg = rPar.Get( 1 );
rPar.Get( 0 )->PutDouble( atan( pArg->GetDouble() ) );
}
}
void SbRtl_Abs(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariableRef pArg = rPar.Get( 1 );
rPar.Get( 0 )->PutDouble( fabs( pArg->GetDouble() ) );
}
}
void SbRtl_Asc(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariableRef pArg = rPar.Get( 1 );
OUString aStr( pArg->GetOUString() );
if ( aStr.isEmpty())
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
rPar.Get(0)->PutEmpty();
}
else
{
sal_Unicode aCh = aStr[0];
rPar.Get(0)->PutLong( aCh );
}
}
}
void implChr( SbxArray& rPar, bool bChrW )
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariableRef pArg = rPar.Get( 1 );
OUString aStr;
if( !bChrW && SbiRuntime::isVBAEnabled() )
{
sal_Char c = static_cast<sal_Char>(pArg->GetByte());
aStr = OUString(&c, 1, osl_getThreadTextEncoding());
}
else
{
sal_Unicode aCh = static_cast<sal_Unicode>(pArg->GetUShort());
aStr = OUString(aCh);
}
rPar.Get(0)->PutString( aStr );
}
}
void SbRtl_Chr(StarBASIC *, SbxArray & rPar, bool)
{
implChr( rPar, false/*bChrW*/ );
}
void SbRtl_ChrW(StarBASIC *, SbxArray & rPar, bool)
{
implChr( rPar, true/*bChrW*/ );
}
void SbRtl_CurDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
{
(void)pBasic;
(void)bWrite;
// #57064 Although this function doesn't work with DirEntry, it isn't touched
// by the adjustment to virtual URLs, as, using the DirEntry-functionality,
// there's no possibility to detect the current one in a way that a virtual URL
// could be delivered.
#if defined(_WIN32)
int nCurDir = 0; // Current dir // JSM
if ( rPar.Count() == 2 )
{
OUString aDrive = rPar.Get(1)->GetOUString();
if ( aDrive.getLength() != 1 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
auto c = rtl::toAsciiUpperCase(aDrive[0]);
if ( !rtl::isAsciiUpperCase( c ) )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
nCurDir = c - 'A' + 1;
}
wchar_t pBuffer[ _MAX_PATH ];
if ( _wgetdcwd( nCurDir, pBuffer, _MAX_PATH ) != nullptr )
{
rPar.Get(0)->PutString( o3tl::toU(pBuffer) );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE );
}
#else
const int PATH_INCR = 250;
int nSize = PATH_INCR;
std::unique_ptr<char[]> pMem;
while( true )
{
pMem.reset(new char[nSize]);
if( !pMem )
{
StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY );
return;
}
if( getcwd( pMem.get(), nSize-1 ) != nullptr )
{
rPar.Get(0)->PutString( OUString::createFromAscii(pMem.get()) );
return;
}
if( errno != ERANGE )
{
StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
return;
}
nSize += PATH_INCR;
};
#endif
}
void SbRtl_ChDir(StarBASIC * pBasic, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if (rPar.Count() == 2)
{
// VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.)
if( SbiRuntime::isVBAEnabled() )
{
::basic::vba::registerCurrentDirectory( getDocumentModel( pBasic ), rPar.Get(1)->GetOUString() );
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
void SbRtl_ChDrive(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if (rPar.Count() != 2)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
// Implementation of StepRENAME with UCB
void implStepRenameUCB( const OUString& aSource, const OUString& aDest )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
OUString aSourceFullPath = getFullPath( aSource );
if( !xSFI->exists( aSourceFullPath ) )
{
StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
return;
}
OUString aDestFullPath = getFullPath( aDest );
if( xSFI->exists( aDestFullPath ) )
{
StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS );
}
else
{
xSFI->move( aSourceFullPath, aDestFullPath );
}
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
}
}
}
// Implementation of StepRENAME with OSL
void implStepRenameOSL( const OUString& aSource, const OUString& aDest )
{
FileBase::RC nRet = File::move( getFullPath( aSource ), getFullPath( aDest ) );
if( nRet != FileBase::E_None )
{
StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
}
}
void SbRtl_FileCopy(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if (rPar.Count() == 3)
{
OUString aSource = rPar.Get(1)->GetOUString();
OUString aDest = rPar.Get(2)->GetOUString();
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
xSFI->copy( getFullPath( aSource ), getFullPath( aDest ) );
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
}
}
}
else
{
FileBase::RC nRet = File::copy( getFullPath( aSource ), getFullPath( aDest ) );
if( nRet != FileBase::E_None )
{
StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
}
}
}
else
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
void SbRtl_Kill(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if (rPar.Count() == 2)
{
OUString aFileSpec = rPar.Get(1)->GetOUString();
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
OUString aFullPath = getFullPath( aFileSpec );
if( !xSFI->exists( aFullPath ) || xSFI->isFolder( aFullPath ) )
{
StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
return;
}
try
{
xSFI->kill( aFullPath );
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_IO_GENERAL );
}
}
}
else
{
File::remove( getFullPath( aFileSpec ) );
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
void SbRtl_MkDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
{
rPar.Get(0)->PutEmpty();
if (rPar.Count() == 2)
{
OUString aPath = rPar.Get(1)->GetOUString();
if ( SbiRuntime::isVBAEnabled() )
{
// In vba if the full path is not specified then
// folder is created relative to the curdir
INetURLObject aURLObj( getFullPath( aPath ) );
if ( aURLObj.GetProtocol() != INetProtocol::File )
{
SbxArrayRef pPar = new SbxArray();
SbxVariableRef pResult = new SbxVariable();
SbxVariableRef pParam = new SbxVariable();
pPar->Insert( pResult.get(), pPar->Count() );
pPar->Insert( pParam.get(), pPar->Count() );
SbRtl_CurDir( pBasic, *pPar, bWrite );
rtl::OUString sCurPathURL;
File::getFileURLFromSystemPath( pPar->Get(0)->GetOUString(), sCurPathURL );
aURLObj.SetURL( sCurPathURL );
aURLObj.Append( aPath );
File::getSystemPathFromFileURL(aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ),aPath ) ;
}
}
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
xSFI->createFolder( getFullPath( aPath ) );
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_IO_GENERAL );
}
}
}
else
{
Directory::create( getFullPath( aPath ) );
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
// In OSL only empty directories can be deleted
// so we have to delete all files recursively
void implRemoveDirRecursive( const OUString& aDirPath )
{
DirectoryItem aItem;
FileBase::RC nRet = DirectoryItem::get( aDirPath, aItem );
bool bExists = (nRet == FileBase::E_None);
FileStatus aFileStatus( osl_FileStatus_Mask_Type );
nRet = aItem.getFileStatus( aFileStatus );
bool bFolder = nRet == FileBase::E_None
&& isFolder( aFileStatus.getFileType() );
if( !bExists || !bFolder )
{
StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
return;
}
Directory aDir( aDirPath );
nRet = aDir.open();
if( nRet != FileBase::E_None )
{
StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
return;
}
for( ;; )
{
DirectoryItem aItem2;
nRet = aDir.getNextItem( aItem2 );
if( nRet != FileBase::E_None )
{
break;
}
// Handle flags
FileStatus aFileStatus2( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL );
nRet = aItem2.getFileStatus( aFileStatus2 );
if( nRet != FileBase::E_None )
{
SAL_WARN("basic", "getFileStatus failed");
continue;
}
OUString aPath = aFileStatus2.getFileURL();
// Directory?
FileStatus::Type aType2 = aFileStatus2.getFileType();
bool bFolder2 = isFolder( aType2 );
if( bFolder2 )
{
implRemoveDirRecursive( aPath );
}
else
{
File::remove( aPath );
}
}
aDir.close();
Directory::remove( aDirPath );
}
void SbRtl_RmDir(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if (rPar.Count() == 2)
{
OUString aPath = rPar.Get(1)->GetOUString();
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
if( !xSFI->isFolder( aPath ) )
{
StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
return;
}
SbiInstance* pInst = GetSbData()->pInst;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
if( bCompatibility )
{
Sequence< OUString > aContent = xSFI->getFolderContents( aPath, true );
sal_Int32 nCount = aContent.getLength();
if( nCount > 0 )
{
StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR );
return;
}
}
xSFI->kill( getFullPath( aPath ) );
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_IO_GENERAL );
}
}
}
else
{
implRemoveDirRecursive( getFullPath( aPath ) );
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
void SbRtl_SendKeys(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
}
void SbRtl_Exp(StarBASIC *, SbxArray & rPar, bool)
{
if( rPar.Count() < 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
else
{
double aDouble = rPar.Get( 1 )->GetDouble();
aDouble = exp( aDouble );
checkArithmeticOverflow( aDouble );
rPar.Get( 0 )->PutDouble( aDouble );
}
}
void SbRtl_FileLen(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariableRef pArg = rPar.Get( 1 );
OUString aStr( pArg->GetOUString() );
sal_Int32 nLen = 0;
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
nLen = xSFI->getSize( getFullPath( aStr ) );
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_IO_GENERAL );
}
}
}
else
{
DirectoryItem aItem;
DirectoryItem::get( getFullPath( aStr ), aItem );
FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
aItem.getFileStatus( aFileStatus );
nLen = static_cast<sal_Int32>(aFileStatus.getFileSize());
}
rPar.Get(0)->PutLong( static_cast<long>(nLen) );
}
}
void SbRtl_Hex(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariableRef pArg = rPar.Get( 1 );
// converting value to unsigned and limit to 2 or 4 byte representation
sal_uInt32 nVal = pArg->IsInteger() ?
static_cast<sal_uInt16>(pArg->GetInteger()) :
static_cast<sal_uInt32>(pArg->GetLong());
OUString aStr(OUString::number( nVal, 16 ));
aStr = aStr.toAsciiUpperCase();
rPar.Get(0)->PutString( aStr );
}
}
void SbRtl_FuncCaller(StarBASIC *, SbxArray & rPar, bool)
{
if ( SbiRuntime::isVBAEnabled() && GetSbData()->pInst && GetSbData()->pInst->pRun )
{
if ( GetSbData()->pInst->pRun->GetExternalCaller() )
*rPar.Get(0) = *GetSbData()->pInst->pRun->GetExternalCaller();
else
{
SbxVariableRef pVar = new SbxVariable(SbxVARIANT);
*rPar.Get(0) = *pVar;
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
}
}
// InStr( [start],string,string,[compare] )
void SbRtl_InStr(StarBASIC *, SbxArray & rPar, bool)
{
std::size_t nArgCount = rPar.Count()-1;
if ( nArgCount < 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
else
{
sal_Int32 nStartPos = 1;
sal_Int32 nFirstStringPos = 1;
if ( nArgCount >= 3 )
{
nStartPos = rPar.Get(1)->GetLong();
if( nStartPos <= 0 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
nStartPos = 1;
}
nFirstStringPos++;
}
SbiInstance* pInst = GetSbData()->pInst;
bool bTextMode;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
if( bCompatibility )
{
SbiRuntime* pRT = pInst->pRun;
bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
}
else
{
bTextMode = true;
}
if ( nArgCount == 4 )
{
bTextMode = rPar.Get(4)->GetInteger();
}
sal_Int32 nPos;
const OUString& rToken = rPar.Get(nFirstStringPos+1)->GetOUString();
// #97545 Always find empty string
if( rToken.isEmpty() )
{
nPos = nStartPos;
}
else
{
if( !bTextMode )
{
const OUString& rStr1 = rPar.Get(nFirstStringPos)->GetOUString();
nPos = rStr1.indexOf( rToken, nStartPos - 1 ) + 1;
}
else
{
OUString aStr1 = rPar.Get(nFirstStringPos)->GetOUString();
OUString aToken = rToken;
aStr1 = aStr1.toAsciiUpperCase();
aToken = aToken.toAsciiUpperCase();
nPos = aStr1.indexOf( aToken, nStartPos-1 ) + 1;
}
}
rPar.Get(0)->PutLong( nPos );
}
}
// InstrRev(string1, string2[, start[, compare]])
void SbRtl_InStrRev(StarBASIC *, SbxArray & rPar, bool)
{
std::size_t nArgCount = rPar.Count()-1;
if ( nArgCount < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aStr1 = rPar.Get(1)->GetOUString();
OUString aToken = rPar.Get(2)->GetOUString();
sal_Int32 nStartPos = -1;
if ( nArgCount >= 3 )
{
nStartPos = rPar.Get(3)->GetLong();
if( nStartPos <= 0 && nStartPos != -1 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
nStartPos = -1;
}
}
SbiInstance* pInst = GetSbData()->pInst;
bool bTextMode;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
if( bCompatibility )
{
SbiRuntime* pRT = pInst->pRun;
bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
}
else
{
bTextMode = true;
}
if ( nArgCount == 4 )
{
bTextMode = rPar.Get(4)->GetInteger();
}
sal_Int32 nStrLen = aStr1.getLength();
if( nStartPos == -1 )
{
nStartPos = nStrLen;
}
sal_Int32 nPos = 0;
if( nStartPos <= nStrLen )
{
sal_Int32 nTokenLen = aToken.getLength();
if( !nTokenLen )
{
// Always find empty string
nPos = nStartPos;
}
else if( nStrLen > 0 )
{
if( !bTextMode )
{
nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
}
else
{
aStr1 = aStr1.toAsciiUpperCase();
aToken = aToken.toAsciiUpperCase();
nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
}
}
}
rPar.Get(0)->PutLong( nPos );
}
}
/*
Int( 2.8 ) = 2.0
Int( -2.8 ) = -3.0
Fix( 2.8 ) = 2.0
Fix( -2.8 ) = -2.0 <- !!
*/
void SbRtl_Int(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
else
{
SbxVariableRef pArg = rPar.Get( 1 );
double aDouble= pArg->GetDouble();
/*
floor( 2.8 ) = 2.0
floor( -2.8 ) = -3.0
*/
aDouble = floor( aDouble );
rPar.Get(0)->PutDouble( aDouble );
}
}
void SbRtl_Fix(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
else
{
SbxVariableRef pArg = rPar.Get( 1 );
double aDouble = pArg->GetDouble();
if ( aDouble >= 0.0 )
aDouble = floor( aDouble );
else
aDouble = ceil( aDouble );
rPar.Get(0)->PutDouble( aDouble );
}
}
void SbRtl_LCase(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
const CharClass& rCharClass = GetCharClass();
OUString aStr( rPar.Get(1)->GetOUString() );
aStr = rCharClass.lowercase(aStr);
rPar.Get(0)->PutString( aStr );
}
}
void SbRtl_Left(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aStr( rPar.Get(1)->GetOUString() );
sal_Int32 nResultLen = rPar.Get(2)->GetLong();
if( nResultLen < 0 )
{
nResultLen = 0;
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else if(nResultLen > aStr.getLength())
{
nResultLen = aStr.getLength();
}
aStr = aStr.copy(0, nResultLen );
rPar.Get(0)->PutString( aStr );
}
}
void SbRtl_Log(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
double aArg = rPar.Get(1)->GetDouble();
if ( aArg > 0 )
{
double d = log( aArg );
checkArithmeticOverflow( d );
rPar.Get( 0 )->PutDouble( d );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
}
void SbRtl_LTrim(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aStr(comphelper::string::stripStart(rPar.Get(1)->GetOUString(), ' '));
rPar.Get(0)->PutString(aStr);
}
}
// Mid( String, nStart, nLength )
void SbRtl_Mid(StarBASIC *, SbxArray & rPar, bool bWrite)
{
int nArgCount = rPar.Count()-1;
if ( nArgCount < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
// #23178: replicate the functionality of Mid$ as a command
// by adding a replacement-string as a fourth parameter.
// In contrast to the original the third parameter (nLength)
// can't be left out here. That's considered in bWrite already.
if( nArgCount == 4 )
{
bWrite = true;
}
OUString aArgStr = rPar.Get(1)->GetOUString();
sal_Int32 nStartPos = rPar.Get(2)->GetLong();
if ( nStartPos < 1 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
nStartPos--;
sal_Int32 nLen = -1;
bool bWriteNoLenParam = false;
if ( nArgCount == 3 || bWrite )
{
sal_Int32 n = rPar.Get(3)->GetLong();
if( bWrite && n == -1 )
{
bWriteNoLenParam = true;
}
nLen = n;
}
if ( bWrite )
{
sal_Int32 nArgLen = aArgStr.getLength();
if( nStartPos > nArgLen )
{
SbiInstance* pInst = GetSbData()->pInst;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
if( bCompatibility )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
nStartPos = nArgLen;
}
OUString aReplaceStr = rPar.Get(4)->GetOUString();
sal_Int32 nReplaceStrLen = aReplaceStr.getLength();
sal_Int32 nReplaceLen;
if( bWriteNoLenParam )
{
nReplaceLen = nReplaceStrLen;
}
else
{
nReplaceLen = nLen;
if( nReplaceLen < 0 || nReplaceLen > nReplaceStrLen )
{
nReplaceLen = nReplaceStrLen;
}
}
sal_Int32 nReplaceEndPos = nStartPos + nReplaceLen;
if( nReplaceEndPos > nArgLen )
{
nReplaceLen -= (nReplaceEndPos - nArgLen);
}
OUStringBuffer aResultStr = aArgStr;
sal_Int32 nErase = nReplaceLen;
aResultStr.remove( nStartPos, nErase );
aResultStr.insert( nStartPos, aReplaceStr.getStr(), nReplaceLen);
rPar.Get(1)->PutString( aResultStr.makeStringAndClear() );
}
else
{
OUString aResultStr;
if (nStartPos > aArgStr.getLength())
{
// do nothing
}
else if(nArgCount == 2)
{
aResultStr = aArgStr.copy( nStartPos);
}
else
{
if (nLen < 0)
nLen = 0;
if(nStartPos + nLen > aArgStr.getLength())
{
nLen = aArgStr.getLength() - nStartPos;
}
if (nLen > 0)
aResultStr = aArgStr.copy( nStartPos, nLen );
}
rPar.Get(0)->PutString( aResultStr );
}
}
}
}
void SbRtl_Oct(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
char aBuffer[16];
SbxVariableRef pArg = rPar.Get( 1 );
if ( pArg->IsInteger() )
{
snprintf( aBuffer, sizeof(aBuffer), "%o", pArg->GetInteger() );
}
else
{
snprintf( aBuffer, sizeof(aBuffer), "%lo", static_cast<long unsigned int>(pArg->GetLong()) );
}
rPar.Get(0)->PutString( OUString::createFromAscii( aBuffer ) );
}
}
// Replace(expression, find, replace[, start[, count[, compare]]])
void SbRtl_Replace(StarBASIC *, SbxArray & rPar, bool)
{
std::size_t nArgCount = rPar.Count()-1;
if ( nArgCount < 3 || nArgCount > 6 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aExpStr = rPar.Get(1)->GetOUString();
OUString aFindStr = rPar.Get(2)->GetOUString();
OUString aReplaceStr = rPar.Get(3)->GetOUString();
sal_Int32 lStartPos = 1;
if ( nArgCount >= 4 )
{
if( rPar.Get(4)->GetType() != SbxEMPTY )
{
lStartPos = rPar.Get(4)->GetLong();
}
if( lStartPos < 1)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
lStartPos = 1;
}
}
sal_Int32 lCount = -1;
if( nArgCount >=5 )
{
if( rPar.Get(5)->GetType() != SbxEMPTY )
{
lCount = rPar.Get(5)->GetLong();
}
if( lCount < -1)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
lCount = -1;
}
}
SbiInstance* pInst = GetSbData()->pInst;
bool bTextMode;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
if( bCompatibility )
{
SbiRuntime* pRT = pInst->pRun;
bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
}
else
{
bTextMode = true;
}
if ( nArgCount == 6 )
{
bTextMode = rPar.Get(6)->GetInteger();
}
sal_Int32 nExpStrLen = aExpStr.getLength();
sal_Int32 nFindStrLen = aFindStr.getLength();
sal_Int32 nReplaceStrLen = aReplaceStr.getLength();
if( lStartPos <= nExpStrLen )
{
sal_Int32 nPos = lStartPos - 1;
sal_Int32 nCounts = 0;
while( lCount == -1 || lCount > nCounts )
{
OUString aSrcStr( aExpStr );
if( bTextMode )
{
aSrcStr = aSrcStr.toAsciiUpperCase();
aFindStr = aFindStr.toAsciiUpperCase();
}
nPos = aSrcStr.indexOf( aFindStr, nPos );
if( nPos >= 0 )
{
aExpStr = aExpStr.replaceAt( nPos, nFindStrLen, aReplaceStr );
nPos = nPos + nReplaceStrLen;
nCounts++;
}
else
{
break;
}
}
}
rPar.Get(0)->PutString( aExpStr.copy( lStartPos - 1 ) );
}
}
void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
const OUString& rStr = rPar.Get(1)->GetOUString();
int nResultLen = rPar.Get(2)->GetLong();
if( nResultLen < 0 )
{
nResultLen = 0;
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
int nStrLen = rStr.getLength();
if ( nResultLen > nStrLen )
{
nResultLen = nStrLen;
}
OUString aResultStr = rStr.copy( nStrLen - nResultLen );
rPar.Get(0)->PutString( aResultStr );
}
}
void SbRtl_RTL(StarBASIC * pBasic, SbxArray & rPar, bool)
{
rPar.Get( 0 )->PutObject( pBasic->getRTL().get() );
}
void SbRtl_RTrim(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aStr(comphelper::string::stripEnd(rPar.Get(1)->GetOUString(), ' '));
rPar.Get(0)->PutString(aStr);
}
}
void SbRtl_Sgn(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
double aDouble = rPar.Get(1)->GetDouble();
sal_Int16 nResult = 0;
if ( aDouble > 0 )
{
nResult = 1;
}
else if ( aDouble < 0 )
{
nResult = -1;
}
rPar.Get(0)->PutInteger( nResult );
}
}
void SbRtl_Space(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUStringBuffer aBuf;
string::padToLength(aBuf, rPar.Get(1)->GetLong(), ' ');
rPar.Get(0)->PutString(aBuf.makeStringAndClear());
}
}
void SbRtl_Spc(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUStringBuffer aBuf;
string::padToLength(aBuf, rPar.Get(1)->GetLong(), ' ');
rPar.Get(0)->PutString(aBuf.makeStringAndClear());
}
}
void SbRtl_Sqr(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
double aDouble = rPar.Get(1)->GetDouble();
if ( aDouble >= 0 )
{
rPar.Get(0)->PutDouble( sqrt( aDouble ));
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
}
void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aStr;
OUString aStrNew("");
SbxVariableRef pArg = rPar.Get( 1 );
pArg->Format( aStr );
// Numbers start with a space
if( pArg->IsNumericRTL() )
{
// replace commas by points so that it's symmetric to Val!
aStr = aStr.replaceFirst( ",", "." );
SbiInstance* pInst = GetSbData()->pInst;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
if( bCompatibility )
{
sal_Int32 nLen = aStr.getLength();
const sal_Unicode* pBuf = aStr.getStr();
bool bNeg = ( pBuf[0] == '-' );
sal_Int32 iZeroSearch = 0;
if( bNeg )
{
aStrNew += "-";
iZeroSearch++;
}
else
{
if( pBuf[0] != ' ' )
{
aStrNew += " ";
}
}
sal_Int32 iNext = iZeroSearch + 1;
if( pBuf[iZeroSearch] == '0' && nLen > iNext && pBuf[iNext] == '.' )
{
iZeroSearch += 1;
}
aStrNew += aStr.copy(iZeroSearch);
}
else
{
aStrNew = " " + aStr;
}
}
else
{
aStrNew = aStr;
}
rPar.Get(0)->PutString( aStrNew );
}
}
void SbRtl_StrComp(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
rPar.Get(0)->PutEmpty();
return;
}
const OUString& rStr1 = rPar.Get(1)->GetOUString();
const OUString& rStr2 = rPar.Get(2)->GetOUString();
SbiInstance* pInst = GetSbData()->pInst;
bool bTextCompare;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
if( bCompatibility )
{
SbiRuntime* pRT = pInst->pRun;
bTextCompare = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
}
else
{
bTextCompare = true;
}
if ( rPar.Count() == 4 )
bTextCompare = rPar.Get(3)->GetInteger();
if( !bCompatibility )
{
bTextCompare = !bTextCompare;
}
sal_Int32 nRetValue = 0;
if( bTextCompare )
{
::utl::TransliterationWrapper* pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
if( !pTransliterationWrapper )
{
uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
GetSbData()->pTransliterationWrapper.reset(
new ::utl::TransliterationWrapper( xContext,
TransliterationFlags::IGNORE_CASE |
TransliterationFlags::IGNORE_KANA |
TransliterationFlags::IGNORE_WIDTH ) );
pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
}
LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
pTransliterationWrapper->loadModuleIfNeeded( eLangType );
nRetValue = pTransliterationWrapper->compareString( rStr1, rStr2 );
}
else
{
sal_Int32 aResult;
aResult = rStr1.compareTo( rStr2 );
if ( aResult < 0 )
{
nRetValue = -1;
}
else if ( aResult > 0)
{
nRetValue = 1;
}
}
rPar.Get(0)->PutInteger( sal::static_int_cast< sal_Int16 >( nRetValue ) );
}
void SbRtl_String(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
sal_Unicode aFiller;
sal_Int32 lCount = rPar.Get(1)->GetLong();
if( lCount < 0 || lCount > 0xffff )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
if( rPar.Get(2)->GetType() == SbxINTEGER )
{
aFiller = static_cast<sal_Unicode>(rPar.Get(2)->GetInteger());
}
else
{
const OUString& rStr = rPar.Get(2)->GetOUString();
aFiller = rStr[0];
}
OUStringBuffer aBuf(lCount);
string::padToLength(aBuf, lCount, aFiller);
rPar.Get(0)->PutString(aBuf.makeStringAndClear());
}
}
void SbRtl_Tab(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
else
{
OUStringBuffer aStr;
comphelper::string::padToLength(aStr, rPar.Get(1)->GetLong(), '\t');
rPar.Get(0)->PutString(aStr.makeStringAndClear());
}
}
void SbRtl_Tan(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariableRef pArg = rPar.Get( 1 );
rPar.Get( 0 )->PutDouble( tan( pArg->GetDouble() ) );
}
}
void SbRtl_UCase(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
const CharClass& rCharClass = GetCharClass();
OUString aStr( rPar.Get(1)->GetOUString() );
aStr = rCharClass.uppercase( aStr );
rPar.Get(0)->PutString( aStr );
}
}
void SbRtl_Val(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
{
(void)pBasic;
(void)bWrite;
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
double nResult = 0.0;
char* pEndPtr;
OUString aStr( rPar.Get(1)->GetOUString() );
FilterWhiteSpace( aStr );
if ( aStr.getLength() > 1 && aStr[0] == '&' )
{
int nRadix = 10;
char aChar = static_cast<char>(aStr[1]);
if ( aChar == 'h' || aChar == 'H' )
{
nRadix = 16;
}
else if ( aChar == 'o' || aChar == 'O' )
{
nRadix = 8;
}
if ( nRadix != 10 )
{
OString aByteStr(OUStringToOString(aStr, osl_getThreadTextEncoding()));
sal_Int16 nlResult = static_cast<sal_Int16>(strtol( aByteStr.getStr()+2, &pEndPtr, nRadix));
nResult = static_cast<double>(nlResult);
}
}
else
{
rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
sal_Int32 nParseEnd = 0;
nResult = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd );
if ( eStatus != rtl_math_ConversionStatus_Ok )
StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW );
/* TODO: we should check whether all characters were parsed here,
* but earlier code silently ignored trailing nonsense such as "1x"
* resulting in 1 with the side effect that any alpha-only-string
* like "x" resulted in 0. Not changing that now (2013-03-22) as
* user macros may rely on it. */
#if 0
else if ( nParseEnd != aStr.getLength() )
StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
#endif
}
rPar.Get(0)->PutDouble( nResult );
}
}
// Helper functions for date conversion
sal_Int16 implGetDateDay( double aDate )
{
aDate -= 2.0; // standardize: 1.1.1900 => 0.0
aDate = floor( aDate );
Date aRefDate( 1, 1, 1900 );
aRefDate.AddDays( aDate );
sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetDay() );
return nRet;
}
sal_Int16 implGetDateMonth( double aDate )
{
Date aRefDate( 1,1,1900 );
sal_Int32 nDays = static_cast<sal_Int32>(aDate);
nDays -= 2; // standardize: 1.1.1900 => 0.0
aRefDate.AddDays( nDays );
sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetMonth() );
return nRet;
}
css::util::Date SbxDateToUNODate( const SbxValue* const pVal )
{
double aDate = pVal->GetDate();
css::util::Date aUnoDate;
aUnoDate.Day = implGetDateDay ( aDate );
aUnoDate.Month = implGetDateMonth( aDate );
aUnoDate.Year = implGetDateYear ( aDate );
return aUnoDate;
}
void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate)
{
double dDate;
if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, false, dDate ) )
{
pVal->PutDate( dDate );
}
}
// Function to convert date to UNO date (com.sun.star.util.Date)
void SbRtl_CDateToUnoDate(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODate(rPar.Get(1))));
}
// Function to convert date from UNO date (com.sun.star.util.Date)
void SbRtl_CDateFromUnoDate(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Date>::get()));
css::util::Date aUnoDate;
if(aAny >>= aUnoDate)
SbxDateFromUNODate(rPar.Get(0), aUnoDate);
else
SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
}
css::util::Time SbxDateToUNOTime( const SbxValue* const pVal )
{
double aDate = pVal->GetDate();
css::util::Time aUnoTime;
aUnoTime.Hours = implGetHour ( aDate );
aUnoTime.Minutes = implGetMinute ( aDate );
aUnoTime.Seconds = implGetSecond ( aDate );
aUnoTime.NanoSeconds = 0;
return aUnoTime;
}
void SbxDateFromUNOTime( SbxValue *pVal, const css::util::Time& aUnoTime)
{
pVal->PutDate( implTimeSerial(aUnoTime.Hours, aUnoTime.Minutes, aUnoTime.Seconds) );
}
// Function to convert date to UNO time (com.sun.star.util.Time)
void SbRtl_CDateToUnoTime(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
unoToSbxValue(rPar.Get(0), Any(SbxDateToUNOTime(rPar.Get(1))));
}
// Function to convert date from UNO time (com.sun.star.util.Time)
void SbRtl_CDateFromUnoTime(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Time>::get()));
css::util::Time aUnoTime;
if(aAny >>= aUnoTime)
SbxDateFromUNOTime(rPar.Get(0), aUnoTime);
else
SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
}
css::util::DateTime SbxDateToUNODateTime( const SbxValue* const pVal )
{
double aDate = pVal->GetDate();
css::util::DateTime aUnoDT;
aUnoDT.Day = implGetDateDay ( aDate );
aUnoDT.Month = implGetDateMonth( aDate );
aUnoDT.Year = implGetDateYear ( aDate );
aUnoDT.Hours = implGetHour ( aDate );
aUnoDT.Minutes = implGetMinute ( aDate );
aUnoDT.Seconds = implGetSecond ( aDate );
aUnoDT.NanoSeconds = 0;
return aUnoDT;
}
void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT)
{
double dDate(0.0);
if( implDateTimeSerial( aUnoDT.Year, aUnoDT.Month, aUnoDT.Day,
aUnoDT.Hours, aUnoDT.Minutes, aUnoDT.Seconds,
dDate ) )
{
pVal->PutDate( dDate );
}
}
// Function to convert date to UNO date (com.sun.star.util.Date)
void SbRtl_CDateToUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODateTime(rPar.Get(1))));
}
// Function to convert date from UNO date (com.sun.star.util.Date)
void SbRtl_CDateFromUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::DateTime>::get()));
css::util::DateTime aUnoDT;
if(aAny >>= aUnoDT)
SbxDateFromUNODateTime(rPar.Get(0), aUnoDT);
else
SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
}
// Function to convert date to ISO 8601 date format YYYYMMDD
void SbRtl_CDateToIso(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() == 2 )
{
double aDate = rPar.Get(1)->GetDate();
// Date may actually even be -YYYYYMMDD
char Buffer[11];
sal_Int16 nYear = implGetDateYear( aDate );
snprintf( Buffer, sizeof( Buffer ), (nYear < 0 ? "%05d%02d%02d" : "%04d%02d%02d"),
static_cast<int>(nYear),
static_cast<int>(implGetDateMonth( aDate )),
static_cast<int>(implGetDateDay( aDate )) );
OUString aRetStr = OUString::createFromAscii( Buffer );
rPar.Get(0)->PutString( aRetStr );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
// Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD
// And even YYMMDD for compatibility, sigh..
void SbRtl_CDateFromIso(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() == 2 )
{
do
{
OUString aStr = rPar.Get(1)->GetOUString();
if (aStr.isEmpty())
break;
// Valid formats are
// YYYYMMDD -YYYMMDD YYYYYMMDD -YYYYYMMDD YYMMDD
// YYYY-MM-DD -YYYY-MM-DD YYYYY-MM-DD -YYYYY-MM-DD
sal_Int32 nSign = 1;
if (aStr[0] == '-')
{
nSign = -1;
aStr = aStr.copy(1);
}
const sal_Int32 nLen = aStr.getLength();
// Signed YYMMDD two digit year is invalid.
if (nLen == 6 && nSign == -1)
break;
// Now valid
// YYYYMMDD YYYYYMMDD YYMMDD
// YYYY-MM-DD YYYYY-MM-DD
if (nLen != 6 && (nLen < 8 || 11 < nLen))
break;
bool bUseTwoDigitYear = false;
OUString aYearStr, aMonthStr, aDayStr;
if (nLen == 6 || nLen == 8 || nLen == 9)
{
// ((Y)YY)YYMMDD
if (!comphelper::string::isdigitAsciiString(aStr))
break;
const sal_Int32 nMonthPos = (nLen == 8 ? 4 : (nLen == 6 ? 2 : 5));
if (nMonthPos == 2)
bUseTwoDigitYear = true;
aYearStr = aStr.copy( 0, nMonthPos );
aMonthStr = aStr.copy( nMonthPos, 2 );
aDayStr = aStr.copy( nMonthPos + 2, 2 );
}
else
{
// (Y)YYYY-MM-DD
const sal_Int32 nMonthSep = (nLen == 11 ? 5 : 4);
if (aStr.indexOf('-') != nMonthSep)
break;
if (aStr.indexOf('-', nMonthSep + 1) != nMonthSep + 3)
break;
aYearStr = aStr.copy( 0, nMonthSep );
aMonthStr = aStr.copy( nMonthSep + 1, 2 );
aDayStr = aStr.copy( nMonthSep + 4, 2 );
if ( !comphelper::string::isdigitAsciiString(aYearStr) ||
!comphelper::string::isdigitAsciiString(aMonthStr) ||
!comphelper::string::isdigitAsciiString(aDayStr))
break;
}
double dDate;
if (!implDateSerial( static_cast<sal_Int16>(nSign * aYearStr.toInt32()),
static_cast<sal_Int16>(aMonthStr.toInt32()), static_cast<sal_Int16>(aDayStr.toInt32()), bUseTwoDigitYear, false, dDate ))
break;
rPar.Get(0)->PutDate( dDate );
return;
}
while (false);
SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
void SbRtl_DateSerial(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_Int16 nYear = rPar.Get(1)->GetInteger();
sal_Int16 nMonth = rPar.Get(2)->GetInteger();
sal_Int16 nDay = rPar.Get(3)->GetInteger();
double dDate;
if( implDateSerial( nYear, nMonth, nDay, true, true, dDate ) )
{
rPar.Get(0)->PutDate( dDate );
}
}
void SbRtl_TimeSerial(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_Int16 nHour = rPar.Get(1)->GetInteger();
if ( nHour == 24 )
{
nHour = 0; // because of UNO DateTimes, which go till 24 o'clock
}
sal_Int16 nMinute = rPar.Get(2)->GetInteger();
sal_Int16 nSecond = rPar.Get(3)->GetInteger();
if ((nHour < 0 || nHour > 23) ||
(nMinute < 0 || nMinute > 59 ) ||
(nSecond < 0 || nSecond > 59 ))
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
rPar.Get(0)->PutDate( implTimeSerial(nHour, nMinute, nSecond) ); // JSM
}
void SbRtl_DateValue(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
// #39629 check GetSbData()->pInst, can be called from the URL line
std::shared_ptr<SvNumberFormatter> pFormatter;
if( GetSbData()->pInst )
{
pFormatter = GetSbData()->pInst->GetNumberFormatter();
}
else
{
sal_uInt32 n; // Dummy
pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
}
LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
double fResult;
OUString aStr( rPar.Get(1)->GetOUString() );
bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
SvNumFormatType nType = pFormatter->GetType( nIndex );
// DateValue("February 12, 1969") raises error if the system locale is not en_US
// It seems that both locale number formatter and English number
// formatter are supported in Visual Basic.
if( !bSuccess && ( eLangType != LANGUAGE_ENGLISH_US ) )
{
// Try using LANGUAGE_ENGLISH_US to get the date value.
nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US);
bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
nType = pFormatter->GetType( nIndex );
}
if(bSuccess && (nType==SvNumFormatType::DATE || nType==SvNumFormatType::DATETIME))
{
if ( nType == SvNumFormatType::DATETIME )
{
// cut time
if ( fResult > 0.0 )
{
fResult = floor( fResult );
}
else
{
fResult = ceil( fResult );
}
}
rPar.Get(0)->PutDate( fResult );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
}
}
}
void SbRtl_TimeValue(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
std::shared_ptr<SvNumberFormatter> pFormatter;
if( GetSbData()->pInst )
pFormatter = GetSbData()->pInst->GetNumberFormatter();
else
{
sal_uInt32 n;
pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
}
sal_uInt32 nIndex = 0;
double fResult;
bool bSuccess = pFormatter->IsNumberFormat( rPar.Get(1)->GetOUString(),
nIndex, fResult );
SvNumFormatType nType = pFormatter->GetType(nIndex);
if(bSuccess && (nType==SvNumFormatType::TIME||nType==SvNumFormatType::DATETIME))
{
if ( nType == SvNumFormatType::DATETIME )
{
// cut days
fResult = fmod( fResult, 1 );
}
rPar.Get(0)->PutDate( fResult );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
}
}
}
void SbRtl_Day(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariableRef pArg = rPar.Get( 1 );
double aDate = pArg->GetDate();
sal_Int16 nDay = implGetDateDay( aDate );
rPar.Get(0)->PutInteger( nDay );
}
}
void SbRtl_Year(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
sal_Int16 nYear = implGetDateYear( rPar.Get(1)->GetDate() );
rPar.Get(0)->PutInteger( nYear );
}
}
sal_Int16 implGetHour( double dDate )
{
double nFrac = dDate - floor( dDate );
nFrac *= 86400.0;
sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
sal_Int16 nHour = static_cast<sal_Int16>(nSeconds / 3600);
return nHour;
}
void SbRtl_Hour(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
double nArg = rPar.Get(1)->GetDate();
sal_Int16 nHour = implGetHour( nArg );
rPar.Get(0)->PutInteger( nHour );
}
}
void SbRtl_Minute(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
double nArg = rPar.Get(1)->GetDate();
sal_Int16 nMin = implGetMinute( nArg );
rPar.Get(0)->PutInteger( nMin );
}
}
void SbRtl_Month(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
sal_Int16 nMonth = implGetDateMonth( rPar.Get(1)->GetDate() );
rPar.Get(0)->PutInteger( nMonth );
}
}
sal_Int16 implGetSecond( double dDate )
{
double nFrac = dDate - floor( dDate );
nFrac *= 86400.0;
sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds / 3600);
nSeconds -= nTemp * 3600;
nTemp = static_cast<sal_Int16>(nSeconds / 60);
nSeconds -= nTemp * 60;
sal_Int16 nRet = static_cast<sal_Int16>(nSeconds);
return nRet;
}
void SbRtl_Second(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
double nArg = rPar.Get(1)->GetDate();
sal_Int16 nSecond = implGetSecond( nArg );
rPar.Get(0)->PutInteger( nSecond );
}
}
double Now_Impl()
{
Date aDate( Date::SYSTEM );
tools::Time aTime( tools::Time::SYSTEM );
double aSerial = static_cast<double>(GetDayDiff( aDate ));
long nSeconds = aTime.GetHour();
nSeconds *= 3600;
nSeconds += aTime.GetMin() * 60;
nSeconds += aTime.GetSec();
double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
aSerial += nDays;
return aSerial;
}
// Date Now()
void SbRtl_Now(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutDate( Now_Impl() );
}
// Date Time()
void SbRtl_Time(StarBASIC *, SbxArray & rPar, bool bWrite)
{
if ( !bWrite )
{
tools::Time aTime( tools::Time::SYSTEM );
SbxVariable* pMeth = rPar.Get( 0 );
OUString aRes;
if( pMeth->IsFixed() )
{
// Time$: hh:mm:ss
char buf[ 20 ];
snprintf( buf, sizeof(buf), "%02d:%02d:%02d",
aTime.GetHour(), aTime.GetMin(), aTime.GetSec() );
aRes = OUString::createFromAscii( buf );
}
else
{
// Time: system dependent
long nSeconds=aTime.GetHour();
nSeconds *= 3600;
nSeconds += aTime.GetMin() * 60;
nSeconds += aTime.GetSec();
double nDays = static_cast<double>(nSeconds) * ( 1.0 / (24.0*3600.0) );
Color* pCol;
std::shared_ptr<SvNumberFormatter> pFormatter;
sal_uInt32 nIndex;
if( GetSbData()->pInst )
{
pFormatter = GetSbData()->pInst->GetNumberFormatter();
nIndex = GetSbData()->pInst->GetStdTimeIdx();
}
else
{
sal_uInt32 n; // Dummy
pFormatter = SbiInstance::PrepareNumberFormatter( n, nIndex, n );
}
pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
}
pMeth->PutString( aRes );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
}
}
void SbRtl_Timer(StarBASIC *, SbxArray & rPar, bool)
{
tools::Time aTime( tools::Time::SYSTEM );
long nSeconds = aTime.GetHour();
nSeconds *= 3600;
nSeconds += aTime.GetMin() * 60;
nSeconds += aTime.GetSec();
rPar.Get(0)->PutDate( static_cast<double>(nSeconds) );
}
void SbRtl_Date(StarBASIC *, SbxArray & rPar, bool bWrite)
{
if ( !bWrite )
{
Date aToday( Date::SYSTEM );
double nDays = static_cast<double>(GetDayDiff( aToday ));
SbxVariable* pMeth = rPar.Get( 0 );
if( pMeth->IsString() )
{
OUString aRes;
Color* pCol;
std::shared_ptr<SvNumberFormatter> pFormatter;
sal_uInt32 nIndex;
if( GetSbData()->pInst )
{
pFormatter = GetSbData()->pInst->GetNumberFormatter();
nIndex = GetSbData()->pInst->GetStdDateIdx();
}
else
{
sal_uInt32 n;
pFormatter = SbiInstance::PrepareNumberFormatter( nIndex, n, n );
}
pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
pMeth->PutString( aRes );
}
else
{
pMeth->PutDate( nDays );
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
}
}
void SbRtl_IsArray(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
rPar.Get(0)->PutBool((rPar.Get(1)->GetType() & SbxARRAY) != 0);
}
}
void SbRtl_IsObject(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariable* pVar = rPar.Get(1);
bool bObject = pVar->IsObject();
SbxBase* pObj = (bObject ? pVar->GetObject() : nullptr);
SbUnoClass* pUnoClass;
if( pObj && ( pUnoClass=dynamic_cast<SbUnoClass*>( pObj) ) != nullptr )
{
bObject = pUnoClass->getUnoClass().is();
}
rPar.Get( 0 )->PutBool( bObject );
}
}
void SbRtl_IsDate(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
// #46134 only string is converted, all other types result in sal_False
SbxVariableRef xArg = rPar.Get( 1 );
SbxDataType eType = xArg->GetType();
bool bDate = false;
if( eType == SbxDATE )
{
bDate = true;
}
else if( eType == SbxSTRING )
{
ErrCode nPrevError = SbxBase::GetError();
SbxBase::ResetError();
// force conversion of the parameter to SbxDATE
xArg->SbxValue::GetDate();
bDate = !SbxBase::IsError();
SbxBase::ResetError();
SbxBase::SetError( nPrevError );
}
rPar.Get( 0 )->PutBool( bDate );
}
}
void SbRtl_IsEmpty(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariable* pVar = nullptr;
if( SbiRuntime::isVBAEnabled() )
{
pVar = getDefaultProp( rPar.Get(1) );
}
if ( pVar )
{
pVar->Broadcast( SfxHintId::BasicDataWanted );
rPar.Get( 0 )->PutBool( pVar->IsEmpty() );
}
else
{
rPar.Get( 0 )->PutBool( rPar.Get(1)->IsEmpty() );
}
}
}
void SbRtl_IsError(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxVariable* pVar =rPar.Get( 1 );
SbUnoObject* pObj = dynamic_cast<SbUnoObject*>( pVar );
if ( !pObj )
{
if ( SbxBase* pBaseObj = (pVar->IsObject() ? pVar->GetObject() : nullptr) )
{
pObj = dynamic_cast<SbUnoObject*>( pBaseObj );
}
}
uno::Reference< script::XErrorQuery > xError;
if ( pObj )
{
xError.set( pObj->getUnoAny(), uno::UNO_QUERY );
}
if ( xError.is() )
{
rPar.Get( 0 )->PutBool( xError->hasError() );
}
else
{
rPar.Get( 0 )->PutBool( rPar.Get(1)->IsErr() );
}
}
}
void SbRtl_IsNull(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
// #51475 because of Uno-objects return true
// even if the pObj value is NULL
SbxVariableRef pArg = rPar.Get( 1 );
bool bNull = rPar.Get(1)->IsNull();
if( !bNull && pArg->GetType() == SbxOBJECT )
{
SbxBase* pObj = pArg->GetObject();
if( !pObj )
{
bNull = true;
}
}
rPar.Get( 0 )->PutBool( bNull );
}
}
void SbRtl_IsNumeric(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
rPar.Get( 0 )->PutBool( rPar.Get( 1 )->IsNumericRTL() );
}
}
void SbRtl_IsMissing(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
// #57915 Missing is reported by an error
rPar.Get( 0 )->PutBool( rPar.Get(1)->IsErr() );
}
}
// Function looks for wildcards, removes them and always returns the pure path
OUString implSetupWildcard( const OUString& rFileParam, SbiRTLData* pRTLData )
{
static sal_Char cDelim1 = '/';
static sal_Char cDelim2 = '\\';
static sal_Char cWild1 = '*';
static sal_Char cWild2 = '?';
pRTLData->pWildCard.reset();
pRTLData->sFullNameToBeChecked.clear();
OUString aFileParam = rFileParam;
sal_Int32 nLastWild = aFileParam.lastIndexOf( cWild1 );
if( nLastWild < 0 )
{
nLastWild = aFileParam.lastIndexOf( cWild2 );
}
bool bHasWildcards = ( nLastWild >= 0 );
sal_Int32 nLastDelim = aFileParam.lastIndexOf( cDelim1 );
if( nLastDelim < 0 )
{
nLastDelim = aFileParam.lastIndexOf( cDelim2 );
}
if( bHasWildcards )
{
// Wildcards in path?
if( nLastDelim >= 0 && nLastDelim > nLastWild )
{
return aFileParam;
}
}
else
{
OUString aPathStr = getFullPath( aFileParam );
if( nLastDelim != aFileParam.getLength() - 1 )
{
pRTLData->sFullNameToBeChecked = aPathStr;
}
return aPathStr;
}
OUString aPureFileName;
if( nLastDelim < 0 )
{
aPureFileName = aFileParam;
aFileParam.clear();
}
else
{
aPureFileName = aFileParam.copy( nLastDelim + 1 );
aFileParam = aFileParam.copy( 0, nLastDelim );
}
// Try again to get a valid URL/UNC-path with only the path
OUString aPathStr = getFullPath( aFileParam );
// Is there a pure file name left? Otherwise the path is
// invalid anyway because it was not accepted by OSL before
if (aPureFileName != "*")
{
pRTLData->pWildCard = o3tl::make_unique<WildCard>( aPureFileName );
}
return aPathStr;
}
inline bool implCheckWildcard( const OUString& rName, SbiRTLData const * pRTLData )
{
bool bMatch = true;
if( pRTLData->pWildCard )
{
bMatch = pRTLData->pWildCard->Matches( rName );
}
return bMatch;
}
bool isRootDir( const OUString& aDirURLStr )
{
INetURLObject aDirURLObj( aDirURLStr );
bool bRoot = false;
// Check if it's a root directory
sal_Int32 nCount = aDirURLObj.getSegmentCount();
// No segment means Unix root directory "file:///"
if( nCount == 0 )
{
bRoot = true;
}
// Exactly one segment needs further checking, because it
// can be Unix "file:///foo/" -> no root
// or Windows "file:///c:/" -> root
else if( nCount == 1 )
{
OUString aSeg1 = aDirURLObj.getName( 0, true,
INetURLObject::DecodeMechanism::WithCharset );
if( aSeg1[1] == ':' )
{
bRoot = true;
}
}
// More than one segments can never be root
// so bRoot remains false
return bRoot;
}
void SbRtl_Dir(StarBASIC *, SbxArray & rPar, bool)
{
OUString aPath;
sal_uInt16 nParCount = rPar.Count();
if( nParCount > 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbiRTLData* pRTLData = GetSbData()->pInst->GetRTLData();
// #34645: can also be called from the URL line via 'macro: Dir'
// there's no pRTLDate existing in that case and the method must be left
if( !pRTLData )
{
return;
}
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
if ( nParCount >= 2 )
{
OUString aFileParam = rPar.Get(1)->GetOUString();
OUString aFileURLStr = implSetupWildcard( aFileParam, pRTLData );
if( !pRTLData->sFullNameToBeChecked.isEmpty())
{
bool bExists = false;
try { bExists = xSFI->exists( aFileURLStr ); }
catch(const Exception & ) {}
OUString aNameOnlyStr;
if( bExists )
{
INetURLObject aFileURL( aFileURLStr );
aNameOnlyStr = aFileURL.getName( INetURLObject::LAST_SEGMENT,
true, INetURLObject::DecodeMechanism::WithCharset );
}
rPar.Get(0)->PutString( aNameOnlyStr );
return;
}
try
{
OUString aDirURLStr;
bool bFolder = xSFI->isFolder( aFileURLStr );
if( bFolder )
{
aDirURLStr = aFileURLStr;
}
else
{
rPar.Get(0)->PutString( "" );
}
SbAttributes nFlags = SbAttributes::NONE;
if ( nParCount > 2 )
{
pRTLData->nDirFlags = nFlags = static_cast<SbAttributes>(rPar.Get(2)->GetInteger());
}
else
{
pRTLData->nDirFlags = SbAttributes::NONE;
}
// Read directory
bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
pRTLData->aDirSeq = xSFI->getFolderContents( aDirURLStr, bIncludeFolders );
pRTLData->nCurDirPos = 0;
// #78651 Add "." and ".." directories for VB compatibility
if( bIncludeFolders )
{
bool bRoot = isRootDir( aDirURLStr );
// If it's no root directory we flag the need for
// the "." and ".." directories by the value -2
// for the actual position. Later for -2 will be
// returned "." and for -1 ".."
if( !bRoot )
{
pRTLData->nCurDirPos = -2;
}
}
}
catch(const Exception & )
{
}
}
if( pRTLData->aDirSeq.getLength() > 0 )
{
bool bFolderFlag = bool(pRTLData->nDirFlags & SbAttributes::DIRECTORY);
SbiInstance* pInst = GetSbData()->pInst;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
for( ;; )
{
if( pRTLData->nCurDirPos < 0 )
{
if( pRTLData->nCurDirPos == -2 )
{
aPath = ".";
}
else if( pRTLData->nCurDirPos == -1 )
{
aPath = "..";
}
pRTLData->nCurDirPos++;
}
else if( pRTLData->nCurDirPos >= pRTLData->aDirSeq.getLength() )
{
pRTLData->aDirSeq.realloc( 0 );
aPath.clear();
break;
}
else
{
OUString aFile = pRTLData->aDirSeq.getConstArray()[pRTLData->nCurDirPos++];
if( bCompatibility )
{
if( !bFolderFlag )
{
bool bFolder = xSFI->isFolder( aFile );
if( bFolder )
{
continue;
}
}
}
else
{
// Only directories
if( bFolderFlag )
{
bool bFolder = xSFI->isFolder( aFile );
if( !bFolder )
{
continue;
}
}
}
INetURLObject aURL( aFile );
aPath = aURL.getName( INetURLObject::LAST_SEGMENT, true,
INetURLObject::DecodeMechanism::WithCharset );
}
bool bMatch = implCheckWildcard( aPath, pRTLData );
if( !bMatch )
{
continue;
}
break;
}
}
rPar.Get(0)->PutString( aPath );
}
}
else
{
// TODO: OSL
if ( nParCount >= 2 )
{
OUString aFileParam = rPar.Get(1)->GetOUString();
OUString aDirURL = implSetupWildcard( aFileParam, pRTLData );
SbAttributes nFlags = SbAttributes::NONE;
if ( nParCount > 2 )
{
pRTLData->nDirFlags = nFlags = static_cast<SbAttributes>( rPar.Get(2)->GetInteger() );
}
else
{
pRTLData->nDirFlags = SbAttributes::NONE;
}
// Read directory
bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
pRTLData->pDir = o3tl::make_unique<Directory>( aDirURL );
FileBase::RC nRet = pRTLData->pDir->open();
if( nRet != FileBase::E_None )
{
pRTLData->pDir.reset();
rPar.Get(0)->PutString( OUString() );
return;
}
// #86950 Add "." and ".." directories for VB compatibility
pRTLData->nCurDirPos = 0;
if( bIncludeFolders )
{
bool bRoot = isRootDir( aDirURL );
// If it's no root directory we flag the need for
// the "." and ".." directories by the value -2
// for the actual position. Later for -2 will be
// returned "." and for -1 ".."
if( !bRoot )
{
pRTLData->nCurDirPos = -2;
}
}
}
if( pRTLData->pDir )
{
bool bFolderFlag = bool(pRTLData->nDirFlags & SbAttributes::DIRECTORY);
for( ;; )
{
if( pRTLData->nCurDirPos < 0 )
{
if( pRTLData->nCurDirPos == -2 )
{
aPath = ".";
}
else if( pRTLData->nCurDirPos == -1 )
{
aPath = "..";
}
pRTLData->nCurDirPos++;
}
else
{
DirectoryItem aItem;
FileBase::RC nRet = pRTLData->pDir->getNextItem( aItem );
if( nRet != FileBase::E_None )
{
pRTLData->pDir.reset();
aPath.clear();
break;
}
// Handle flags
FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName );
nRet = aItem.getFileStatus( aFileStatus );
if( nRet != FileBase::E_None )
{
SAL_WARN("basic", "getFileStatus failed");
continue;
}
// Only directories?
if( bFolderFlag )
{
FileStatus::Type aType = aFileStatus.getFileType();
bool bFolder = isFolder( aType );
if( !bFolder )
{
continue;
}
}
aPath = aFileStatus.getFileName();
}
bool bMatch = implCheckWildcard( aPath, pRTLData );
if( !bMatch )
{
continue;
}
break;
}
}
rPar.Get(0)->PutString( aPath );
}
}
}
void SbRtl_GetAttr(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
{
(void)pBasic;
(void)bWrite;
if ( rPar.Count() == 2 )
{
sal_Int16 nFlags = 0;
// In Windows, we want to use Windows API to get the file attributes
// for VBA interoperability.
#if defined(_WIN32)
if( SbiRuntime::isVBAEnabled() )
{
OUString aPathURL = getFullPath( rPar.Get(1)->GetOUString() );
OUString aPath;
FileBase::getSystemPathFromFileURL( aPathURL, aPath );
DWORD nRealFlags = GetFileAttributesW (o3tl::toW(aPath.getStr()));
if (nRealFlags != 0xffffffff)
{
if (nRealFlags == FILE_ATTRIBUTE_NORMAL)
{
nRealFlags = 0;
}
nFlags = static_cast<sal_Int16>(nRealFlags);
}
else
{
StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
}
rPar.Get(0)->PutInteger( nFlags );
return;
}
#endif
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
OUString aPath = getFullPath( rPar.Get(1)->GetOUString() );
bool bExists = false;
try { bExists = xSFI->exists( aPath ); }
catch(const Exception & ) {}
if( !bExists )
{
StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
return;
}
bool bReadOnly = xSFI->isReadOnly( aPath );
bool bHidden = xSFI->isHidden( aPath );
bool bDirectory = xSFI->isFolder( aPath );
if( bReadOnly )
{
nFlags |= sal_uInt16(SbAttributes::READONLY);
}
if( bHidden )
{
nFlags |= sal_uInt16(SbAttributes::HIDDEN);
}
if( bDirectory )
{
nFlags |= sal_uInt16(SbAttributes::DIRECTORY);
}
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_IO_GENERAL );
}
}
}
else
{
DirectoryItem aItem;
DirectoryItem::get( getFullPath( rPar.Get(1)->GetOUString() ), aItem );
FileStatus aFileStatus( osl_FileStatus_Mask_Attributes | osl_FileStatus_Mask_Type );
aItem.getFileStatus( aFileStatus );
sal_uInt64 nAttributes = aFileStatus.getAttributes();
bool bReadOnly = (nAttributes & osl_File_Attribute_ReadOnly) != 0;
FileStatus::Type aType = aFileStatus.getFileType();
bool bDirectory = isFolder( aType );
if( bReadOnly )
{
nFlags |= sal_uInt16(SbAttributes::READONLY);
}
if( bDirectory )
{
nFlags |= sal_uInt16(SbAttributes::DIRECTORY);
}
}
rPar.Get(0)->PutInteger( nFlags );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
void SbRtl_FileDateTime(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aPath = rPar.Get(1)->GetOUString();
tools::Time aTime( tools::Time::EMPTY );
Date aDate( Date::EMPTY );
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
util::DateTime aUnoDT = xSFI->getDateTimeModified( aPath );
aTime = tools::Time( aUnoDT );
aDate = Date( aUnoDT );
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_IO_GENERAL );
}
}
}
else
{
DirectoryItem aItem;
DirectoryItem::get( getFullPath( aPath ), aItem );
FileStatus aFileStatus( osl_FileStatus_Mask_ModifyTime );
aItem.getFileStatus( aFileStatus );
TimeValue aTimeVal = aFileStatus.getModifyTime();
oslDateTime aDT;
osl_getDateTimeFromTimeValue( &aTimeVal, &aDT );
aTime = tools::Time( aDT.Hours, aDT.Minutes, aDT.Seconds, aDT.NanoSeconds );
aDate = Date( aDT.Day, aDT.Month, aDT.Year );
}
double fSerial = static_cast<double>(GetDayDiff( aDate ));
long nSeconds = aTime.GetHour();
nSeconds *= 3600;
nSeconds += aTime.GetMin() * 60;
nSeconds += aTime.GetSec();
double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
fSerial += nDays;
Color* pCol;
std::shared_ptr<SvNumberFormatter> pFormatter;
sal_uInt32 nIndex;
if( GetSbData()->pInst )
{
pFormatter = GetSbData()->pInst->GetNumberFormatter();
nIndex = GetSbData()->pInst->GetStdDateTimeIdx();
}
else
{
sal_uInt32 n;
pFormatter = SbiInstance::PrepareNumberFormatter( n, n, nIndex );
}
OUString aRes;
pFormatter->GetOutputString( fSerial, nIndex, aRes, &pCol );
rPar.Get(0)->PutString( aRes );
}
}
void SbRtl_EOF(StarBASIC *, SbxArray & rPar, bool)
{
// No changes for UCB
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
sal_Int16 nChannel = rPar.Get(1)->GetInteger();
SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
SbiStream* pSbStrm = pIO->GetStream( nChannel );
if ( !pSbStrm )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
return;
}
bool beof;
SvStream* pSvStrm = pSbStrm->GetStrm();
if ( pSbStrm->IsText() )
{
char cBla;
(*pSvStrm).ReadChar( cBla ); // can we read another character?
beof = pSvStrm->eof();
if ( !beof )
{
pSvStrm->SeekRel( -1 );
}
}
else
{
beof = pSvStrm->eof(); // for binary data!
}
rPar.Get(0)->PutBool( beof );
}
}
void SbRtl_FileAttr(StarBASIC *, SbxArray & rPar, bool)
{
// No changes for UCB
// #57064 Although this function doesn't operate with DirEntry, it is
// not touched by the adjustment to virtual URLs, as it only works on
// already opened files and the name doesn't matter there.
if ( rPar.Count() != 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
sal_Int16 nChannel = rPar.Get(1)->GetInteger();
SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
SbiStream* pSbStrm = pIO->GetStream( nChannel );
if ( !pSbStrm )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
return;
}
sal_Int16 nRet;
if ( rPar.Get(2)->GetInteger() == 1 )
{
nRet = static_cast<sal_Int16>(pSbStrm->GetMode());
}
else
{
nRet = 0; // System file handle not supported
}
rPar.Get(0)->PutInteger( nRet );
}
}
void SbRtl_Loc(StarBASIC *, SbxArray & rPar, bool)
{
// No changes for UCB
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
sal_Int16 nChannel = rPar.Get(1)->GetInteger();
SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
SbiStream* pSbStrm = pIO->GetStream( nChannel );
if ( !pSbStrm )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
return;
}
SvStream* pSvStrm = pSbStrm->GetStrm();
std::size_t nPos;
if( pSbStrm->IsRandom())
{
short nBlockLen = pSbStrm->GetBlockLen();
nPos = nBlockLen ? (pSvStrm->Tell() / nBlockLen) : 0;
nPos++; // block positions starting at 1
}
else if ( pSbStrm->IsText() )
{
nPos = pSbStrm->GetLine();
}
else if( pSbStrm->IsBinary() )
{
nPos = pSvStrm->Tell();
}
else if ( pSbStrm->IsSeq() )
{
nPos = ( pSvStrm->Tell()+1 ) / 128;
}
else
{
nPos = pSvStrm->Tell();
}
rPar.Get(0)->PutLong( static_cast<sal_Int32>(nPos) );
}
}
void SbRtl_Lof(StarBASIC *, SbxArray & rPar, bool)
{
// No changes for UCB
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
sal_Int16 nChannel = rPar.Get(1)->GetInteger();
SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
SbiStream* pSbStrm = pIO->GetStream( nChannel );
if ( !pSbStrm )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
return;
}
SvStream* pSvStrm = pSbStrm->GetStrm();
sal_uInt64 const nOldPos = pSvStrm->Tell();
sal_uInt64 const nLen = pSvStrm->Seek( STREAM_SEEK_TO_END );
pSvStrm->Seek( nOldPos );
rPar.Get(0)->PutLong( static_cast<sal_Int32>(nLen) );
}
}
void SbRtl_Seek(StarBASIC *, SbxArray & rPar, bool)
{
// No changes for UCB
int nArgs = static_cast<int>(rPar.Count());
if ( nArgs < 2 || nArgs > 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_Int16 nChannel = rPar.Get(1)->GetInteger();
SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
SbiStream* pSbStrm = pIO->GetStream( nChannel );
if ( !pSbStrm )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
return;
}
SvStream* pStrm = pSbStrm->GetStrm();
if ( nArgs == 2 ) // Seek-Function
{
sal_uInt64 nPos = pStrm->Tell();
if( pSbStrm->IsRandom() )
{
nPos = nPos / pSbStrm->GetBlockLen();
}
nPos++; // Basic counts from 1
rPar.Get(0)->PutLong( static_cast<sal_Int32>(nPos) );
}
else // Seek-Statement
{
sal_Int32 nPos = rPar.Get(2)->GetLong();
if ( nPos < 1 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
nPos--; // Basic counts from 1, SvStreams count from 0
pSbStrm->SetExpandOnWriteTo( 0 );
if ( pSbStrm->IsRandom() )
{
nPos *= pSbStrm->GetBlockLen();
}
pStrm->Seek( static_cast<sal_uInt64>(nPos) );
pSbStrm->SetExpandOnWriteTo( nPos );
}
}
void SbRtl_Format(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt16 nArgCount = rPar.Count();
if ( nArgCount < 2 || nArgCount > 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aResult;
if( nArgCount == 2 )
{
rPar.Get(1)->Format( aResult );
}
else
{
OUString aFmt( rPar.Get(2)->GetOUString() );
rPar.Get(1)->Format( aResult, &aFmt );
}
rPar.Get(0)->PutString( aResult );
}
}
namespace {
// note: BASIC does not use comphelper::random, because
// Randomize(int) must be supported and should not affect non-BASIC random use
struct RandomNumberGenerator
{
std::mt19937 global_rng;
RandomNumberGenerator()
{
try
{
std::random_device rd;
// initialises the state of the global random number generator
// should only be called once.
// (note, a few std::variate_generator<> (like normal) have their
// own state which would need a reset as well to guarantee identical
// sequence of numbers, e.g. via myrand.distribution().reset())
global_rng.seed(rd() ^ time(nullptr));
}
catch (std::runtime_error& e)
{
SAL_WARN("basic", "Using std::random_device failed: " << e.what());
global_rng.seed(time(nullptr));
}
}
};
class theRandomNumberGenerator : public rtl::Static<RandomNumberGenerator, theRandomNumberGenerator> {};
}
void SbRtl_Randomize(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() > 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
if( rPar.Count() == 2 )
{
int nSeed = static_cast<int>(rPar.Get(1)->GetInteger());
theRandomNumberGenerator::get().global_rng.seed(nSeed);
}
// without parameter, no need to do anything - RNG is seeded at first use
}
void SbRtl_Rnd(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() > 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
std::uniform_real_distribution<double> dist(0.0, 1.0);
double const tmp(dist(theRandomNumberGenerator::get().global_rng));
rPar.Get(0)->PutDouble(tmp);
}
}
// Syntax: Shell("Path",[ Window-Style,[ "Params", [ bSync = sal_False ]]])
// WindowStyles (VBA compatible):
// 2 == Minimized
// 3 == Maximized
// 10 == Full-Screen (text mode applications OS/2, WIN95, WNT)
// HACK: The WindowStyle will be passed to
// Application::StartApp in Creator. Format: "xxxx2"
void SbRtl_Shell(StarBASIC *, SbxArray & rPar, bool)
{
std::size_t nArgCount = rPar.Count();
if ( nArgCount < 2 || nArgCount > 5 )
{
rPar.Get(0)->PutLong(0);
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
oslProcessOption nOptions = osl_Process_SEARCHPATH | osl_Process_DETACHED;
OUString aCmdLine = rPar.Get(1)->GetOUString();
// attach additional parameters - everything must be parsed anyway
if( nArgCount >= 4 )
{
OUString tmp = rPar.Get(3)->GetOUString().trim();
if (!tmp.isEmpty())
{
aCmdLine += " ";
aCmdLine += tmp;
}
}
else if( aCmdLine.isEmpty() )
{
// avoid special treatment (empty list)
aCmdLine += " ";
}
sal_Int32 nLen = aCmdLine.getLength();
// #55735 if there are parameters, they have to be separated
// #72471 also separate the single parameters
std::vector<OUString> aTokenVector;
OUString aToken;
sal_Int32 i = 0;
sal_Unicode c;
while( i < nLen )
{
for ( ;; ++i )
{
c = aCmdLine[ i ];
if ( c != ' ' && c != '\t' )
{
break;
}
}
if( c == '\"' || c == '\'' )
{
sal_Int32 iFoundPos = aCmdLine.indexOf( c, i + 1 );
if( iFoundPos < 0 )
{
aToken = aCmdLine.copy( i);
i = nLen;
}
else
{
aToken = aCmdLine.copy( i + 1, (iFoundPos - i - 1) );
i = iFoundPos + 1;
}
}
else
{
sal_Int32 iFoundSpacePos = aCmdLine.indexOf( ' ', i );
sal_Int32 iFoundTabPos = aCmdLine.indexOf( '\t', i );
sal_Int32 iFoundPos = iFoundSpacePos >= 0 ? iFoundTabPos >= 0 ? std::min( iFoundSpacePos, iFoundTabPos ) : iFoundSpacePos : -1;
if( iFoundPos < 0 )
{
aToken = aCmdLine.copy( i );
i = nLen;
}
else
{
aToken = aCmdLine.copy( i, (iFoundPos - i) );
i = iFoundPos;
}
}
// insert into the list
aTokenVector.push_back( aToken );
}
// #55735 / #72471 end
sal_Int16 nWinStyle = 0;
if( nArgCount >= 3 )
{
nWinStyle = rPar.Get(2)->GetInteger();
switch( nWinStyle )
{
case 2:
nOptions |= osl_Process_MINIMIZED;
break;
case 3:
nOptions |= osl_Process_MAXIMIZED;
break;
case 10:
nOptions |= osl_Process_FULLSCREEN;
break;
}
bool bSync = false;
if( nArgCount >= 5 )
{
bSync = rPar.Get(4)->GetBool();
}
if( bSync )
{
nOptions |= osl_Process_WAIT;
}
}
// #72471 work parameter(s) up
std::vector<OUString>::const_iterator iter = aTokenVector.begin();
OUString aOUStrProgURL = getFullPath( *iter );
++iter;
sal_uInt16 nParamCount = sal::static_int_cast< sal_uInt16 >(aTokenVector.size() - 1 );
std::unique_ptr<rtl_uString*[]> pParamList;
if( nParamCount )
{
pParamList.reset( new rtl_uString*[nParamCount]);
for(int iVector = 0; iter != aTokenVector.end(); ++iVector, ++iter)
{
const OUString& rParamStr = (*iter);
pParamList[iVector] = nullptr;
rtl_uString_assign(&(pParamList[iVector]), rParamStr.pData);
}
}
oslProcess pApp;
bool bSucc = osl_executeProcess(
aOUStrProgURL.pData,
pParamList.get(),
nParamCount,
nOptions,
nullptr,
nullptr,
nullptr, 0,
&pApp ) == osl_Process_E_None;
// 53521 only free process handle on success
if (bSucc)
{
osl_freeProcessHandle( pApp );
}
for(int j = 0; j < nParamCount; ++j)
{
rtl_uString_release(pParamList[j]);
}
if( !bSucc )
{
StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
}
else
{
rPar.Get(0)->PutLong( 0 );
}
}
}
void SbRtl_VarType(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxDataType eType = rPar.Get(1)->GetType();
rPar.Get(0)->PutInteger( static_cast<sal_Int16>(eType) );
}
}
// Exported function
OUString getBasicTypeName( SbxDataType eType )
{
static const char* pTypeNames[] =
{
"Empty", // SbxEMPTY
"Null", // SbxNULL
"Integer", // SbxINTEGER
"Long", // SbxLONG
"Single", // SbxSINGLE
"Double", // SbxDOUBLE
"Currency", // SbxCURRENCY
"Date", // SbxDATE
"String", // SbxSTRING
"Object", // SbxOBJECT
"Error", // SbxERROR
"Boolean", // SbxBOOL
"Variant", // SbxVARIANT
"DataObject", // SbxDATAOBJECT
"Unknown Type",
"Unknown Type",
"Char", // SbxCHAR
"Byte", // SbxBYTE
"UShort", // SbxUSHORT
"ULong", // SbxULONG
"Long64", // SbxLONG64
"ULong64", // SbxULONG64
"Int", // SbxINT
"UInt", // SbxUINT
"Void", // SbxVOID
"HResult", // SbxHRESULT
"Pointer", // SbxPOINTER
"DimArray", // SbxDIMARRAY
"CArray", // SbxCARRAY
"Userdef", // SbxUSERDEF
"Lpstr", // SbxLPSTR
"Lpwstr", // SbxLPWSTR
"Unknown Type", // SbxCoreSTRING
"WString", // SbxWSTRING
"WChar", // SbxWCHAR
"Int64", // SbxSALINT64
"UInt64", // SbxSALUINT64
"Decimal", // SbxDECIMAL
};
size_t nPos = static_cast<size_t>(eType) & 0x0FFF;
const size_t nTypeNameCount = SAL_N_ELEMENTS( pTypeNames );
if ( nPos >= nTypeNameCount )
{
nPos = nTypeNameCount - 1;
}
return OUString::createFromAscii(pTypeNames[nPos]);
}
OUString getObjectTypeName( SbxVariable* pVar )
{
OUString sRet( "Object" );
if ( pVar )
{
SbxBase* pBaseObj = pVar->GetObject();
if( !pBaseObj )
{
sRet = "Nothing";
}
else
{
SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pVar );
if ( !pUnoObj )
{
pUnoObj = dynamic_cast<SbUnoObject*>( pBaseObj );
}
if ( pUnoObj )
{
Any aObj = pUnoObj->getUnoAny();
// For upstreaming unless we start to build oovbaapi by default
// we need to get detect the vba-ness of the object in some
// other way
// note: Automation objects do not support XServiceInfo
uno::Reference< XServiceInfo > xServInfo( aObj, uno::UNO_QUERY );
if ( xServInfo.is() )
{
// is this a VBA object ?
Sequence< OUString > sServices = xServInfo->getSupportedServiceNames();
if ( sServices.getLength() )
{
sRet = sServices[ 0 ];
}
}
else
{
uno::Reference< bridge::oleautomation::XAutomationObject > xAutoMation( aObj, uno::UNO_QUERY );
if ( xAutoMation.is() )
{
uno::Reference< script::XInvocation > xInv( aObj, uno::UNO_QUERY );
if ( xInv.is() )
{
try
{
xInv->getValue( "$GetTypeName" ) >>= sRet;
}
catch(const Exception& )
{
}
}
}
}
sal_Int32 nDot = sRet.lastIndexOf( '.' );
if ( nDot != -1 && nDot < sRet.getLength() )
{
sRet = sRet.copy( nDot + 1 );
}
}
}
}
return sRet;
}
void SbRtl_TypeName(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
SbxDataType eType = rPar.Get(1)->GetType();
bool bIsArray = ( ( eType & SbxARRAY ) != 0 );
OUString aRetStr;
if ( SbiRuntime::isVBAEnabled() && eType == SbxOBJECT )
{
aRetStr = getObjectTypeName( rPar.Get(1) );
}
else
{
aRetStr = getBasicTypeName( eType );
}
if( bIsArray )
{
aRetStr += "()";
}
rPar.Get(0)->PutString( aRetStr );
}
}
void SbRtl_Len(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
const OUString& rStr = rPar.Get(1)->GetOUString();
rPar.Get(0)->PutLong( rStr.getLength() );
}
}
void SbRtl_DDEInitiate(StarBASIC *, SbxArray & rPar, bool)
{
int nArgs = static_cast<int>(rPar.Count());
if ( nArgs != 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
const OUString& rApp = rPar.Get(1)->GetOUString();
const OUString& rTopic = rPar.Get(2)->GetOUString();
SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
size_t nChannel;
ErrCode nDdeErr = pDDE->Initiate( rApp, rTopic, nChannel );
if( nDdeErr )
{
StarBASIC::Error( nDdeErr );
}
else
{
rPar.Get(0)->PutInteger( static_cast<sal_Int16>(nChannel) );
}
}
void SbRtl_DDETerminate(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
int nArgs = static_cast<int>(rPar.Count());
if ( nArgs != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
size_t nChannel = rPar.Get(1)->GetInteger();
SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
ErrCode nDdeErr = pDDE->Terminate( nChannel );
if( nDdeErr )
{
StarBASIC::Error( nDdeErr );
}
}
void SbRtl_DDETerminateAll(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
int nArgs = static_cast<int>(rPar.Count());
if ( nArgs != 1 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
ErrCode nDdeErr = pDDE->TerminateAll();
if( nDdeErr )
{
StarBASIC::Error( nDdeErr );
}
}
void SbRtl_DDERequest(StarBASIC *, SbxArray & rPar, bool)
{
int nArgs = static_cast<int>(rPar.Count());
if ( nArgs != 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
size_t nChannel = rPar.Get(1)->GetInteger();
const OUString& rItem = rPar.Get(2)->GetOUString();
SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
OUString aResult;
ErrCode nDdeErr = pDDE->Request( nChannel, rItem, aResult );
if( nDdeErr )
{
StarBASIC::Error( nDdeErr );
}
else
{
rPar.Get(0)->PutString( aResult );
}
}
void SbRtl_DDEExecute(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
int nArgs = static_cast<int>(rPar.Count());
if ( nArgs != 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
size_t nChannel = rPar.Get(1)->GetInteger();
const OUString& rCommand = rPar.Get(2)->GetOUString();
SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
ErrCode nDdeErr = pDDE->Execute( nChannel, rCommand );
if( nDdeErr )
{
StarBASIC::Error( nDdeErr );
}
}
void SbRtl_DDEPoke(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
int nArgs = static_cast<int>(rPar.Count());
if ( nArgs != 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
size_t nChannel = rPar.Get(1)->GetInteger();
const OUString& rItem = rPar.Get(2)->GetOUString();
const OUString& rData = rPar.Get(3)->GetOUString();
SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
ErrCode nDdeErr = pDDE->Poke( nChannel, rItem, rData );
if( nDdeErr )
{
StarBASIC::Error( nDdeErr );
}
}
void SbRtl_FreeFile(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 1 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
short nChannel = 1;
while( nChannel < CHANNELS )
{
SbiStream* pStrm = pIO->GetStream( nChannel );
if( !pStrm )
{
rPar.Get(0)->PutInteger( nChannel );
return;
}
nChannel++;
}
StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES );
}
void SbRtl_LBound(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt16 nParCount = rPar.Count();
if ( nParCount != 3 && nParCount != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxBase* pParObj = rPar.Get(1)->GetObject();
SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
if( pArr )
{
sal_Int32 nLower, nUpper;
short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
if( !pArr->GetDim32( nDim, nLower, nUpper ) )
StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
else
rPar.Get(0)->PutLong( nLower );
}
else
StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
}
void SbRtl_UBound(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt16 nParCount = rPar.Count();
if ( nParCount != 3 && nParCount != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxBase* pParObj = rPar.Get(1)->GetObject();
SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
if( pArr )
{
sal_Int32 nLower, nUpper;
short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
if( !pArr->GetDim32( nDim, nLower, nUpper ) )
StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
else
rPar.Get(0)->PutLong( nUpper );
}
else
StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
}
void SbRtl_RGB(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_Int32 nRed = rPar.Get(1)->GetInteger() & 0xFF;
sal_Int32 nGreen = rPar.Get(2)->GetInteger() & 0xFF;
sal_Int32 nBlue = rPar.Get(3)->GetInteger() & 0xFF;
sal_Int32 nRGB;
SbiInstance* pInst = GetSbData()->pInst;
bool bCompatibility = ( pInst && pInst->IsCompatibility() );
if( bCompatibility )
{
nRGB = (nBlue << 16) | (nGreen << 8) | nRed;
}
else
{
nRGB = (nRed << 16) | (nGreen << 8) | nBlue;
}
rPar.Get(0)->PutLong( nRGB );
}
void SbRtl_QBColor(StarBASIC *, SbxArray & rPar, bool)
{
static const sal_Int32 pRGB[] =
{
0x000000,
0x800000,
0x008000,
0x808000,
0x000080,
0x800080,
0x008080,
0xC0C0C0,
0x808080,
0xFF0000,
0x00FF00,
0xFFFF00,
0x0000FF,
0xFF00FF,
0x00FFFF,
0xFFFFFF,
};
if ( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_Int16 nCol = rPar.Get(1)->GetInteger();
if( nCol < 0 || nCol > 15 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_Int32 nRGB = pRGB[ nCol ];
rPar.Get(0)->PutLong( nRGB );
}
// StrConv(string, conversion, LCID)
void SbRtl_StrConv(StarBASIC *, SbxArray & rPar, bool)
{
std::size_t nArgCount = rPar.Count()-1;
if( nArgCount < 2 || nArgCount > 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
OUString aOldStr = rPar.Get(1)->GetOUString();
sal_Int32 nConversion = rPar.Get(2)->GetLong();
LanguageType nLanguage = LANGUAGE_SYSTEM;
sal_Int32 nOldLen = aOldStr.getLength();
if( nOldLen == 0 )
{
// null string,return
rPar.Get(0)->PutString(aOldStr);
return;
}
TransliterationFlags nType = TransliterationFlags::NONE;
if ( (nConversion & 0x03) == 3 ) // vbProperCase
{
const CharClass& rCharClass = GetCharClass();
aOldStr = rCharClass.titlecase( aOldStr.toAsciiLowerCase(), 0, nOldLen );
}
else if ( (nConversion & 0x01) == 1 ) // vbUpperCase
{
nType |= TransliterationFlags::LOWERCASE_UPPERCASE;
}
else if ( (nConversion & 0x02) == 2 ) // vbLowerCase
{
nType |= TransliterationFlags::UPPERCASE_LOWERCASE;
}
if ( (nConversion & 0x04) == 4 ) // vbWide
{
nType |= TransliterationFlags::HALFWIDTH_FULLWIDTH;
}
else if ( (nConversion & 0x08) == 8 ) // vbNarrow
{
nType |= TransliterationFlags::FULLWIDTH_HALFWIDTH;
}
if ( (nConversion & 0x10) == 16) // vbKatakana
{
nType |= TransliterationFlags::HIRAGANA_KATAKANA;
}
else if ( (nConversion & 0x20) == 32 ) // vbHiragana
{
nType |= TransliterationFlags::KATAKANA_HIRAGANA;
}
OUString aNewStr( aOldStr );
if( nType != TransliterationFlags::NONE )
{
uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
::utl::TransliterationWrapper aTransliterationWrapper( xContext, nType );
uno::Sequence<sal_Int32> aOffsets;
aTransliterationWrapper.loadModuleIfNeeded( nLanguage );
aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets );
}
if ( (nConversion & 0x40) == 64 ) // vbUnicode
{
// convert the string to byte string, preserving unicode (2 bytes per character)
sal_Int32 nSize = aNewStr.getLength()*2;
const sal_Unicode* pSrc = aNewStr.getStr();
std::unique_ptr<sal_Char[]> pChar(new sal_Char[nSize+1]);
for( sal_Int32 i=0; i < nSize; i++ )
{
pChar[i] = static_cast< sal_Char >( (i%2) ? ((*pSrc) >> 8) & 0xff : (*pSrc) & 0xff );
if( i%2 )
{
pSrc++;
}
}
pChar[nSize] = '\0';
OString aOStr(pChar.get());
// there is no concept about default codepage in unix. so it is incorrectly in unix
OUString aOUStr = OStringToOUString(aOStr, osl_getThreadTextEncoding());
rPar.Get(0)->PutString( aOUStr );
return;
}
else if ( (nConversion & 0x80) == 128 ) // vbFromUnicode
{
// there is no concept about default codepage in unix. so it is incorrectly in unix
OString aOStr = OUStringToOString(aNewStr,osl_getThreadTextEncoding());
const sal_Char* pChar = aOStr.getStr();
sal_Int32 nArraySize = aOStr.getLength();
SbxDimArray* pArray = new SbxDimArray(SbxBYTE);
bool bIncIndex = (IsBaseIndexOne() && SbiRuntime::isVBAEnabled() );
if(nArraySize)
{
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 );
pNew->PutByte(*pChar);
pChar++;
pNew->SetFlag( SbxFlagBits::Write );
short aIdx[1];
aIdx[0] = i;
if( bIncIndex )
{
++aIdx[0];
}
pArray->Put(pNew, aIdx);
}
SbxVariableRef refVar = rPar.Get(0);
SbxFlagBits nFlags = refVar->GetFlags();
refVar->ResetFlag( SbxFlagBits::Fixed );
refVar->PutObject( pArray );
refVar->SetFlags( nFlags );
refVar->SetParameters( nullptr );
return;
}
rPar.Get(0)->PutString(aNewStr);
}
void SbRtl_Beep(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 1 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
Sound::Beep();
}
void SbRtl_Load(StarBASIC *, SbxArray & rPar, bool)
{
if( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxBase* pObj = static_cast<SbxObject*>(rPar.Get(1)->GetObject());
if ( pObj )
{
if (SbUserFormModule* pModule = dynamic_cast<SbUserFormModule*>(pObj))
{
pModule->Load();
}
else if (SbxObject* pSbxObj = dynamic_cast<SbxObject*>(pObj))
{
SbxVariable* pVar = pSbxObj->Find("Load", SbxClassType::Method);
if( pVar )
{
pVar->GetInteger();
}
}
}
}
void SbRtl_Unload(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxBase* pObj = static_cast<SbxObject*>(rPar.Get(1)->GetObject());
if ( pObj )
{
if (SbUserFormModule* pFormModule = dynamic_cast<SbUserFormModule*>(pObj))
{
pFormModule->Unload();
}
else if (SbxObject *pSbxObj = dynamic_cast<SbxObject*>(pObj))
{
SbxVariable* pVar = pSbxObj->Find("Unload", SbxClassType::Method);
if( pVar )
{
pVar->GetInteger();
}
}
}
}
void SbRtl_LoadPicture(StarBASIC *, SbxArray & rPar, bool)
{
if( rPar.Count() != 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
OUString aFileURL = getFullPath( rPar.Get(1)->GetOUString() );
std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aFileURL, StreamMode::READ ));
if( pStream )
{
Bitmap aBmp;
ReadDIB(aBmp, *pStream, true);
Graphic aGraphic(aBmp);
SbxObjectRef xRef = new SbStdPicture;
static_cast<SbStdPicture*>(xRef.get())->SetGraphic( aGraphic );
rPar.Get(0)->PutObject( xRef.get() );
}
}
void SbRtl_SavePicture(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if( rPar.Count() != 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxBase* pObj = static_cast<SbxObject*>(rPar.Get(1)->GetObject());
if (SbStdPicture *pPicture = dynamic_cast<SbStdPicture*>(pObj))
{
SvFileStream aOStream( rPar.Get(2)->GetOUString(), StreamMode::WRITE | StreamMode::TRUNC );
Graphic aGraphic = pPicture->GetGraphic();
WriteGraphic( aOStream, aGraphic );
}
}
void SbRtl_MsgBox(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt16 nArgCount = rPar.Count();
if( nArgCount < 2 || nArgCount > 6 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
WinBits nType = 0; // MB_OK
if( nArgCount >= 3 )
nType = static_cast<WinBits>(rPar.Get(2)->GetInteger());
WinBits nStyle = nType;
nStyle &= 15; // delete bits 4-16
if (nStyle > 5)
nStyle = 0;
enum BasicResponse
{
Ok = 1,
Cancel = 2,
Abort = 3,
Retry = 4,
Ignore = 5,
Yes = 6,
No = 7
};
OUString aMsg = rPar.Get(1)->GetOUString();
OUString aTitle;
if( nArgCount >= 4 )
{
aTitle = rPar.Get(3)->GetOUString();
}
else
{
aTitle = Application::GetDisplayName();
}
WinBits nDialogType = nType & (16+32+64);
SolarMutexGuard aSolarGuard;
vcl::Window* pParentWin = Application::GetDefDialogParent();
weld::Widget* pParent = pParentWin ? pParentWin->GetFrameWeld() : nullptr;
VclMessageType eType = VclMessageType::Info;
switch (nDialogType)
{
case 16:
eType = VclMessageType::Error;
break;
case 32:
eType = VclMessageType::Question;
break;
case 48:
eType = VclMessageType::Warning;
break;
case 64:
default:
eType = VclMessageType::Info;
break;
}
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
eType, VclButtonsType::NONE, aMsg));
switch (nStyle)
{
case 0: // MB_OK
default:
xBox->add_button(Button::GetStandardText(StandardButtonType::OK), BasicResponse::Ok);
break;
case 1: // MB_OKCANCEL
xBox->add_button(Button::GetStandardText(StandardButtonType::OK), BasicResponse::Ok);
xBox->add_button(Button::GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
if (nType & 256 || nType & 512)
xBox->set_default_response(BasicResponse::Cancel);
else
xBox->set_default_response(BasicResponse::Ok);
break;
case 2: // MB_ABORTRETRYIGNORE
xBox->add_button(Button::GetStandardText(StandardButtonType::Abort), BasicResponse::Abort);
xBox->add_button(Button::GetStandardText(StandardButtonType::Retry), BasicResponse::Retry);
xBox->add_button(Button::GetStandardText(StandardButtonType::Ignore), BasicResponse::Ignore);
if (nType & 256)
xBox->set_default_response(BasicResponse::Retry);
else if (nType & 512)
xBox->set_default_response(BasicResponse::Ignore);
else
xBox->set_default_response(BasicResponse::Cancel);
break;
case 3: // MB_YESNOCANCEL
xBox->add_button(Button::GetStandardText(StandardButtonType::Yes), BasicResponse::Yes);
xBox->add_button(Button::GetStandardText(StandardButtonType::No), BasicResponse::No);
xBox->add_button(Button::GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
if (nType & 256 || nType & 512)
xBox->set_default_response(BasicResponse::Cancel);
else
xBox->set_default_response(BasicResponse::Yes);
break;
case 4: // MB_YESNO
xBox->add_button(Button::GetStandardText(StandardButtonType::Yes), BasicResponse::Yes);
xBox->add_button(Button::GetStandardText(StandardButtonType::No), BasicResponse::No);
if (nType & 256 || nType & 512)
xBox->set_default_response(BasicResponse::No);
else
xBox->set_default_response(BasicResponse::Yes);
break;
case 5: // MB_RETRYCANCEL
xBox->add_button(Button::GetStandardText(StandardButtonType::Retry), BasicResponse::Retry);
xBox->add_button(Button::GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
if (nType & 256 || nType & 512)
xBox->set_default_response(BasicResponse::Cancel);
else
xBox->set_default_response(BasicResponse::Retry);
break;
}
xBox->set_title(aTitle);
sal_Int16 nRet = xBox->run();
rPar.Get(0)->PutInteger(nRet);
}
void SbRtl_SetAttr(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if ( rPar.Count() == 3 )
{
OUString aStr = rPar.Get(1)->GetOUString();
SbAttributes nFlags = static_cast<SbAttributes>( rPar.Get(2)->GetInteger() );
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
bool bReadOnly = bool(nFlags & SbAttributes::READONLY);
xSFI->setReadOnly( aStr, bReadOnly );
bool bHidden = bool(nFlags & SbAttributes::HIDDEN);
xSFI->setHidden( aStr, bHidden );
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_IO_GENERAL );
}
}
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
void SbRtl_Reset(StarBASIC *, SbxArray &, bool)
{
SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
if (pIO)
{
pIO->CloseAll();
}
}
void SbRtl_DumpAllObjects(StarBASIC * pBasic, SbxArray & rPar, bool)
{
sal_uInt16 nArgCount = rPar.Count();
if( nArgCount < 2 || nArgCount > 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else if( !pBasic )
{
StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
}
else
{
SbxObject* p = pBasic;
while( p->GetParent() )
{
p = p->GetParent();
}
SvFileStream aStrm( rPar.Get( 1 )->GetOUString(),
StreamMode::WRITE | StreamMode::TRUNC );
p->Dump( aStrm, rPar.Get( 2 )->GetBool() );
aStrm.Close();
if( aStrm.GetError() != ERRCODE_NONE )
{
StarBASIC::Error( ERRCODE_BASIC_IO_ERROR );
}
}
}
void SbRtl_FileExists(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() == 2 )
{
OUString aStr = rPar.Get(1)->GetOUString();
bool bExists = false;
if( hasUno() )
{
uno::Reference< ucb::XSimpleFileAccess3 > xSFI = getFileAccess();
if( xSFI.is() )
{
try
{
bExists = xSFI->exists( aStr );
}
catch(const Exception & )
{
StarBASIC::Error( ERRCODE_IO_GENERAL );
}
}
}
else
{
DirectoryItem aItem;
FileBase::RC nRet = DirectoryItem::get( getFullPath( aStr ), aItem );
bExists = (nRet == FileBase::E_None);
}
rPar.Get(0)->PutBool( bExists );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
void SbRtl_Partition(StarBASIC *, SbxArray & rPar, bool)
{
if ( rPar.Count() != 5 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_Int32 nNumber = rPar.Get(1)->GetLong();
sal_Int32 nStart = rPar.Get(2)->GetLong();
sal_Int32 nStop = rPar.Get(3)->GetLong();
sal_Int32 nInterval = rPar.Get(4)->GetLong();
if( nStart < 0 || nStop <= nStart || nInterval < 1 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// the Partition function inserts leading spaces before lowervalue and uppervalue
// so that they both have the same number of characters as the string
// representation of the value (Stop + 1). This ensures that if you use the output
// of the Partition function with several values of Number, the resulting text
// will be handled properly during any subsequent sort operation.
// calculate the maximun number of characters before lowervalue and uppervalue
OUString aBeforeStart = OUString::number( nStart - 1 );
OUString aAfterStop = OUString::number( nStop + 1 );
sal_Int32 nLen1 = aBeforeStart.getLength();
sal_Int32 nLen2 = aAfterStop.getLength();
sal_Int32 nLen = nLen1 >= nLen2 ? nLen1:nLen2;
OUStringBuffer aRetStr( nLen * 2 + 1);
OUString aLowerValue;
OUString aUpperValue;
if( nNumber < nStart )
{
aUpperValue = aBeforeStart;
}
else if( nNumber > nStop )
{
aLowerValue = aAfterStop;
}
else
{
sal_Int32 nLowerValue = nNumber;
sal_Int32 nUpperValue = nLowerValue;
if( nInterval > 1 )
{
nLowerValue = ((( nNumber - nStart ) / nInterval ) * nInterval ) + nStart;
nUpperValue = nLowerValue + nInterval - 1;
}
aLowerValue = OUString::number( nLowerValue );
aUpperValue = OUString::number( nUpperValue );
}
nLen1 = aLowerValue.getLength();
nLen2 = aUpperValue.getLength();
if( nLen > nLen1 )
{
// appending the leading spaces for the lowervalue
for ( sal_Int32 i= (nLen - nLen1) ; i > 0; --i )
{
aRetStr.append(" ");
}
}
aRetStr.append( aLowerValue ).append(":");
if( nLen > nLen2 )
{
// appending the leading spaces for the uppervalue
for ( sal_Int32 i= (nLen - nLen2) ; i > 0; --i )
{
aRetStr.append(" ");
}
}
aRetStr.append( aUpperValue );
rPar.Get(0)->PutString( aRetStr.makeStringAndClear());
}
#endif
static long GetDayDiff( const Date& rDate )
{
Date aRefDate( 1,1,1900 );
long nDiffDays;
if ( aRefDate > rDate )
{
nDiffDays = aRefDate - rDate;
nDiffDays *= -1;
}
else
{
nDiffDays = rDate - aRefDate;
}
nDiffDays += 2; // adjustment VisualBasic: 1.Jan.1900 == 2
return nDiffDays;
}
sal_Int16 implGetDateYear( double aDate )
{
Date aRefDate( 1,1,1900 );
long nDays = static_cast<long>(aDate);
nDays -= 2; // standardize: 1.1.1900 => 0.0
aRefDate.AddDays( nDays );
sal_Int16 nRet = aRefDate.GetYear();
return nRet;
}
bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
bool bUseTwoDigitYear, bool bRollOver, double& rdRet )
{
// XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and
// 30..99 can not be input as they are 2-digit for 2000..2029 and
// 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always
// true). For VBA years > 9999 are invalid.
// For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99
// can not be input as they are 2-digit for 1900..1999, years<0 are
// accepted. If bUseTwoDigitYear==false then all years are accepted, but
// year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01).
#if HAVE_FEATURE_SCRIPTING
if ( (nYear < 0 || 9999 < nYear) && SbiRuntime::isVBAEnabled() )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return false;
}
else if ( nYear < 30 && SbiRuntime::isVBAEnabled() )
{
nYear += 2000;
}
else
#endif
{
if ( 0 <= nYear && nYear < 100 && (bUseTwoDigitYear
#if HAVE_FEATURE_SCRIPTING
|| SbiRuntime::isVBAEnabled()
#endif
) )
{
nYear += 1900;
}
}
sal_Int32 nAddMonths = 0;
sal_Int32 nAddDays = 0;
// Always sanitize values to set date and to use for validity detection.
if (nMonth < 1 || 12 < nMonth)
{
sal_Int16 nM = ((nMonth < 1) ? (12 + (nMonth % 12)) : (nMonth % 12));
nAddMonths = nMonth - nM;
nMonth = nM;
}
// Day 0 would already be normalized during Date::Normalize(), include
// it in negative days, also to detect non-validity. The actual day of
// month is 1+(nDay-1)
if (nDay < 1)
{
nAddDays = nDay - 1;
nDay = 1;
}
else if (nDay > 31)
{
nAddDays = nDay - 31;
nDay = 31;
}
Date aCurDate( nDay, nMonth, nYear );
/* TODO: we could enable the same rollover mechanism for StarBASIC to be
* compatible with VBA (just with our wider supported date range), then
* documentation would need to be adapted. As is, the DateSerial() runtime
* function works as dumb as documented.. (except that the resulting date
* is checked for validity now and not just day<=31 and month<=12).
* If change wanted then simply remove overriding bRollOver here and adapt
* documentation.*/
#if HAVE_FEATURE_SCRIPTING
if (!SbiRuntime::isVBAEnabled())
bRollOver = false;
#endif
if (nYear == 0 || (!bRollOver && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
{
#if HAVE_FEATURE_SCRIPTING
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
#endif
return false;
}
if (bRollOver)
{
aCurDate.Normalize();
if (nAddMonths)
aCurDate.AddMonths( nAddMonths);
if (nAddDays)
aCurDate.AddDays( nAddDays);
}
long nDiffDays = GetDayDiff( aCurDate );
rdRet = static_cast<double>(nDiffDays);
return true;
}
double implTimeSerial( sal_Int16 nHours, sal_Int16 nMinutes, sal_Int16 nSeconds )
{
return
static_cast<double>( nHours * ::tools::Time::secondPerHour +
nMinutes * ::tools::Time::secondPerMinute +
nSeconds)
/
static_cast<double>( ::tools::Time::secondPerDay );
}
bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond,
double& rdRet )
{
double dDate;
if(!implDateSerial(nYear, nMonth, nDay, false/*bUseTwoDigitYear*/, false/*bRollOver*/, dDate))
return false;
rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond);
return true;
}
sal_Int16 implGetMinute( double dDate )
{
double nFrac = dDate - floor( dDate );
nFrac *= 86400.0;
sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds % 3600);
sal_Int16 nMin = nTemp / 60;
return nMin;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */