Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellcode-Bibliothek methods.cxx   Sprache: 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 <vcl/wintypes.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/weld.hxx>
#include <basic/sbx.hxx>
#include <svl/zforlist.hxx>
#include <rtl/character.hxx>
#include <rtl/math.hxx>
#include <tools/urlobj.hxx>
#include <osl/time.h>
#include <unotools/charclass.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <unotools/wincodepage.hxx>
#include <tools/wldcrd.hxx>
#include <i18nlangtag/lang.h>
#include <rtl/string.hxx>
#include <sal/log.hxx>
#include <comphelper/DirectoryHelper.hxx>
#include <comphelper/lok.hxx>

#include <runtime.hxx>
#include <sbunoobj.hxx>
#include <osl/file.hxx>
#include <errobject.hxx>

#include <comphelper/string.hxx>
#include <comphelper/processfactory.hxx>

#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/util/DateTime.hpp>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
#include <com/sun/star/script/XErrorQuery.hpp>
#include <ooo/vba/VbStrConv.hpp>
#include <ooo/vba/VbTriState.hpp>
#include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp>
#include <memory>
#include <random>
#include <string_view>
#include <o3tl/char16_t2wchar_t.hxx>

// include search util
#include <com/sun/star/i18n/Transliteration.hpp>
#include <com/sun/star/util/SearchAlgorithms2.hpp>
#include <i18nutil/searchopt.hxx>
#include <unotools/textsearch.hxx>
#include <svl/numformat.hxx>

#include <date.hxx>
#include <sbstdobj.hxx>
#include <rtlproto.hxx>
#include <image.hxx>
#include <iosys.hxx>
#include "ddectrl.hxx"
#include <sbintern.hxx>
#include <basic/vbahelper.hxx>

#include <vector>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include <sbobjmod.hxx>
#include <sbxmod.hxx>

#ifdef _WIN32
#include <prewin.h>
#include <direct.h>
#include <io.h>
#include <postwin.h>
#else
#include <unistd.h>
#endif

#include <vcl/TypeSerializer.hxx>

using namespace comphelper;
using namespace osl;
using namespace com::sun::star;
using namespace com::sun::star::lang;
using namespace com::sun::star::uno;

static sal_Int32 GetDayDiff(const Date& rDate) { return rDate - Date(1899'12'30); }

#if HAVE_FEATURE_SCRIPTING

static sal_Int32 nanoSecToMilliSec(sal_Int64 nNanoSeconds)
{
    // Rounding nanoseconds to milliseconds precision to avoid comparison inaccuracies
    return o3tl::convert(nNanoSeconds, 1, tools::Time::nanoPerMilli);
}

static void FilterWhiteSpace( OUString& rStr )
{
    if (rStr.isEmpty())
    {
        return;
    }
    OUStringBuffer aRet;

    for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
    {
        sal_Unicode cChar = rStr[i];
        if ((cChar != ' ') && (cChar != '\t') &&
           (cChar != '\n') && (cChar != '\r'))
        {
            aRet.append(cChar);
        }
    }

    rStr = aRet.makeStringAndClear();
}

static const CharClass& GetCharClass()
{
    static CharClass aCharClass( Application::GetSettings().GetLanguageTag() );
    return aCharClass;
}

static bool isFolder( FileStatus::Type aType )
{
    return ( aType == FileStatus::Directory || aType == FileStatus::Volume );
}


//*** UCB file access ***

// Converts possibly relative paths to absolute paths
// according to the setting done by ChDir/ChDrive
OUString getFullPath( const OUString& aRelPath )
{
    OUString aFileURL;

    // #80204 Try first if it already is a valid URL
    INetURLObject aURLObj( aRelPath );
    aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );

    if( aFileURL.isEmpty() )
    {
        File::getFileURLFromSystemPath( aRelPath, aFileURL );
    }

    return aFileURL;
}

// TODO: -> SbiGlobals
static uno::Reference< ucb::XSimpleFileAccess3 > const & getFileAccess()
{
    static uno::Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
    return xSFI;
}


// Properties and methods lie down the return value at the Get (bPut = sal_False) in the
// element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True)

// CreateObject( class )

void SbRtl_CreateObject(StarBASIC * pBasic, SbxArray & rPar, bool)
{
    if( rPar.Count() < 2 )
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    OUString aClass(rPar.Get(1)->GetOUString());
    SbxObjectRef p = SbxBase::CreateObject( aClass );
    if( !p.is() )
        return StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD );

    // Convenience: enter BASIC as parent
    p->SetParent( pBasic );
    rPar.Get(0)->PutObject(p.get());
}

// Error( n )

void SbRtl_Error(StarBASIC * pBasic, SbxArray & rPar, bool)
{
    if( !pBasic )
        return StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );

    OUString aErrorMsg;
    ErrCode nErr = ERRCODE_NONE;
    sal_Int32 nCode = 0;
    if (rPar.Count() == 1)
    {
        nErr = StarBASIC::GetErrBasic();
        aErrorMsg = StarBASIC::GetErrorMsg();
    }
    else
    {
        nCode = rPar.Get(1)->GetLong();
        if( nCode > 65535 )
        {
            StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
        }
        else
        {
            nErr = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nCode) );
        }
    }
    bool bVBA = SbiRuntime::isVBAEnabled();
    OUString tmpErrMsg;
    if( bVBA && !aErrorMsg.isEmpty())
    {
        tmpErrMsg = aErrorMsg;
    }
    else
    {
        StarBASIC::MakeErrorText( nErr, aErrorMsg );
        tmpErrMsg = StarBASIC::GetErrorText();
    }
    // If this rtlfunc 'Error' passed an errcode the same as the active Err Objects's
    // current err then  return the description for the error message if it is set
    // ( complicated isn't it ? )
    if (bVBA && rPar.Count() > 1)
    {
        uno::Reference< ooo::vba::XErrObject > xErrObj( SbxErrObject::getUnoErrObject() );
        if ( xErrObj.is() && xErrObj->getNumber() == nCode && !xErrObj->getDescription().isEmpty() )
        {
            tmpErrMsg = xErrObj->getDescription();
        }
    }
    rPar.Get(0)->PutString(tmpErrMsg);
}

// Sinus

void SbRtl_Sin(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);
    rPar.Get(0)->PutDouble(sin(pArg->GetDouble()));
}


void SbRtl_Cos(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);
    rPar.Get(0)->PutDouble(cos(pArg->GetDouble()));
}


void SbRtl_Atn(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);
    rPar.Get(0)->PutDouble(atan(pArg->GetDouble()));
}


void SbRtl_Abs(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);
    rPar.Get(0)->PutDouble(fabs(pArg->GetDouble()));
}


void SbRtl_Asc(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);
    OUString aStr( pArg->GetOUString() );
    if ( aStr.isEmpty())
    {
        StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
        rPar.Get(0)->PutEmpty();
        return;
    }
    sal_Unicode aCh = aStr[0];
    rPar.Get(0)->PutLong(aCh);
}

static void implChr( SbxArray& rPar, bool bChrW )
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);

    OUString aStr;
    if( !bChrW && SbiRuntime::isVBAEnabled() )
    {
        char c = static_cast<char>(pArg->GetByte());
        aStr = OUString(&c, 1, osl_getThreadTextEncoding());
    }
    else
    {
        // Map negative 16-bit values to large positive ones, so that code like Chr(&H8000)
        // still works after the fix for tdf#62326 changed those four-digit hex notations to
        // produce negative values:
        sal_Int32 aCh = pArg->GetLong();
        if (aCh < -0x8000 || aCh > 0xFFFF)
        {
            StarBASIC::Error(ERRCODE_BASIC_MATH_OVERFLOW);
            aCh = 0;
        }
        aStr = OUString(static_cast<sal_Unicode>(aCh));
    }
    rPar.Get(0)->PutString(aStr);
}

void SbRtl_Chr(StarBASIC *, SbxArray & rPar, bool)
{
    implChr( rPar, false/*bChrW*/ );
}

void SbRtl_ChrW(StarBASIC *, SbxArray & rPar, bool)
{
    implChr( rPar, true/*bChrW*/ );
}

#if defined _WIN32

namespace {

extern "C" void invalidParameterHandler(
    wchar_t const * expression, wchar_t const * function, wchar_t const * file, unsigned int line,
    uintptr_t)
{
    SAL_INFO(
        "basic",
        "invalid parameter during _wgetdcwd; \""
            << (expression ? OUString(o3tl::toU(expression)) : OUString("???"))
            << "\" (" << (function ? OUString(o3tl::toU(function)) : OUString("???")) << ") at "
            << (file ? OUString(o3tl::toU(file)) : OUString("???")) << ":" << line);
}

}

#endif

void SbRtl_CurDir(StarBASIC *, SbxArray & rPar, bool)
{
    // #57064 Although this function doesn't work with DirEntry, it isn't touched
    // by the adjustment to virtual URLs, as, using the DirEntry-functionality,
    // there's no possibility to detect the current one in a way that a virtual URL
    // could be delivered.

    if (rPar.Count() > 2)
       return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

#if defined(_WIN32)
    int nCurDir = 0;  // Current dir // JSM
    if (rPar.Count() == 2)
    {
        OUString aDrive = rPar.Get(1)->GetOUString();
        if ( aDrive.getLength() != 1 )
            return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

        auto c = rtl::toAsciiUpperCase(aDrive[0]);
        if ( !rtl::isAsciiUpperCase( c ) )
            return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

        nCurDir = c - 'A' + 1;
    }
    wchar_t pBuffer[ _MAX_PATH ];
    // _wgetdcwd calls the C runtime's invalid parameter handler (which by default terminates the
    // process) if nCurDir does not correspond to an existing drive, so temporarily set a "harmless"
    // handler:
    auto const handler = _set_thread_local_invalid_parameter_handler(&invalidParameterHandler);
    auto const ok = _wgetdcwd( nCurDir, pBuffer, _MAX_PATH ) != nullptr;
    _set_thread_local_invalid_parameter_handler(handler);
    if ( !ok )
        return StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE );

    rPar.Get(0)->PutString(OUString(o3tl::toU(pBuffer)));

#else

    const int PATH_INCR = 250;

    int nSize = PATH_INCR;
    std::unique_ptr<char[]> pMem;
    whiletrue )
    {
        pMem.reset(new char[nSize]);
        if( !pMem )
            return StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY );

        if( getcwd( pMem.get(), nSize-1 ) != nullptr )
        {
            rPar.Get(0)->PutString(OUString::createFromAscii(pMem.get()));
            return;
        }
        if( errno != ERANGE )
            return StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );

        nSize += PATH_INCR;
    };

#endif
}

void SbRtl_ChDir(StarBASIC * pBasic, SbxArray & rPar, bool)
{
    rPar.Get(0)->PutEmpty();
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    // 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());
    }
}

void SbRtl_ChDrive(StarBASIC *, SbxArray & rPar, bool)
{
    rPar.Get(0)->PutEmpty();
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}


// Implementation of StepRENAME with UCB
void implStepRenameUCB( const OUString& aSource, const OUString& aDest )
{
    const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
    if( !xSFI.is() )
        return;

    try
    {
        OUString aSourceFullPath = getFullPath( aSource );
        if( !xSFI->exists( aSourceFullPath ) )
        {
            StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
            return;
        }

        OUString aDestFullPath = getFullPath( aDest );
        if( xSFI->exists( aDestFullPath ) )
        {
            StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS );
        }
        else
        {
            xSFI->move( aSourceFullPath, aDestFullPath );
        }
    }
    catch(const Exception & )
    {
        StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
    }
}

// Implementation of StepRENAME with OSL
void implStepRenameOSL( const OUString& aSource, const OUString& aDest )
{
    FileBase::RC nRet = File::move( getFullPath( aSource ), getFullPath( aDest ) );
    if( nRet != FileBase::E_None )
    {
        StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
    }
}

void SbRtl_FileCopy(StarBASIC *, SbxArray & rPar, bool)
{
    rPar.Get(0)->PutEmpty();
    if (rPar.Count() != 3)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    OUString aSource = rPar.Get(1)->GetOUString();
    OUString aDest = rPar.Get(2)->GetOUString();
    if( hasUno() )
    {
        const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
        if( xSFI.is() )
        {
            try
            {
                xSFI->copy( getFullPath( aSource ), getFullPath( aDest ) );
            }
            catch(const Exception & )
            {
                StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
            }
        }
    }
    else
    {
        FileBase::RC nRet = File::copy( getFullPath( aSource ), getFullPath( aDest ) );
        if( nRet != FileBase::E_None )
        {
            StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
        }
    }
}

void SbRtl_Kill(StarBASIC *, SbxArray & rPar, bool)
{
    rPar.Get(0)->PutEmpty();
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    OUString aFileSpec = rPar.Get(1)->GetOUString();

    if( hasUno() )
    {
        const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
        if( xSFI.is() )
        {
            OUString aFullPath = getFullPath( aFileSpec );
            if( !xSFI->exists( aFullPath ) || xSFI->isFolder( aFullPath ) )
            {
                StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
                return;
            }
            try
            {
                xSFI->kill( aFullPath );
            }
            catch(const Exception & )
            {
                StarBASIC::Error( ERRCODE_IO_GENERAL );
            }
        }
    }
    else
    {
        File::remove( getFullPath( aFileSpec ) );
    }
}

void SbRtl_MkDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
{
    rPar.Get(0)->PutEmpty();
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    OUString aPath = rPar.Get(1)->GetOUString();
    if ( SbiRuntime::isVBAEnabled() )
    {
        // In vba if the full path is not specified then
        // folder is created relative to the curdir
        INetURLObject aURLObj( getFullPath( aPath ) );
        if ( aURLObj.GetProtocol() != INetProtocol::File )
        {
            SbxArrayRef pPar = new SbxArray();
            SbxVariableRef pResult = new SbxVariable();
            SbxVariableRef pParam = new SbxVariable();
            pPar->Insert(pResult.get(), pPar->Count());
            pPar->Insert(pParam.get(), pPar->Count());
            SbRtl_CurDir( pBasic, *pPar, bWrite );

            OUString sCurPathURL;
            File::getFileURLFromSystemPath(pPar->Get(0)->GetOUString(), sCurPathURL);

            aURLObj.SetURL( sCurPathURL );
            aURLObj.Append( aPath );
            File::getSystemPathFromFileURL(aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri  ),aPath ) ;
        }
    }

    if( hasUno() )
    {
        const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
        if( xSFI.is() )
        {
            try
            {
                xSFI->createFolder( getFullPath( aPath ) );
            }
            catch(const Exception & )
            {
                StarBASIC::Error( ERRCODE_IO_GENERAL );
            }
        }
    }
    else
    {
        Directory::create( getFullPath( aPath ) );
    }
}


static void implRemoveDirRecursive( const OUString& aDirPath )
{
    DirectoryItem aItem;
    FileBase::RC nRet = DirectoryItem::get( aDirPath, aItem );
    bool bExists = (nRet == FileBase::E_None);

    FileStatus aFileStatus( osl_FileStatus_Mask_Type );
    nRet = aItem.getFileStatus( aFileStatus );
    bool bFolder = nRet == FileBase::E_None
        && isFolder( aFileStatus.getFileType() );

    if( !bExists || !bFolder )
    {
        return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
    }

    Directory aDir( aDirPath );
    nRet = aDir.open();
    if( nRet != FileBase::E_None )
    {
        return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
    }
    aDir.close();

    comphelper::DirectoryHelper::deleteDirRecursively(aDirPath);
}


void SbRtl_RmDir(StarBASIC *, SbxArray & rPar, bool)
{
    rPar.Get(0)->PutEmpty();
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    OUString aPath = rPar.Get(1)->GetOUString();
    if( hasUno() )
    {
        const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
        if( xSFI.is() )
        {
            try
            {
                if( !xSFI->isFolder( aPath ) )
                {
                    return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
                }
                SbiInstance* pInst = GetSbData()->pInst;
                bool bCompatibility = ( pInst && pInst->IsCompatibility() );
                if( bCompatibility )
                {
                    Sequence< OUString > aContent = xSFI->getFolderContents( aPath, true );
                    if( aContent.hasElements() )
                    {
                        return StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR );
                    }
                }

                xSFI->kill( getFullPath( aPath ) );
            }
            catch(const Exception & )
            {
                StarBASIC::Error( ERRCODE_IO_GENERAL );
            }
        }
    }
    else
    {
        implRemoveDirRecursive( getFullPath( aPath ) );
    }
}

void SbRtl_SendKeys(StarBASIC *, SbxArray & rPar, bool)
{
    rPar.Get(0)->PutEmpty();
    StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
}

void SbRtl_Exp(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    double aDouble = rPar.Get(1)->GetDouble();
    aDouble = exp( aDouble );
    checkArithmeticOverflow( aDouble );
    rPar.Get(0)->PutDouble(aDouble);
}

void SbRtl_FileLen(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    SbxVariableRef pArg = rPar.Get(1);
    OUString aStr( pArg->GetOUString() );
    sal_Int32 nLen = 0;
    if( hasUno() )
    {
        const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
        if( xSFI.is() )
        {
            try
            {
                nLen = xSFI->getSize( getFullPath( aStr ) );
            }
            catch(const Exception & )
            {
                StarBASIC::Error( ERRCODE_IO_GENERAL );
            }
        }
    }
    else
    {
        DirectoryItem aItem;
        (void)DirectoryItem::get( getFullPath( aStr ), aItem );
        FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
        (void)aItem.getFileStatus( aFileStatus );
        nLen = static_cast<sal_Int32>(aFileStatus.getFileSize());
    }
    rPar.Get(0)->PutLong(nLen);
}



void SbRtl_Hex(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    SbxVariableRef pArg = rPar.Get(1);
    // converting value to unsigned and limit to 2 or 4 byte representation
    sal_uInt32 nVal = pArg->IsInteger() ?
        static_cast<sal_uInt16>(pArg->GetInteger()) :
        static_cast<sal_uInt32>(pArg->GetLong());
    rPar.Get(0)->PutString(OUString::number(nVal, 16).toAsciiUpperCase());
}

void SbRtl_FuncCaller(StarBASIC *, SbxArray & rPar, bool)
{
    if (!SbiRuntime::isVBAEnabled() || GetSbData()->pInst == nullptr || GetSbData()->pInst->pRun == nullptr)
        return StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);

    if ( GetSbData()->pInst->pRun->GetExternalCaller() )
        *rPar.Get(0) = *GetSbData()->pInst->pRun->GetExternalCaller();
    else
    {
        SbxVariableRef pVar = new SbxVariable(SbxVARIANT);
        *rPar.Get(0) = *pVar;
    }
}
// InStr( [start],string,string,[compare] )

void SbRtl_InStr(StarBASIC *, SbxArray & rPar, bool)
{
    const sal_uInt32 nArgCount = rPar.Count() - 1;
    if ( nArgCount < 2 )
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);

    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 aToken = rPar.Get(nFirstStringPos + 1)->GetOUString();

    // #97545 Always find empty string
    if( aToken.isEmpty() )
    {
        nPos = nStartPos;
    }
    else
    {
        const OUString aStr1 = rPar.Get(nFirstStringPos)->GetOUString();
        const sal_Int32 nrStr1Len = aStr1.getLength();
        if (nStartPos > nrStr1Len)
        {
            // Start position is greater than the string being searched
            nPos = 0;
        }
        else
        {
            if( !bTextMode )
            {
                nPos = aStr1.indexOf( aToken, nStartPos - 1 ) + 1;
            }
            else
            {
                // tdf#139840 - case-insensitive operation for non-ASCII characters
                i18nutil::SearchOptions2 aSearchOptions;
                aSearchOptions.searchString = aToken;
                aSearchOptions.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
                aSearchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
                utl::TextSearch textSearch(aSearchOptions);

                sal_Int32 nStart = nStartPos - 1;
                sal_Int32 nEnd = nrStr1Len;
                nPos = textSearch.SearchForward(aStr1, &nStart, &nEnd) ? nStart + 1 : 0;
            }
        }
    }
    rPar.Get(0)->PutLong(nPos);
}


// InstrRev(string1, string2[, start[, compare]])

void SbRtl_InStrRev(StarBASIC *, SbxArray & rPar, bool)
{
    const sal_uInt32 nArgCount = rPar.Count() - 1;
    if ( nArgCount < 2 )
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    const OUString aStr1 = rPar.Get(1)->GetOUString();
    const OUString aToken = rPar.Get(2)->GetOUString();

    sal_Int32 nStartPos = -1;
    if ( nArgCount >= 3 )
    {
        nStartPos = rPar.Get(3)->GetLong();
        if( nStartPos <= 0 && nStartPos != -1 )
        {
            StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
            nStartPos = -1;
        }
    }

    SbiInstance* pInst = GetSbData()->pInst;
    bool bTextMode;
    bool bCompatibility = ( pInst && pInst->IsCompatibility() );
    if( bCompatibility )
    {
        SbiRuntime* pRT = pInst->pRun;
        bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
    }
    else
    {
        bTextMode = true;
    }
    if ( nArgCount == 4 )
    {
        bTextMode = rPar.Get(4)->GetInteger();
    }
    const sal_Int32 nStrLen = aStr1.getLength();
    if( nStartPos == -1 )
    {
        nStartPos = nStrLen;
    }

    sal_Int32 nPos = 0;
    if( nStartPos <= nStrLen )
    {
        sal_Int32 nTokenLen = aToken.getLength();
        if( !nTokenLen )
        {
           // Always find empty string
           nPos = nStartPos;
        }
        else if( nStrLen > 0 )
        {
            if( !bTextMode )
            {
               nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
            }
            else
            {
               // tdf#143332 - case-insensitive operation for non-ASCII characters
               i18nutil::SearchOptions2 aSearchOptions;
               aSearchOptions.searchString = aToken;
               aSearchOptions.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
               aSearchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
               utl::TextSearch textSearch(aSearchOptions);

               sal_Int32 nStart = 0;
               sal_Int32 nEnd = nStartPos;
               nPos = textSearch.SearchBackward(aStr1, &nEnd, &nStart) ? nStart : 0;
            }
        }
    }
    rPar.Get(0)->PutLong(nPos);
}


/*
    Int( 2.8 )  =  2.0
    Int( -2.8 ) = -3.0
    Fix( 2.8 )  =  2.0
    Fix( -2.8 ) = -2.0    <- !!
*/


void SbRtl_Int(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);
    double aDouble= pArg->GetDouble();
    /*
        floor( 2.8 ) =  2.0
        floor( -2.8 ) = -3.0
    */

    aDouble = floor( aDouble );
    rPar.Get(0)->PutDouble(aDouble);
}


void SbRtl_Fix(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);
    double aDouble = pArg->GetDouble();
    if ( aDouble >= 0.0 )
        aDouble = floor( aDouble );
    else
        aDouble = ceil( aDouble );
    rPar.Get(0)->PutDouble(aDouble);
}


void SbRtl_LCase(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    const CharClass& rCharClass = GetCharClass();
    OUString aStr(rPar.Get(1)->GetOUString());
    aStr = rCharClass.lowercase(aStr);
    rPar.Get(0)->PutString(aStr);
}

void SbRtl_Left(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 3)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    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)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    double aArg = rPar.Get(1)->GetDouble();
    if ( aArg <= 0 )
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    double d = log( aArg );
    checkArithmeticOverflow( d );
    rPar.Get(0)->PutDouble(d);
}

void SbRtl_LTrim(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    OUString aStr(comphelper::string::stripStart(rPar.Get(1)->GetOUString(), ' '));
    rPar.Get(0)->PutString(aStr);
}


// Mid( String, nStart, nLength )

void SbRtl_Mid(StarBASIC *, SbxArray & rPar, bool bWrite)
{
    int nArgCount = rPar.Count() - 1;
    if ( nArgCount < 2 )
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }
    // #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 )
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    nStartPos--;
    sal_Int32 nLen = -1;
    bool bWriteNoLenParam = false;
    if ( nArgCount == 3 || bWrite )
    {
        sal_Int32 n = rPar.Get(3)->GetLong();
        if( bWrite && n == -1 )
        {
            bWriteNoLenParam = true;
        }
        nLen = n;
    }
    if ( bWrite )
    {
        sal_Int32 nArgLen = aArgStr.getLength();
        if( nStartPos > nArgLen )
        {
            SbiInstance* pInst = GetSbData()->pInst;
            bool bCompatibility = ( pInst && pInst->IsCompatibility() );
            if( bCompatibility )
            {
                return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
            }
            nStartPos = nArgLen;
        }

        OUString aReplaceStr = rPar.Get(4)->GetOUString();
        sal_Int32 nReplaceStrLen = aReplaceStr.getLength();
        sal_Int32 nReplaceLen;
        if( bWriteNoLenParam )
        {
            nReplaceLen = nArgLen - nStartPos;
        }
        else
        {
            nReplaceLen = nLen;
            if( nReplaceLen < 0 || nReplaceLen > nArgLen - nStartPos )
            {
                nReplaceLen = nArgLen - nStartPos;
            }
        }

        OUStringBuffer aResultStr(aArgStr);
        sal_Int32 nErase = nReplaceLen;
        aResultStr.remove( nStartPos, nErase );
        aResultStr.insert(
            nStartPos, aReplaceStr.getStr(), std::min(nReplaceLen, nReplaceStrLen));

        rPar.Get(1)->PutString(aResultStr.makeStringAndClear());
    }
    else
    {
        OUString aResultStr;
        if (nStartPos > aArgStr.getLength())
        {
            // do nothing
        }
        else if(nArgCount == 2)
        {
            aResultStr = aArgStr.copy( nStartPos);
        }
        else
        {
            if (nLen < 0)
                nLen = 0;
            if(nStartPos + nLen > aArgStr.getLength())
            {
                nLen = aArgStr.getLength() - nStartPos;
            }
            if (nLen > 0)
                aResultStr = aArgStr.copy( nStartPos, nLen );
        }
        rPar.Get(0)->PutString(aResultStr);
    }
}

void SbRtl_Oct(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariableRef pArg = rPar.Get(1);
    // converting value to unsigned and limit to 2 or 4 byte representation
    sal_uInt32 nVal = pArg->IsInteger() ?
        static_cast<sal_uInt16>(pArg->GetInteger()) :
        static_cast<sal_uInt32>(pArg->GetLong());
    rPar.Get(0)->PutString(OUString::number(nVal, 8));
}

// Replace(expression, find, replace[, start[, count[, compare]]])

void SbRtl_Replace(StarBASIC *, SbxArray & rPar, bool)
{
    const sal_uInt32 nArgCount = rPar.Count() - 1;
    if ( nArgCount < 3 || nArgCount > 6 )
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    sal_Int32 lStartPos = 1;
    if (nArgCount >= 4)
    {
        if (rPar.Get(4)->GetType() != SbxEMPTY)
        {
            lStartPos = rPar.Get(4)->GetLong();
        }
        if (lStartPos < 1)
        {
            return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
        }
    }
    --lStartPos; // Make it 0-based

    sal_Int32 lCount = -1;
    if (nArgCount >= 5)
    {
        if (rPar.Get(5)->GetType() != SbxEMPTY)
        {
            lCount = rPar.Get(5)->GetLong();
        }
        if (lCount < -1)
        {
            return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
        }
    }

    bool bCaseInsensitive;
    if (nArgCount == 6)
    {
        bCaseInsensitive = rPar.Get(6)->GetInteger();
    }
    else
    {
        SbiInstance* pInst = GetSbData()->pInst;
        if (pInst && pInst->IsCompatibility())
        {
            SbiRuntime* pRT = pInst->pRun;
            bCaseInsensitive = pRT && pRT->IsImageFlag(SbiImageFlags::COMPARETEXT);
        }
        else
        {
            bCaseInsensitive = true;
        }
    }

    const OUString aExpStr = rPar.Get(1)->GetOUString();
    OUString aFindStr = rPar.Get(2)->GetOUString();
    const OUString aReplaceStr = rPar.Get(3)->GetOUString();

    OUString aSrcStr(aExpStr);
    sal_Int32 nPrevPos = std::min(lStartPos, aSrcStr.getLength());
    css::uno::Sequence<sal_Int32> aOffset;
    if (bCaseInsensitive)
    {
        // tdf#132389: case-insensitive operation for non-ASCII characters
        // tdf#142487: use css::i18n::Transliteration to correctly handle ß -> ss expansion
        // tdf#132388: We can't use utl::TextSearch (css::i18n::XTextSearch), because each call to
        //             css::i18n::XTextSearch::SearchForward transliterates input string, making
        //             performance of repeated calls unacceptable
        auto xTrans = css::i18n::Transliteration::create(comphelper::getProcessComponentContext());
        xTrans->loadModule(css::i18n::TransliterationModules_IGNORE_CASE, {});
        aFindStr = xTrans->transliterate(aFindStr, 0, aFindStr.getLength(), aOffset);
        aSrcStr = xTrans->transliterate(aSrcStr, nPrevPos, aSrcStr.getLength() - nPrevPos, aOffset);
        nPrevPos = std::distance(aOffset.begin(),
                                 std::lower_bound(aOffset.begin(), aOffset.end(), nPrevPos));
    }

    auto getExpStrPos = [aOffset, nExpLen = aExpStr.getLength()](sal_Int32 nSrcStrPos) -> sal_Int32
    {
        assert(!aOffset.hasElements() || aOffset.getLength() >= nSrcStrPos);
        if (!aOffset.hasElements())
            return nSrcStrPos;
        return aOffset.getLength() > nSrcStrPos ? aOffset[nSrcStrPos] : nExpLen;
    };

    // Note: the result starts from lStartPos, removing everything to the left. See i#94895.
    OUStringBuffer sResult(aSrcStr.getLength() - nPrevPos);
    sal_Int32 nCounts = 0;
    while (lCount == -1 || lCount > nCounts)
    {
        sal_Int32 nPos = aSrcStr.indexOf(aFindStr, nPrevPos);
        if (nPos < 0)
            break;

        lStartPos = getExpStrPos(nPrevPos);
        sResult.append(aExpStr.getStr() + lStartPos, getExpStrPos(nPos) - lStartPos);
        sResult.append(aReplaceStr);
        nPrevPos = nPos + aFindStr.getLength();
        nCounts++;
    }
    lStartPos = getExpStrPos(nPrevPos);
    sResult.append(aExpStr.getStr() + lStartPos, aExpStr.getLength() - lStartPos);
    rPar.Get(0)->PutString(sResult.makeStringAndClear());
}

void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 3)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    const OUString aStr = rPar.Get(1)->GetOUString();
    int nResultLen = rPar.Get(2)->GetLong();
    if( nResultLen < 0 )
    {
        nResultLen = 0;
        StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }
    int nStrLen = aStr.getLength();
    if ( nResultLen > nStrLen )
    {
        nResultLen = nStrLen;
    }
    OUString aResultStr = aStr.copy( nStrLen - nResultLen );
    rPar.Get(0)->PutString(aResultStr);
}

void SbRtl_RTL(StarBASIC * pBasic, SbxArray & rPar, bool)
{
    rPar.Get(0)->PutObject(pBasic->getRTL().get());
}

void SbRtl_RTrim(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    OUString aStr(comphelper::string::stripEnd(rPar.Get(1)->GetOUString(), ' '));
    rPar.Get(0)->PutString(aStr);
}

void SbRtl_Sgn(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    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)
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }

    const sal_Int32 nCount = rPar.Get(1)->GetLong();
    OUStringBuffer aBuf(nCount);
    string::padToLength(aBuf, nCount, ' ');
    rPar.Get(0)->PutString(aBuf.makeStringAndClear());
}

void SbRtl_Sqr(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }

    double aDouble = rPar.Get(1)->GetDouble();
    if ( aDouble < 0 )
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }
    rPar.Get(0)->PutDouble(sqrt(aDouble));
}

void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }

    OUString aStr;
    OUString aStrNew;
    SbxVariableRef pArg = rPar.Get(1);
    const SbxDataType argType = pArg->GetType();
    if (argType == SbxSTRING)
    {
        // From Help: "If a string is passed as argument, it is returned without any changes"
        aStr = pArg->GetOUString();
    }
    else
    {
        pArg->Format(aStr);
    }

    // Numbers start with a space
    if (argType != SbxBOOL && argType != SbxSTRING && pArg->IsNumericRTL())
    {
        // replace commas by points so that it's symmetric to Val!
        aStr = aStr.replaceFirst( ",""." );

        SbiInstance* pInst = GetSbData()->pInst;
        bool bCompatibility = ( pInst && pInst->IsCompatibility() );
        if( bCompatibility )
        {
            sal_Int32 nLen = aStr.getLength();

            const sal_Unicode* pBuf = aStr.getStr();

            bool bNeg = ( pBuf[0] == '-' );
            sal_Int32 iZeroSearch = 0;
            if( bNeg )
            {
                aStrNew += "-";
                iZeroSearch++;
            }
            else
            {
                if( pBuf[0] != ' ' )
                {
                    aStrNew += " ";
                }
            }
            sal_Int32 iNext = iZeroSearch + 1;
            if( pBuf[iZeroSearch] == '0' && nLen > iNext && pBuf[iNext] == '.' )
            {
                iZeroSearch += 1;
            }
            aStrNew += aStr.subView(iZeroSearch);
        }
        else
        {
            if (!aStr.startsWith("-"))
                aStrNew = " ";
            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 aStr1 = rPar.Get(1)->GetOUString();
    const OUString aStr2 = 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 )
        {
            const 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( aStr1, aStr2 );
    }
    else
    {
        sal_Int32 aResult;
        aResult = aStr1.compareTo( aStr2 );
        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)
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }

    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 aStr = rPar.Get(2)->GetOUString();
        aFiller = aStr[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)
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }

    const sal_Int32 nCount = std::max(rPar.Get(1)->GetLong(), sal_Int32(0));
    OUStringBuffer aStr(nCount);
    comphelper::string::padToLength(aStr, nCount, '\t');
    rPar.Get(0)->PutString(aStr.makeStringAndClear());
}

void SbRtl_Tan(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }

    SbxVariableRef pArg = rPar.Get(1);
    rPar.Get(0)->PutDouble(tan(pArg->GetDouble()));
}

void SbRtl_UCase(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }

    const CharClass& rCharClass = GetCharClass();
    OUString aStr(rPar.Get(1)->GetOUString());
    aStr = rCharClass.uppercase( aStr );
    rPar.Get(0)->PutString(aStr);
}


void SbRtl_Val(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 2)
    {
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
    }
    double nResult = 0.0;

    OUString aStr(rPar.Get(1)->GetOUString());

    FilterWhiteSpace( aStr );
    if ( aStr.getLength() > 1 && aStr[0] == '&' )
    {
        sal_Unicode aChar = aStr[1];
        if ( aChar == 'h' || aChar == 'H' )
        {
            nResult = static_cast<sal_Int16>(o3tl::toInt64(aStr.subView(2), 16));
        }
        else if ( aChar == 'o' || aChar == 'O' )
        {
            nResult = static_cast<sal_Int16>(o3tl::toInt64(aStr.subView(2), 8));
        }
    }
    else
    {
        rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
        sal_Int32 nParseEnd = 0;
        nResult = ::rtl::math::stringToDouble( aStr, '.'',', &eStatus, &nParseEnd );
        if ( eStatus != rtl_math_ConversionStatus_Ok )
            StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW );
        /* TODO: we should check whether all characters were parsed here,
         * but earlier code silently ignored trailing nonsense such as "1x"
         * resulting in 1 with the side effect that any alpha-only-string
         * like "x" resulted in 0. Not changing that now (2013-03-22) as
         * user macros may rely on it. */

#if 0
        else if ( nParseEnd != aStr.getLength() )
            StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
#endif
    }

    rPar.Get(0)->PutDouble(nResult);
}


// Helper functions for date conversion
sal_Int16 implGetDateDay( double aDate )
{
    aDate = floor( aDate );
    Date aRefDate(1899'12'30);
    aRefDate.AddDays( aDate );

    sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetDay() );
    return nRet;
}

sal_Int16 implGetDateMonth( double aDate )
{
    Date aRefDate(1899'12'30);
    sal_Int32 nDays = static_cast<sal_Int32>(aDate);
    aRefDate.AddDays( nDays );
    sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetMonth() );
    return nRet;
}

css::util::Date SbxDateToUNODate( const SbxValue* const pVal )
{
    double aDate = pVal->GetDate();

    css::util::Date aUnoDate;
    aUnoDate.Day   = implGetDateDay  ( aDate );
    aUnoDate.Month = implGetDateMonth( aDate );
    aUnoDate.Year  = implGetDateYear ( aDate );

    return aUnoDate;
}

void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate)
{
    double dDate;
    if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, SbDateCorrection::None, dDate ) )
    {
        pVal->PutDate( dDate );
    }
}

// Function to convert date to UNO date (com.sun.star.util.Date)
void SbRtl_CDateToUnoDate(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODate(rPar.Get(1))));
}

// Function to convert date from UNO date (com.sun.star.util.Date)
void SbRtl_CDateFromUnoDate(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Date>::get()));
    css::util::Date aUnoDate;
    if(aAny >>= aUnoDate)
        SbxDateFromUNODate(rPar.Get(0), aUnoDate);
    else
        SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
}

css::util::Time SbxDateToUNOTime( const SbxValue* const pVal )
{
    double aDate = pVal->GetDate();

    css::util::Time aUnoTime;
    aUnoTime.Hours       = implGetHour      ( aDate );
    aUnoTime.Minutes     = implGetMinute    ( aDate );
    aUnoTime.Seconds     = implGetSecond    ( aDate );
    aUnoTime.NanoSeconds = implGetNanoSecond( aDate );

    return aUnoTime;
}

void SbxDateFromUNOTime( SbxValue *pVal, const css::util::Time& aUnoTime)
{
    pVal->PutDate(implTimeSerial(aUnoTime.Hours, aUnoTime.Minutes, aUnoTime.Seconds,
                                 nanoSecToMilliSec(aUnoTime.NanoSeconds)));
}

// Function to convert date to UNO time (com.sun.star.util.Time)
void SbRtl_CDateToUnoTime(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    unoToSbxValue(rPar.Get(0), Any(SbxDateToUNOTime(rPar.Get(1))));
}

// Function to convert date from UNO time (com.sun.star.util.Time)
void SbRtl_CDateFromUnoTime(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Time>::get()));
    css::util::Time aUnoTime;
    if(aAny >>= aUnoTime)
        SbxDateFromUNOTime(rPar.Get(0), aUnoTime);
    else
        SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
}

css::util::DateTime SbxDateToUNODateTime( const SbxValue* const pVal )
{
    double aDate = pVal->GetDate();

    css::util::DateTime aUnoDT;
    aUnoDT.Day   = implGetDateDay  ( aDate );
    aUnoDT.Month = implGetDateMonth( aDate );
    aUnoDT.Year  = implGetDateYear ( aDate );
    aUnoDT.Hours       = implGetHour      ( aDate );
    aUnoDT.Minutes     = implGetMinute    ( aDate );
    aUnoDT.Seconds     = implGetSecond    ( aDate );
    aUnoDT.NanoSeconds = implGetNanoSecond( aDate );

    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, nanoSecToMilliSec(aUnoDT.NanoSeconds), dDate))
    {
        pVal->PutDate( dDate );
    }
}

// Function to convert date to UNO date (com.sun.star.util.Date)
void SbRtl_CDateToUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODateTime(rPar.Get(1))));
}

// Function to convert date from UNO date (com.sun.star.util.Date)
void SbRtl_CDateFromUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::DateTime>::get()));
    css::util::DateTime aUnoDT;
    if(aAny >>= aUnoDT)
        SbxDateFromUNODateTime(rPar.Get(0), aUnoDT);
    else
        SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
}

// Function to convert date to ISO 8601 date format YYYYMMDD
void SbRtl_CDateToIso(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
        return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);

    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);
}

// Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD
// And even YYMMDD for compatibility, sigh...
void SbRtl_CDateFromIso(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() == 2)
    {
        do
        {
            OUString aStr = rPar.Get(1)->GetOUString();
            if (aStr.isEmpty())
                break;

            // Valid formats are
            // YYYYMMDD    -YYYMMDD     YYYYYMMDD    -YYYYYMMDD    YYMMDD
            // YYYY-MM-DD  -YYYY-MM-DD  YYYYY-MM-DD  -YYYYY-MM-DD

            sal_Int32 nSign = 1;
            if (aStr[0] == '-')
            {
                nSign = -1;
                aStr = aStr.copy(1);
            }
            const sal_Int32 nLen = aStr.getLength();

            // Signed YYMMDD two digit year is invalid.
            if (nLen == 6 && nSign == -1)
                break;

            // Now valid
            // YYYYMMDD    YYYYYMMDD    YYMMDD
            // YYYY-MM-DD  YYYYY-MM-DD
            if (nLen != 6 && (nLen < 8 || 11 < nLen))
                break;

            bool bUseTwoDigitYear = false;
            std::u16string_view aYearStr, aMonthStr, aDayStr;
            if (nLen == 6 || nLen == 8 || nLen == 9)
            {
                // ((Y)YY)YYMMDD
                if (!comphelper::string::isdigitAsciiString(aStr))
                    break;

                const sal_Int32 nMonthPos = (nLen == 8 ? 4 : (nLen == 6 ? 2 : 5));
                if (nMonthPos == 2)
                    bUseTwoDigitYear = true;
                aYearStr  = aStr.subView( 0, nMonthPos );
                aMonthStr = aStr.subView( nMonthPos, 2 );
                aDayStr   = aStr.subView( nMonthPos + 2, 2 );
            }
            else
            {
                // (Y)YYYY-MM-DD
                const sal_Int32 nMonthSep = (nLen == 11 ? 5 : 4);
                if (aStr.indexOf('-') != nMonthSep)
                    break;
                if (aStr.indexOf('-', nMonthSep + 1) != nMonthSep + 3)
                    break;

                aYearStr  = aStr.subView( 0, nMonthSep );
                aMonthStr = aStr.subView( nMonthSep + 1, 2 );
                aDayStr   = aStr.subView( nMonthSep + 4, 2 );
                if (    !comphelper::string::isdigitAsciiString(aYearStr) ||
                        !comphelper::string::isdigitAsciiString(aMonthStr) ||
                        !comphelper::string::isdigitAsciiString(aDayStr))
                    break;
            }

            double dDate;
            if (!implDateSerial( static_cast<sal_Int16>(nSign * o3tl::toInt32(aYearStr)),
                        static_cast<sal_Int16>(o3tl::toInt32(aMonthStr)), static_cast<sal_Int16>(o3tl::toInt32(aDayStr)),
                        bUseTwoDigitYear, SbDateCorrection::None, dDate ))
                break;

            rPar.Get(0)->PutDate(dDate);

            return;
        }
        while (false);

        SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER );
    }
    else
    {
        StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }
}

void SbRtl_DateSerial(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 4)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }
    sal_Int16 nYear = rPar.Get(1)->GetInteger();
    sal_Int16 nMonth = rPar.Get(2)->GetInteger();
    sal_Int16 nDay = rPar.Get(3)->GetInteger();

    double dDate;
    if( implDateSerial( nYear, nMonth, nDay, true, SbDateCorrection::RollOver, dDate ) )
    {
        rPar.Get(0)->PutDate(dDate);
    }
}

void SbRtl_TimeSerial(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() < 4)
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }
    sal_Int16 nHour = rPar.Get(1)->GetInteger();
    if ( nHour == 24 )
    {
        nHour = 0;                      // because of UNO DateTimes, which go till 24 o'clock
    }
    sal_Int16 nMinute = rPar.Get(2)->GetInteger();
    sal_Int16 nSecond = rPar.Get(3)->GetInteger();
    if ((nHour < 0 || nHour > 23)   ||
        (nMinute < 0 || nMinute > 59 )  ||
        (nSecond < 0 || nSecond > 59 ))
    {
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    }

    rPar.Get(0)->PutDate(implTimeSerial(nHour, nMinute, nSecond, 0)); // 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)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    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)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    sal_Int16 nYear = implGetDateYear(rPar.Get(1)->GetDate());
    rPar.Get(0)->PutInteger(nYear);
}

sal_Int16 implGetHour( double dDate )
{
    double nFrac = (dDate - floor(dDate)) * ::tools::Time::milliSecPerDay;
    sal_uInt64 nMilliSeconds = static_cast<sal_uInt64>(nFrac + 0.5);
    return static_cast<sal_Int16>((nMilliSeconds / ::tools::Time::milliSecPerHour)
                                  % ::tools::Time::hourPerDay);
}

void SbRtl_Hour(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    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)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    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)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    sal_Int16 nMonth = implGetDateMonth(rPar.Get(1)->GetDate());
    rPar.Get(0)->PutInteger(nMonth);
}

sal_Int16 implGetSecond( double dDate )
{
    double nFrac = (dDate - floor(dDate)) * ::tools::Time::milliSecPerDay;
    sal_uInt64 nMilliSeconds = static_cast<sal_uInt64>(nFrac + 0.5);
    return static_cast<sal_Int16>((nMilliSeconds / ::tools::Time::milliSecPerSec)
                                  % ::tools::Time::secondPerMinute);
}

sal_Int32 implGetNanoSecond(double dDate)
{
    double nFrac = (dDate - floor(dDate)) * ::tools::Time::milliSecPerDay;
    sal_uInt64 nMilliSeconds = static_cast<sal_uInt64>(nFrac + 0.5);
    nMilliSeconds %= ::tools::Time::milliSecPerSec;

    return static_cast<sal_Int32>(nMilliSeconds * ::tools::Time ::nanoPerMilli);
}

void SbRtl_Second(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
    double nArg = rPar.Get(1)->GetDate();
    sal_Int16 nSecond = implGetSecond( nArg );
    rPar.Get(0)->PutInteger(nSecond);
}

double Now_Impl()
{
    // tdf#161469 - align implementation with the now function in calc, i.e., include subseconds
    DateTime aActTime(DateTime::SYSTEM);
    return static_cast<double>(GetDayDiff(aActTime))
           + implTimeSerial(aActTime.GetHour(), aActTime.GetMin(), aActTime.GetSec(),
                            nanoSecToMilliSec(aActTime.GetNanoSec()));
}

// Date Now()

void SbRtl_Now(StarBASIC*, SbxArray& rPar, bool)
{
    if (rPar.Count() != 1)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    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);
        if (!pMeth->IsString())
        {
            pMeth->PutDate(aTime.GetTimeInDays());
            return;
        }
        OUString aRes;
        if( pMeth->IsFixed() )
        {
            // Time$: hh:mm:ss
            char buf[ 20 ];
            snprintf( buf, sizeof(buf), "%02d:%02d:%02d",
                      aTime.GetHour(), aTime.GetMin(), aTime.GetSec() );
            aRes = OUString::createFromAscii( buf );
        }
        else
        {
            // Time: system dependent
            tools::Long nSeconds=aTime.GetHour();
            nSeconds *= 3600;
            nSeconds += aTime.GetMin() * 60;
            nSeconds += aTime.GetSec();
            double nDays = static_cast<double>(nSeconds) * ( 1.0 / (24.0*3600.0) );
            const Color* pCol;

            std::shared_ptr<SvNumberFormatter> pFormatter;
            sal_uInt32 nIndex;
            if( GetSbData()->pInst )
            {
                pFormatter = GetSbData()->pInst->GetNumberFormatter();
                nIndex = GetSbData()->pInst->GetStdTimeIdx();
            }
            else
            {
                sal_uInt32 n;   // Dummy
                pFormatter = SbiInstance::PrepareNumberFormatter( n, nIndex, n );
            }

            pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
        }
        pMeth->PutString( aRes );
    }
    else
    {
        StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
    }
}

void SbRtl_Timer(StarBASIC *, SbxArray & rPar, bool)
{
    tools::Time aTime( tools::Time::SYSTEM );
    tools::Long nSeconds = aTime.GetHour();
    nSeconds *= 3600;
    nSeconds += aTime.GetMin() * 60;
    nSeconds += aTime.GetSec();
    rPar.Get(0)->PutDate(static_cast<double>(nSeconds));
}


void SbRtl_Date(StarBASIC *, SbxArray & rPar, bool bWrite)
{
    if ( !bWrite )
    {
        Date aToday( Date::SYSTEM );
        double nDays = static_cast<double>(GetDayDiff( aToday ));
        SbxVariable* pMeth = rPar.Get(0);
        if( pMeth->IsString() )
        {
            OUString aRes;
            const Color* pCol;

            std::shared_ptr<SvNumberFormatter> pFormatter;
            sal_uInt32 nIndex;
            if( GetSbData()->pInst )
            {
                pFormatter = GetSbData()->pInst->GetNumberFormatter();
                nIndex = GetSbData()->pInst->GetStdDateIdx();
            }
            else
            {
                sal_uInt32 n;
                pFormatter = SbiInstance::PrepareNumberFormatter( nIndex, n, n );
            }

            pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
            pMeth->PutString( aRes );
        }
        else
        {
            pMeth->PutDate( nDays );
        }
    }
    else
    {
        StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
    }
}

void SbRtl_IsArray(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    rPar.Get(0)->PutBool((rPar.Get(1)->GetType() & SbxARRAY) != 0);
}

void SbRtl_IsObject(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    SbxVariable* pVar = rPar.Get(1);
    bool bObject = pVar->IsObject();
    SbxBase* pObj = (bObject ? pVar->GetObject() : nullptr);

    if( auto pUnoClass = dynamic_cast<SbUnoClass*>( pObj) )
    {
        bObject = pUnoClass->getUnoClass().is();
    }
    rPar.Get(0)->PutBool(bObject);
}

void SbRtl_IsDate(StarBASIC *, SbxArray & rPar, bool)
{
    if (rPar.Count() != 2)
        return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );

    // #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();

--> --------------------

--> maximum size reached

--> --------------------

99%


¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.111Bemerkung:  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge