1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <tools/date.hxx>
23 #include <basic/sbxvar.hxx>
24 #include <basic/sbuno.hxx>
25 #include <osl/process.h>
26 #include <vcl/dibtools.hxx>
27 #include <vcl/window.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/sound.hxx>
31 #include <tools/wintypes.hxx>
32 #include <vcl/stdtext.hxx>
33 #include <vcl/weld.hxx>
34 #include <basic/sbx.hxx>
35 #include <svl/zforlist.hxx>
36 #include <rtl/character.hxx>
37 #include <rtl/math.hxx>
38 #include <tools/urlobj.hxx>
40 #include <unotools/charclass.hxx>
41 #include <unotools/ucbstreamhelper.hxx>
42 #include <unotools/wincodepage.hxx>
43 #include <tools/wldcrd.hxx>
44 #include <i18nlangtag/lang.h>
45 #include <rtl/string.hxx>
46 #include <sal/log.hxx>
47 #include <comphelper/DirectoryHelper.hxx>
49 #include <runtime.hxx>
50 #include <sbunoobj.hxx>
51 #include <osl/file.hxx>
52 #include <errobject.hxx>
54 #include <comphelper/string.hxx>
55 #include <comphelper/processfactory.hxx>
57 #include <com/sun/star/uno/Sequence.hxx>
58 #include <com/sun/star/util/DateTime.hpp>
59 #include <com/sun/star/lang/Locale.hpp>
60 #include <com/sun/star/lang/XServiceInfo.hpp>
61 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
62 #include <com/sun/star/script/XErrorQuery.hpp>
63 #include <ooo/vba/VbTriState.hpp>
64 #include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp>
67 #include <string_view>
68 #include <o3tl/char16_t2wchar_t.hxx>
70 // include search util
71 #include <com/sun/star/i18n/Transliteration.hpp>
72 #include <com/sun/star/util/SearchAlgorithms2.hpp>
73 #include <i18nutil/searchopt.hxx>
74 #include <unotools/textsearch.hxx>
75 #include <svl/numformat.hxx>
79 using namespace comphelper
;
81 using namespace com::sun::star
;
82 using namespace com::sun::star::lang
;
83 using namespace com::sun::star::uno
;
86 #include <sbstdobj.hxx>
87 #include <rtlproto.hxx>
90 #include "ddectrl.hxx"
91 #include <sbintern.hxx>
92 #include <basic/vbahelper.hxx>
100 #include <sbobjmod.hxx>
101 #include <sbxmod.hxx>
112 #include <com/sun/star/i18n/XCharacterClassification.hpp>
113 #include <vcl/unohelp.hxx>
114 #include <vcl/TypeSerializer.hxx>
116 #if HAVE_FEATURE_SCRIPTING
118 static void FilterWhiteSpace( OUString
& rStr
)
126 for (sal_Int32 i
= 0; i
< rStr
.getLength(); ++i
)
128 sal_Unicode cChar
= rStr
[i
];
129 if ((cChar
!= ' ') && (cChar
!= '\t') &&
130 (cChar
!= '\n') && (cChar
!= '\r'))
136 rStr
= aRet
.makeStringAndClear();
139 static tools::Long
GetDayDiff( const Date
& rDate
);
141 static const CharClass
& GetCharClass()
143 static CharClass
aCharClass( Application::GetSettings().GetLanguageTag() );
147 static bool isFolder( FileStatus::Type aType
)
149 return ( aType
== FileStatus::Directory
|| aType
== FileStatus::Volume
);
153 //*** UCB file access ***
155 // Converts possibly relative paths to absolute paths
156 // according to the setting done by ChDir/ChDrive
157 OUString
getFullPath( const OUString
& aRelPath
)
161 // #80204 Try first if it already is a valid URL
162 INetURLObject
aURLObj( aRelPath
);
163 aFileURL
= aURLObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
165 if( aFileURL
.isEmpty() )
167 File::getFileURLFromSystemPath( aRelPath
, aFileURL
);
173 // TODO: -> SbiGlobals
174 static uno::Reference
< ucb::XSimpleFileAccess3
> const & getFileAccess()
176 static uno::Reference
< ucb::XSimpleFileAccess3
> xSFI
= ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
181 // Properties and methods lie down the return value at the Get (bPut = sal_False) in the
182 // element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True)
184 // CreateObject( class )
186 void SbRtl_CreateObject(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
188 OUString
aClass(rPar
.Get(1)->GetOUString());
189 SbxObjectRef p
= SbxBase::CreateObject( aClass
);
191 StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD
);
194 // Convenience: enter BASIC as parent
195 p
->SetParent( pBasic
);
196 rPar
.Get(0)->PutObject(p
.get());
202 void SbRtl_Error(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
205 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
209 ErrCode nErr
= ERRCODE_NONE
;
211 if (rPar
.Count() == 1)
213 nErr
= StarBASIC::GetErrBasic();
214 aErrorMsg
= StarBASIC::GetErrorMsg();
218 nCode
= rPar
.Get(1)->GetLong();
221 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
225 nErr
= StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16
>(nCode
) );
229 bool bVBA
= SbiRuntime::isVBAEnabled();
231 if( bVBA
&& !aErrorMsg
.isEmpty())
233 tmpErrMsg
= aErrorMsg
;
237 StarBASIC::MakeErrorText( nErr
, aErrorMsg
);
238 tmpErrMsg
= StarBASIC::GetErrorText();
240 // If this rtlfunc 'Error' passed an errcode the same as the active Err Objects's
241 // current err then return the description for the error message if it is set
242 // ( complicated isn't it ? )
243 if (bVBA
&& rPar
.Count() > 1)
245 uno::Reference
< ooo::vba::XErrObject
> xErrObj( SbxErrObject::getUnoErrObject() );
246 if ( xErrObj
.is() && xErrObj
->getNumber() == nCode
&& !xErrObj
->getDescription().isEmpty() )
248 tmpErrMsg
= xErrObj
->getDescription();
251 rPar
.Get(0)->PutString(tmpErrMsg
);
257 void SbRtl_Sin(StarBASIC
*, SbxArray
& rPar
, bool)
259 if (rPar
.Count() < 2)
260 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
263 SbxVariableRef pArg
= rPar
.Get(1);
264 rPar
.Get(0)->PutDouble(sin(pArg
->GetDouble()));
269 void SbRtl_Cos(StarBASIC
*, SbxArray
& rPar
, bool)
271 if (rPar
.Count() < 2)
272 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
275 SbxVariableRef pArg
= rPar
.Get(1);
276 rPar
.Get(0)->PutDouble(cos(pArg
->GetDouble()));
281 void SbRtl_Atn(StarBASIC
*, SbxArray
& rPar
, bool)
283 if (rPar
.Count() < 2)
284 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
287 SbxVariableRef pArg
= rPar
.Get(1);
288 rPar
.Get(0)->PutDouble(atan(pArg
->GetDouble()));
293 void SbRtl_Abs(StarBASIC
*, SbxArray
& rPar
, bool)
295 if (rPar
.Count() < 2)
297 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
301 SbxVariableRef pArg
= rPar
.Get(1);
302 rPar
.Get(0)->PutDouble(fabs(pArg
->GetDouble()));
307 void SbRtl_Asc(StarBASIC
*, SbxArray
& rPar
, bool)
309 if (rPar
.Count() < 2)
311 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
315 SbxVariableRef pArg
= rPar
.Get(1);
316 OUString
aStr( pArg
->GetOUString() );
319 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
320 rPar
.Get(0)->PutEmpty();
324 sal_Unicode aCh
= aStr
[0];
325 rPar
.Get(0)->PutLong(aCh
);
330 static void implChr( SbxArray
& rPar
, bool bChrW
)
332 if (rPar
.Count() < 2)
334 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
338 SbxVariableRef pArg
= rPar
.Get(1);
341 if( !bChrW
&& SbiRuntime::isVBAEnabled() )
343 char c
= static_cast<char>(pArg
->GetByte());
344 aStr
= OUString(&c
, 1, osl_getThreadTextEncoding());
348 // Map negative 16-bit values to large positive ones, so that code like Chr(&H8000)
349 // still works after the fix for tdf#62326 changed those four-digit hex notations to
350 // produce negative values:
351 sal_Int32 aCh
= pArg
->GetLong();
352 if (aCh
< -0x8000 || aCh
> 0xFFFF) {
353 StarBASIC::Error(ERRCODE_BASIC_MATH_OVERFLOW
);
356 aStr
= OUString(static_cast<sal_Unicode
>(aCh
));
358 rPar
.Get(0)->PutString(aStr
);
362 void SbRtl_Chr(StarBASIC
*, SbxArray
& rPar
, bool)
364 implChr( rPar
, false/*bChrW*/ );
367 void SbRtl_ChrW(StarBASIC
*, SbxArray
& rPar
, bool)
369 implChr( rPar
, true/*bChrW*/ );
376 extern "C" void invalidParameterHandler(
377 wchar_t const * expression
, wchar_t const * function
, wchar_t const * file
, unsigned int line
,
382 "invalid parameter during _wgetdcwd; \""
383 << (expression
? OUString(o3tl::toU(expression
)) : OUString("???"))
384 << "\" (" << (function
? OUString(o3tl::toU(function
)) : OUString("???")) << ") at "
385 << (file
? OUString(o3tl::toU(file
)) : OUString("???")) << ":" << line
);
392 void SbRtl_CurDir(StarBASIC
*, SbxArray
& rPar
, bool)
394 // #57064 Although this function doesn't work with DirEntry, it isn't touched
395 // by the adjustment to virtual URLs, as, using the DirEntry-functionality,
396 // there's no possibility to detect the current one in a way that a virtual URL
397 // could be delivered.
400 int nCurDir
= 0; // Current dir // JSM
401 if (rPar
.Count() == 2)
403 OUString aDrive
= rPar
.Get(1)->GetOUString();
404 if ( aDrive
.getLength() != 1 )
406 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
409 auto c
= rtl::toAsciiUpperCase(aDrive
[0]);
410 if ( !rtl::isAsciiUpperCase( c
) )
412 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
415 nCurDir
= c
- 'A' + 1;
417 wchar_t pBuffer
[ _MAX_PATH
];
418 // _wgetdcwd calls the C runtime's invalid parameter handler (which by default terminates the
419 // process) if nCurDir does not correspond to an existing drive, so temporarily set a "harmless"
421 auto const handler
= _set_thread_local_invalid_parameter_handler(&invalidParameterHandler
);
422 auto const ok
= _wgetdcwd( nCurDir
, pBuffer
, _MAX_PATH
) != nullptr;
423 _set_thread_local_invalid_parameter_handler(handler
);
426 rPar
.Get(0)->PutString(OUString(o3tl::toU(pBuffer
)));
430 StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE
);
435 const int PATH_INCR
= 250;
437 int nSize
= PATH_INCR
;
438 std::unique_ptr
<char[]> pMem
;
441 pMem
.reset(new char[nSize
]);
444 StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY
);
447 if( getcwd( pMem
.get(), nSize
-1 ) != nullptr )
449 rPar
.Get(0)->PutString(OUString::createFromAscii(pMem
.get()));
452 if( errno
!= ERANGE
)
454 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
463 void SbRtl_ChDir(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
465 rPar
.Get(0)->PutEmpty();
466 if (rPar
.Count() == 2)
468 // VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.)
469 if( SbiRuntime::isVBAEnabled() )
471 ::basic::vba::registerCurrentDirectory(getDocumentModel(pBasic
),
472 rPar
.Get(1)->GetOUString());
477 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
481 void SbRtl_ChDrive(StarBASIC
*, SbxArray
& rPar
, bool)
483 rPar
.Get(0)->PutEmpty();
484 if (rPar
.Count() != 2)
486 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
491 // Implementation of StepRENAME with UCB
492 void implStepRenameUCB( const OUString
& aSource
, const OUString
& aDest
)
494 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
500 OUString aSourceFullPath
= getFullPath( aSource
);
501 if( !xSFI
->exists( aSourceFullPath
) )
503 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
507 OUString aDestFullPath
= getFullPath( aDest
);
508 if( xSFI
->exists( aDestFullPath
) )
510 StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS
);
514 xSFI
->move( aSourceFullPath
, aDestFullPath
);
517 catch(const Exception
& )
519 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
523 // Implementation of StepRENAME with OSL
524 void implStepRenameOSL( const OUString
& aSource
, const OUString
& aDest
)
526 FileBase::RC nRet
= File::move( getFullPath( aSource
), getFullPath( aDest
) );
527 if( nRet
!= FileBase::E_None
)
529 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
533 void SbRtl_FileCopy(StarBASIC
*, SbxArray
& rPar
, bool)
535 rPar
.Get(0)->PutEmpty();
536 if (rPar
.Count() == 3)
538 OUString aSource
= rPar
.Get(1)->GetOUString();
539 OUString aDest
= rPar
.Get(2)->GetOUString();
542 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
547 xSFI
->copy( getFullPath( aSource
), getFullPath( aDest
) );
549 catch(const Exception
& )
551 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
557 FileBase::RC nRet
= File::copy( getFullPath( aSource
), getFullPath( aDest
) );
558 if( nRet
!= FileBase::E_None
)
560 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
565 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
568 void SbRtl_Kill(StarBASIC
*, SbxArray
& rPar
, bool)
570 rPar
.Get(0)->PutEmpty();
571 if (rPar
.Count() == 2)
573 OUString aFileSpec
= rPar
.Get(1)->GetOUString();
577 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
580 OUString aFullPath
= getFullPath( aFileSpec
);
581 if( !xSFI
->exists( aFullPath
) || xSFI
->isFolder( aFullPath
) )
583 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
588 xSFI
->kill( aFullPath
);
590 catch(const Exception
& )
592 StarBASIC::Error( ERRCODE_IO_GENERAL
);
598 File::remove( getFullPath( aFileSpec
) );
603 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
607 void SbRtl_MkDir(StarBASIC
* pBasic
, SbxArray
& rPar
, bool bWrite
)
609 rPar
.Get(0)->PutEmpty();
610 if (rPar
.Count() == 2)
612 OUString aPath
= rPar
.Get(1)->GetOUString();
613 if ( SbiRuntime::isVBAEnabled() )
615 // In vba if the full path is not specified then
616 // folder is created relative to the curdir
617 INetURLObject
aURLObj( getFullPath( aPath
) );
618 if ( aURLObj
.GetProtocol() != INetProtocol::File
)
620 SbxArrayRef pPar
= new SbxArray();
621 SbxVariableRef pResult
= new SbxVariable();
622 SbxVariableRef pParam
= new SbxVariable();
623 pPar
->Insert(pResult
.get(), pPar
->Count());
624 pPar
->Insert(pParam
.get(), pPar
->Count());
625 SbRtl_CurDir( pBasic
, *pPar
, bWrite
);
627 OUString sCurPathURL
;
628 File::getFileURLFromSystemPath(pPar
->Get(0)->GetOUString(), sCurPathURL
);
630 aURLObj
.SetURL( sCurPathURL
);
631 aURLObj
.Append( aPath
);
632 File::getSystemPathFromFileURL(aURLObj
.GetMainURL( INetURLObject::DecodeMechanism::ToIUri
),aPath
) ;
638 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
643 xSFI
->createFolder( getFullPath( aPath
) );
645 catch(const Exception
& )
647 StarBASIC::Error( ERRCODE_IO_GENERAL
);
653 Directory::create( getFullPath( aPath
) );
658 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
663 static void implRemoveDirRecursive( const OUString
& aDirPath
)
666 FileBase::RC nRet
= DirectoryItem::get( aDirPath
, aItem
);
667 bool bExists
= (nRet
== FileBase::E_None
);
669 FileStatus
aFileStatus( osl_FileStatus_Mask_Type
);
670 nRet
= aItem
.getFileStatus( aFileStatus
);
671 bool bFolder
= nRet
== FileBase::E_None
672 && isFolder( aFileStatus
.getFileType() );
674 if( !bExists
|| !bFolder
)
676 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
680 Directory
aDir( aDirPath
);
682 if( nRet
!= FileBase::E_None
)
684 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
689 comphelper::DirectoryHelper::deleteDirRecursively(aDirPath
);
693 void SbRtl_RmDir(StarBASIC
*, SbxArray
& rPar
, bool)
695 rPar
.Get(0)->PutEmpty();
696 if (rPar
.Count() == 2)
698 OUString aPath
= rPar
.Get(1)->GetOUString();
701 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
706 if( !xSFI
->isFolder( aPath
) )
708 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
711 SbiInstance
* pInst
= GetSbData()->pInst
;
712 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
715 Sequence
< OUString
> aContent
= xSFI
->getFolderContents( aPath
, true );
716 if( aContent
.hasElements() )
718 StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR
);
723 xSFI
->kill( getFullPath( aPath
) );
725 catch(const Exception
& )
727 StarBASIC::Error( ERRCODE_IO_GENERAL
);
733 implRemoveDirRecursive( getFullPath( aPath
) );
738 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
742 void SbRtl_SendKeys(StarBASIC
*, SbxArray
& rPar
, bool)
744 rPar
.Get(0)->PutEmpty();
745 StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED
);
748 void SbRtl_Exp(StarBASIC
*, SbxArray
& rPar
, bool)
750 if (rPar
.Count() < 2)
751 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
754 double aDouble
= rPar
.Get(1)->GetDouble();
755 aDouble
= exp( aDouble
);
756 checkArithmeticOverflow( aDouble
);
757 rPar
.Get(0)->PutDouble(aDouble
);
761 void SbRtl_FileLen(StarBASIC
*, SbxArray
& rPar
, bool)
763 if (rPar
.Count() < 2)
765 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
769 SbxVariableRef pArg
= rPar
.Get(1);
770 OUString
aStr( pArg
->GetOUString() );
774 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
779 nLen
= xSFI
->getSize( getFullPath( aStr
) );
781 catch(const Exception
& )
783 StarBASIC::Error( ERRCODE_IO_GENERAL
);
790 (void)DirectoryItem::get( getFullPath( aStr
), aItem
);
791 FileStatus
aFileStatus( osl_FileStatus_Mask_FileSize
);
792 (void)aItem
.getFileStatus( aFileStatus
);
793 nLen
= static_cast<sal_Int32
>(aFileStatus
.getFileSize());
795 rPar
.Get(0)->PutLong(static_cast<tools::Long
>(nLen
));
800 void SbRtl_Hex(StarBASIC
*, SbxArray
& rPar
, bool)
802 if (rPar
.Count() < 2)
804 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
808 SbxVariableRef pArg
= rPar
.Get(1);
809 // converting value to unsigned and limit to 2 or 4 byte representation
810 sal_uInt32 nVal
= pArg
->IsInteger() ?
811 static_cast<sal_uInt16
>(pArg
->GetInteger()) :
812 static_cast<sal_uInt32
>(pArg
->GetLong());
813 OUString
aStr(OUString::number( nVal
, 16 ));
814 aStr
= aStr
.toAsciiUpperCase();
815 rPar
.Get(0)->PutString(aStr
);
819 void SbRtl_FuncCaller(StarBASIC
*, SbxArray
& rPar
, bool)
821 if ( SbiRuntime::isVBAEnabled() && GetSbData()->pInst
&& GetSbData()->pInst
->pRun
)
823 if ( GetSbData()->pInst
->pRun
->GetExternalCaller() )
824 *rPar
.Get(0) = *GetSbData()->pInst
->pRun
->GetExternalCaller();
827 SbxVariableRef pVar
= new SbxVariable(SbxVARIANT
);
828 *rPar
.Get(0) = *pVar
;
833 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED
);
837 // InStr( [start],string,string,[compare] )
839 void SbRtl_InStr(StarBASIC
*, SbxArray
& rPar
, bool)
841 const sal_uInt32 nArgCount
= rPar
.Count() - 1;
843 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
846 sal_Int32 nStartPos
= 1;
847 sal_Int32 nFirstStringPos
= 1;
849 if ( nArgCount
>= 3 )
851 nStartPos
= rPar
.Get(1)->GetLong();
854 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
860 SbiInstance
* pInst
= GetSbData()->pInst
;
862 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
865 SbiRuntime
* pRT
= pInst
->pRun
;
866 bTextMode
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
872 if ( nArgCount
== 4 )
874 bTextMode
= rPar
.Get(4)->GetInteger();
877 const OUString
& rToken
= rPar
.Get(nFirstStringPos
+ 1)->GetOUString();
879 // #97545 Always find empty string
880 if( rToken
.isEmpty() )
886 const OUString
& rStr1
= rPar
.Get(nFirstStringPos
)->GetOUString();
887 const sal_Int32 nrStr1Len
= rStr1
.getLength();
888 if (nStartPos
> nrStr1Len
)
890 // Start position is greater than the string being searched
897 nPos
= rStr1
.indexOf( rToken
, nStartPos
- 1 ) + 1;
901 // tdf#139840 - case-insensitive operation for non-ASCII characters
902 i18nutil::SearchOptions2 aSearchOptions
;
903 aSearchOptions
.searchString
= rToken
;
904 aSearchOptions
.AlgorithmType2
= util::SearchAlgorithms2::ABSOLUTE
;
905 aSearchOptions
.transliterateFlags
|= TransliterationFlags::IGNORE_CASE
;
906 utl::TextSearch
textSearch(aSearchOptions
);
908 sal_Int32 nStart
= nStartPos
- 1;
909 sal_Int32 nEnd
= nrStr1Len
;
910 nPos
= textSearch
.SearchForward(rStr1
, &nStart
, &nEnd
) ? nStart
+ 1 : 0;
914 rPar
.Get(0)->PutLong(nPos
);
919 // InstrRev(string1, string2[, start[, compare]])
921 void SbRtl_InStrRev(StarBASIC
*, SbxArray
& rPar
, bool)
923 const sal_uInt32 nArgCount
= rPar
.Count() - 1;
926 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
930 const OUString aStr1
= rPar
.Get(1)->GetOUString();
931 const OUString aToken
= rPar
.Get(2)->GetOUString();
933 sal_Int32 nStartPos
= -1;
934 if ( nArgCount
>= 3 )
936 nStartPos
= rPar
.Get(3)->GetLong();
937 if( nStartPos
<= 0 && nStartPos
!= -1 )
939 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
944 SbiInstance
* pInst
= GetSbData()->pInst
;
946 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
949 SbiRuntime
* pRT
= pInst
->pRun
;
950 bTextMode
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
956 if ( nArgCount
== 4 )
958 bTextMode
= rPar
.Get(4)->GetInteger();
960 const sal_Int32 nStrLen
= aStr1
.getLength();
961 if( nStartPos
== -1 )
967 if( nStartPos
<= nStrLen
)
969 sal_Int32 nTokenLen
= aToken
.getLength();
972 // Always find empty string
975 else if( nStrLen
> 0 )
979 nPos
= aStr1
.lastIndexOf( aToken
, nStartPos
) + 1;
983 // tdf#143332 - case-insensitive operation for non-ASCII characters
984 i18nutil::SearchOptions2 aSearchOptions
;
985 aSearchOptions
.searchString
= aToken
;
986 aSearchOptions
.AlgorithmType2
= util::SearchAlgorithms2::ABSOLUTE
;
987 aSearchOptions
.transliterateFlags
|= TransliterationFlags::IGNORE_CASE
;
988 utl::TextSearch
textSearch(aSearchOptions
);
990 sal_Int32 nStart
= 0;
991 sal_Int32 nEnd
= nStartPos
;
992 nPos
= textSearch
.SearchBackward(aStr1
, &nEnd
, &nStart
) ? nStart
: 0;
996 rPar
.Get(0)->PutLong(nPos
);
1005 Fix( -2.8 ) = -2.0 <- !!
1008 void SbRtl_Int(StarBASIC
*, SbxArray
& rPar
, bool)
1010 if (rPar
.Count() < 2)
1011 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1014 SbxVariableRef pArg
= rPar
.Get(1);
1015 double aDouble
= pArg
->GetDouble();
1018 floor( -2.8 ) = -3.0
1020 aDouble
= floor( aDouble
);
1021 rPar
.Get(0)->PutDouble(aDouble
);
1026 void SbRtl_Fix(StarBASIC
*, SbxArray
& rPar
, bool)
1028 if (rPar
.Count() < 2)
1029 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1032 SbxVariableRef pArg
= rPar
.Get(1);
1033 double aDouble
= pArg
->GetDouble();
1034 if ( aDouble
>= 0.0 )
1035 aDouble
= floor( aDouble
);
1037 aDouble
= ceil( aDouble
);
1038 rPar
.Get(0)->PutDouble(aDouble
);
1043 void SbRtl_LCase(StarBASIC
*, SbxArray
& rPar
, bool)
1045 if (rPar
.Count() < 2)
1047 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1051 const CharClass
& rCharClass
= GetCharClass();
1052 OUString
aStr(rPar
.Get(1)->GetOUString());
1053 aStr
= rCharClass
.lowercase(aStr
);
1054 rPar
.Get(0)->PutString(aStr
);
1058 void SbRtl_Left(StarBASIC
*, SbxArray
& rPar
, bool)
1060 if (rPar
.Count() < 3)
1062 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1066 OUString
aStr(rPar
.Get(1)->GetOUString());
1067 sal_Int32 nResultLen
= rPar
.Get(2)->GetLong();
1068 if( nResultLen
< 0 )
1071 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1073 else if(nResultLen
> aStr
.getLength())
1075 nResultLen
= aStr
.getLength();
1077 aStr
= aStr
.copy(0, nResultLen
);
1078 rPar
.Get(0)->PutString(aStr
);
1082 void SbRtl_Log(StarBASIC
*, SbxArray
& rPar
, bool)
1084 if (rPar
.Count() < 2)
1086 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1090 double aArg
= rPar
.Get(1)->GetDouble();
1093 double d
= log( aArg
);
1094 checkArithmeticOverflow( d
);
1095 rPar
.Get(0)->PutDouble(d
);
1099 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1104 void SbRtl_LTrim(StarBASIC
*, SbxArray
& rPar
, bool)
1106 if (rPar
.Count() < 2)
1108 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1112 OUString
aStr(comphelper::string::stripStart(rPar
.Get(1)->GetOUString(), ' '));
1113 rPar
.Get(0)->PutString(aStr
);
1118 // Mid( String, nStart, nLength )
1120 void SbRtl_Mid(StarBASIC
*, SbxArray
& rPar
, bool bWrite
)
1122 int nArgCount
= rPar
.Count() - 1;
1123 if ( nArgCount
< 2 )
1125 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1129 // #23178: replicate the functionality of Mid$ as a command
1130 // by adding a replacement-string as a fourth parameter.
1131 // In contrast to the original the third parameter (nLength)
1132 // can't be left out here. That's considered in bWrite already.
1133 if( nArgCount
== 4 )
1137 OUString aArgStr
= rPar
.Get(1)->GetOUString();
1138 sal_Int32 nStartPos
= rPar
.Get(2)->GetLong();
1139 if ( nStartPos
< 1 )
1141 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1146 sal_Int32 nLen
= -1;
1147 bool bWriteNoLenParam
= false;
1148 if ( nArgCount
== 3 || bWrite
)
1150 sal_Int32 n
= rPar
.Get(3)->GetLong();
1151 if( bWrite
&& n
== -1 )
1153 bWriteNoLenParam
= true;
1159 sal_Int32 nArgLen
= aArgStr
.getLength();
1160 if( nStartPos
> nArgLen
)
1162 SbiInstance
* pInst
= GetSbData()->pInst
;
1163 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
1164 if( bCompatibility
)
1166 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1169 nStartPos
= nArgLen
;
1172 OUString aReplaceStr
= rPar
.Get(4)->GetOUString();
1173 sal_Int32 nReplaceStrLen
= aReplaceStr
.getLength();
1174 sal_Int32 nReplaceLen
;
1175 if( bWriteNoLenParam
)
1177 nReplaceLen
= nArgLen
- nStartPos
;
1182 if( nReplaceLen
< 0 || nReplaceLen
> nArgLen
- nStartPos
)
1184 nReplaceLen
= nArgLen
- nStartPos
;
1188 OUStringBuffer
aResultStr(aArgStr
);
1189 sal_Int32 nErase
= nReplaceLen
;
1190 aResultStr
.remove( nStartPos
, nErase
);
1192 nStartPos
, aReplaceStr
.getStr(), std::min(nReplaceLen
, nReplaceStrLen
));
1194 rPar
.Get(1)->PutString(aResultStr
.makeStringAndClear());
1198 OUString aResultStr
;
1199 if (nStartPos
> aArgStr
.getLength())
1203 else if(nArgCount
== 2)
1205 aResultStr
= aArgStr
.copy( nStartPos
);
1211 if(nStartPos
+ nLen
> aArgStr
.getLength())
1213 nLen
= aArgStr
.getLength() - nStartPos
;
1216 aResultStr
= aArgStr
.copy( nStartPos
, nLen
);
1218 rPar
.Get(0)->PutString(aResultStr
);
1224 void SbRtl_Oct(StarBASIC
*, SbxArray
& rPar
, bool)
1226 if (rPar
.Count() < 2)
1228 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1233 SbxVariableRef pArg
= rPar
.Get(1);
1234 if ( pArg
->IsInteger() )
1236 snprintf( aBuffer
, sizeof(aBuffer
), "%o", pArg
->GetInteger() );
1240 snprintf( aBuffer
, sizeof(aBuffer
), "%lo", static_cast<long unsigned int>(pArg
->GetLong()) );
1242 rPar
.Get(0)->PutString(OUString::createFromAscii(aBuffer
));
1246 // Replace(expression, find, replace[, start[, count[, compare]]])
1248 void SbRtl_Replace(StarBASIC
*, SbxArray
& rPar
, bool)
1250 const sal_uInt32 nArgCount
= rPar
.Count() - 1;
1251 if ( nArgCount
< 3 || nArgCount
> 6 )
1253 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1257 sal_Int32 lStartPos
= 1;
1260 if (rPar
.Get(4)->GetType() != SbxEMPTY
)
1262 lStartPos
= rPar
.Get(4)->GetLong();
1266 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
1270 --lStartPos
; // Make it 0-based
1272 sal_Int32 lCount
= -1;
1275 if (rPar
.Get(5)->GetType() != SbxEMPTY
)
1277 lCount
= rPar
.Get(5)->GetLong();
1281 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
1286 bool bCaseInsensitive
;
1289 bCaseInsensitive
= rPar
.Get(6)->GetInteger();
1293 SbiInstance
* pInst
= GetSbData()->pInst
;
1294 if (pInst
&& pInst
->IsCompatibility())
1296 SbiRuntime
* pRT
= pInst
->pRun
;
1297 bCaseInsensitive
= pRT
&& pRT
->IsImageFlag(SbiImageFlags::COMPARETEXT
);
1301 bCaseInsensitive
= true;
1305 const OUString aExpStr
= rPar
.Get(1)->GetOUString();
1306 OUString aFindStr
= rPar
.Get(2)->GetOUString();
1307 const OUString aReplaceStr
= rPar
.Get(3)->GetOUString();
1309 OUString
aSrcStr(aExpStr
);
1310 sal_Int32 nPrevPos
= std::min(lStartPos
, aSrcStr
.getLength());
1311 css::uno::Sequence
<sal_Int32
> aOffset
;
1312 if (bCaseInsensitive
)
1314 // tdf#132389: case-insensitive operation for non-ASCII characters
1315 // tdf#142487: use css::i18n::Transliteration to correctly handle ß -> ss expansion
1316 // tdf#132388: We can't use utl::TextSearch (css::i18n::XTextSearch), because each call to
1317 // css::i18n::XTextSearch::SearchForward transliterates input string, making
1318 // performance of repeated calls unacceptable
1319 auto xTrans
= css::i18n::Transliteration::create(comphelper::getProcessComponentContext());
1320 xTrans
->loadModule(css::i18n::TransliterationModules_IGNORE_CASE
, {});
1321 aFindStr
= xTrans
->transliterate(aFindStr
, 0, aFindStr
.getLength(), aOffset
);
1322 aSrcStr
= xTrans
->transliterate(aSrcStr
, nPrevPos
, aSrcStr
.getLength() - nPrevPos
, aOffset
);
1323 nPrevPos
= std::distance(aOffset
.begin(),
1324 std::lower_bound(aOffset
.begin(), aOffset
.end(), nPrevPos
));
1327 auto getExpStrPos
= [aOffset
, nExpLen
= aExpStr
.getLength()](sal_Int32 nSrcStrPos
) -> sal_Int32
1329 assert(!aOffset
.hasElements() || aOffset
.getLength() >= nSrcStrPos
);
1330 if (!aOffset
.hasElements())
1332 return aOffset
.getLength() > nSrcStrPos
? aOffset
[nSrcStrPos
] : nExpLen
;
1335 // Note: the result starts from lStartPos, removing everything to the left. See i#94895.
1336 OUStringBuffer
sResult(aSrcStr
.getLength() - nPrevPos
);
1337 sal_Int32 nCounts
= 0;
1338 while (lCount
== -1 || lCount
> nCounts
)
1340 sal_Int32 nPos
= aSrcStr
.indexOf(aFindStr
, nPrevPos
);
1344 lStartPos
= getExpStrPos(nPrevPos
);
1345 sResult
.append(aExpStr
.getStr() + lStartPos
, getExpStrPos(nPos
) - lStartPos
);
1346 sResult
.append(aReplaceStr
);
1347 nPrevPos
= nPos
+ aFindStr
.getLength();
1350 lStartPos
= getExpStrPos(nPrevPos
);
1351 sResult
.append(aExpStr
.getStr() + lStartPos
, aExpStr
.getLength() - lStartPos
);
1352 rPar
.Get(0)->PutString(sResult
.makeStringAndClear());
1355 void SbRtl_Right(StarBASIC
*, SbxArray
& rPar
, bool)
1357 if (rPar
.Count() < 3)
1359 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1363 const OUString
& rStr
= rPar
.Get(1)->GetOUString();
1364 int nResultLen
= rPar
.Get(2)->GetLong();
1365 if( nResultLen
< 0 )
1368 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1370 int nStrLen
= rStr
.getLength();
1371 if ( nResultLen
> nStrLen
)
1373 nResultLen
= nStrLen
;
1375 OUString aResultStr
= rStr
.copy( nStrLen
- nResultLen
);
1376 rPar
.Get(0)->PutString(aResultStr
);
1380 void SbRtl_RTL(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
1382 rPar
.Get(0)->PutObject(pBasic
->getRTL().get());
1385 void SbRtl_RTrim(StarBASIC
*, SbxArray
& rPar
, bool)
1387 if (rPar
.Count() < 2)
1389 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1393 OUString
aStr(comphelper::string::stripEnd(rPar
.Get(1)->GetOUString(), ' '));
1394 rPar
.Get(0)->PutString(aStr
);
1398 void SbRtl_Sgn(StarBASIC
*, SbxArray
& rPar
, bool)
1400 if (rPar
.Count() < 2)
1402 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1406 double aDouble
= rPar
.Get(1)->GetDouble();
1407 sal_Int16 nResult
= 0;
1412 else if ( aDouble
< 0 )
1416 rPar
.Get(0)->PutInteger(nResult
);
1420 void SbRtl_Space(StarBASIC
*, SbxArray
& rPar
, bool)
1422 if (rPar
.Count() < 2)
1424 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1428 OUStringBuffer aBuf
;
1429 string::padToLength(aBuf
, rPar
.Get(1)->GetLong(), ' ');
1430 rPar
.Get(0)->PutString(aBuf
.makeStringAndClear());
1434 void SbRtl_Spc(StarBASIC
*, SbxArray
& rPar
, bool)
1436 if (rPar
.Count() < 2)
1438 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1442 OUStringBuffer aBuf
;
1443 string::padToLength(aBuf
, rPar
.Get(1)->GetLong(), ' ');
1444 rPar
.Get(0)->PutString(aBuf
.makeStringAndClear());
1448 void SbRtl_Sqr(StarBASIC
*, SbxArray
& rPar
, bool)
1450 if (rPar
.Count() < 2)
1452 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1456 double aDouble
= rPar
.Get(1)->GetDouble();
1459 rPar
.Get(0)->PutDouble(sqrt(aDouble
));
1463 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1468 void SbRtl_Str(StarBASIC
*, SbxArray
& rPar
, bool)
1470 if (rPar
.Count() < 2)
1472 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1477 OUString
aStrNew("");
1478 SbxVariableRef pArg
= rPar
.Get(1);
1479 pArg
->Format( aStr
);
1481 // Numbers start with a space
1482 if( pArg
->IsNumericRTL() )
1484 // replace commas by points so that it's symmetric to Val!
1485 aStr
= aStr
.replaceFirst( ",", "." );
1487 SbiInstance
* pInst
= GetSbData()->pInst
;
1488 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
1489 if( bCompatibility
)
1491 sal_Int32 nLen
= aStr
.getLength();
1493 const sal_Unicode
* pBuf
= aStr
.getStr();
1495 bool bNeg
= ( pBuf
[0] == '-' );
1496 sal_Int32 iZeroSearch
= 0;
1504 if( pBuf
[0] != ' ' )
1509 sal_Int32 iNext
= iZeroSearch
+ 1;
1510 if( pBuf
[iZeroSearch
] == '0' && nLen
> iNext
&& pBuf
[iNext
] == '.' )
1514 aStrNew
+= aStr
.subView(iZeroSearch
);
1518 aStrNew
= " " + aStr
;
1525 rPar
.Get(0)->PutString(aStrNew
);
1529 void SbRtl_StrComp(StarBASIC
*, SbxArray
& rPar
, bool)
1531 if (rPar
.Count() < 3)
1533 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1534 rPar
.Get(0)->PutEmpty();
1537 const OUString
& rStr1
= rPar
.Get(1)->GetOUString();
1538 const OUString
& rStr2
= rPar
.Get(2)->GetOUString();
1540 SbiInstance
* pInst
= GetSbData()->pInst
;
1542 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
1543 if( bCompatibility
)
1545 SbiRuntime
* pRT
= pInst
->pRun
;
1546 bTextCompare
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
1550 bTextCompare
= true;
1552 if (rPar
.Count() == 4)
1553 bTextCompare
= rPar
.Get(3)->GetInteger();
1555 if( !bCompatibility
)
1557 bTextCompare
= !bTextCompare
;
1559 sal_Int32 nRetValue
= 0;
1562 ::utl::TransliterationWrapper
* pTransliterationWrapper
= GetSbData()->pTransliterationWrapper
.get();
1563 if( !pTransliterationWrapper
)
1565 uno::Reference
< uno::XComponentContext
> xContext
= getProcessComponentContext();
1566 GetSbData()->pTransliterationWrapper
.reset(
1567 new ::utl::TransliterationWrapper( xContext
,
1568 TransliterationFlags::IGNORE_CASE
|
1569 TransliterationFlags::IGNORE_KANA
|
1570 TransliterationFlags::IGNORE_WIDTH
) );
1571 pTransliterationWrapper
= GetSbData()->pTransliterationWrapper
.get();
1574 LanguageType eLangType
= Application::GetSettings().GetLanguageTag().getLanguageType();
1575 pTransliterationWrapper
->loadModuleIfNeeded( eLangType
);
1576 nRetValue
= pTransliterationWrapper
->compareString( rStr1
, rStr2
);
1581 aResult
= rStr1
.compareTo( rStr2
);
1586 else if ( aResult
> 0)
1591 rPar
.Get(0)->PutInteger(sal::static_int_cast
<sal_Int16
>(nRetValue
));
1594 void SbRtl_String(StarBASIC
*, SbxArray
& rPar
, bool)
1596 if (rPar
.Count() < 2)
1598 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1602 sal_Unicode aFiller
;
1603 sal_Int32 lCount
= rPar
.Get(1)->GetLong();
1604 if( lCount
< 0 || lCount
> 0xffff )
1606 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1608 if (rPar
.Get(2)->GetType() == SbxINTEGER
)
1610 aFiller
= static_cast<sal_Unicode
>(rPar
.Get(2)->GetInteger());
1614 const OUString
& rStr
= rPar
.Get(2)->GetOUString();
1617 OUStringBuffer
aBuf(lCount
);
1618 string::padToLength(aBuf
, lCount
, aFiller
);
1619 rPar
.Get(0)->PutString(aBuf
.makeStringAndClear());
1623 void SbRtl_Tab(StarBASIC
*, SbxArray
& rPar
, bool)
1625 if (rPar
.Count() < 2)
1626 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1629 OUStringBuffer aStr
;
1630 comphelper::string::padToLength(aStr
, rPar
.Get(1)->GetLong(), '\t');
1631 rPar
.Get(0)->PutString(aStr
.makeStringAndClear());
1635 void SbRtl_Tan(StarBASIC
*, SbxArray
& rPar
, bool)
1637 if (rPar
.Count() < 2)
1639 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1643 SbxVariableRef pArg
= rPar
.Get(1);
1644 rPar
.Get(0)->PutDouble(tan(pArg
->GetDouble()));
1648 void SbRtl_UCase(StarBASIC
*, SbxArray
& rPar
, bool)
1650 if (rPar
.Count() < 2)
1652 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1656 const CharClass
& rCharClass
= GetCharClass();
1657 OUString
aStr(rPar
.Get(1)->GetOUString());
1658 aStr
= rCharClass
.uppercase( aStr
);
1659 rPar
.Get(0)->PutString(aStr
);
1664 void SbRtl_Val(StarBASIC
*, SbxArray
& rPar
, bool)
1666 if (rPar
.Count() < 2)
1668 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1672 double nResult
= 0.0;
1675 OUString
aStr(rPar
.Get(1)->GetOUString());
1677 FilterWhiteSpace( aStr
);
1678 if ( aStr
.getLength() > 1 && aStr
[0] == '&' )
1681 char aChar
= static_cast<char>(aStr
[1]);
1682 if ( aChar
== 'h' || aChar
== 'H' )
1686 else if ( aChar
== 'o' || aChar
== 'O' )
1692 OString
aByteStr(OUStringToOString(aStr
, osl_getThreadTextEncoding()));
1693 sal_Int16 nlResult
= static_cast<sal_Int16
>(strtol( aByteStr
.getStr()+2, &pEndPtr
, nRadix
));
1694 nResult
= static_cast<double>(nlResult
);
1699 rtl_math_ConversionStatus eStatus
= rtl_math_ConversionStatus_Ok
;
1700 sal_Int32 nParseEnd
= 0;
1701 nResult
= ::rtl::math::stringToDouble( aStr
, '.', ',', &eStatus
, &nParseEnd
);
1702 if ( eStatus
!= rtl_math_ConversionStatus_Ok
)
1703 StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW
);
1704 /* TODO: we should check whether all characters were parsed here,
1705 * but earlier code silently ignored trailing nonsense such as "1x"
1706 * resulting in 1 with the side effect that any alpha-only-string
1707 * like "x" resulted in 0. Not changing that now (2013-03-22) as
1708 * user macros may rely on it. */
1710 else if ( nParseEnd
!= aStr
.getLength() )
1711 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
1715 rPar
.Get(0)->PutDouble(nResult
);
1720 // Helper functions for date conversion
1721 sal_Int16
implGetDateDay( double aDate
)
1723 aDate
-= 2.0; // standardize: 1.1.1900 => 0.0
1724 aDate
= floor( aDate
);
1725 Date
aRefDate( 1, 1, 1900 );
1726 aRefDate
.AddDays( aDate
);
1728 sal_Int16 nRet
= static_cast<sal_Int16
>( aRefDate
.GetDay() );
1732 sal_Int16
implGetDateMonth( double aDate
)
1734 Date
aRefDate( 1,1,1900 );
1735 sal_Int32 nDays
= static_cast<sal_Int32
>(aDate
);
1736 nDays
-= 2; // standardize: 1.1.1900 => 0.0
1737 aRefDate
.AddDays( nDays
);
1738 sal_Int16 nRet
= static_cast<sal_Int16
>( aRefDate
.GetMonth() );
1742 css::util::Date
SbxDateToUNODate( const SbxValue
* const pVal
)
1744 double aDate
= pVal
->GetDate();
1746 css::util::Date aUnoDate
;
1747 aUnoDate
.Day
= implGetDateDay ( aDate
);
1748 aUnoDate
.Month
= implGetDateMonth( aDate
);
1749 aUnoDate
.Year
= implGetDateYear ( aDate
);
1754 void SbxDateFromUNODate( SbxValue
*pVal
, const css::util::Date
& aUnoDate
)
1757 if( implDateSerial( aUnoDate
.Year
, aUnoDate
.Month
, aUnoDate
.Day
, false, SbDateCorrection::None
, dDate
) )
1759 pVal
->PutDate( dDate
);
1763 // Function to convert date to UNO date (com.sun.star.util.Date)
1764 void SbRtl_CDateToUnoDate(StarBASIC
*, SbxArray
& rPar
, bool)
1766 if (rPar
.Count() != 2)
1768 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1772 unoToSbxValue(rPar
.Get(0), Any(SbxDateToUNODate(rPar
.Get(1))));
1775 // Function to convert date from UNO date (com.sun.star.util.Date)
1776 void SbRtl_CDateFromUnoDate(StarBASIC
*, SbxArray
& rPar
, bool)
1778 if (rPar
.Count() != 2 || rPar
.Get(1)->GetType() != SbxOBJECT
)
1780 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1784 Any
aAny(sbxToUnoValue(rPar
.Get(1), cppu::UnoType
<css::util::Date
>::get()));
1785 css::util::Date aUnoDate
;
1786 if(aAny
>>= aUnoDate
)
1787 SbxDateFromUNODate(rPar
.Get(0), aUnoDate
);
1789 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
1792 css::util::Time
SbxDateToUNOTime( const SbxValue
* const pVal
)
1794 double aDate
= pVal
->GetDate();
1796 css::util::Time aUnoTime
;
1797 aUnoTime
.Hours
= implGetHour ( aDate
);
1798 aUnoTime
.Minutes
= implGetMinute ( aDate
);
1799 aUnoTime
.Seconds
= implGetSecond ( aDate
);
1800 aUnoTime
.NanoSeconds
= 0;
1805 void SbxDateFromUNOTime( SbxValue
*pVal
, const css::util::Time
& aUnoTime
)
1807 pVal
->PutDate( implTimeSerial(aUnoTime
.Hours
, aUnoTime
.Minutes
, aUnoTime
.Seconds
) );
1810 // Function to convert date to UNO time (com.sun.star.util.Time)
1811 void SbRtl_CDateToUnoTime(StarBASIC
*, SbxArray
& rPar
, bool)
1813 if (rPar
.Count() != 2)
1815 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1819 unoToSbxValue(rPar
.Get(0), Any(SbxDateToUNOTime(rPar
.Get(1))));
1822 // Function to convert date from UNO time (com.sun.star.util.Time)
1823 void SbRtl_CDateFromUnoTime(StarBASIC
*, SbxArray
& rPar
, bool)
1825 if (rPar
.Count() != 2 || rPar
.Get(1)->GetType() != SbxOBJECT
)
1827 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1831 Any
aAny(sbxToUnoValue(rPar
.Get(1), cppu::UnoType
<css::util::Time
>::get()));
1832 css::util::Time aUnoTime
;
1833 if(aAny
>>= aUnoTime
)
1834 SbxDateFromUNOTime(rPar
.Get(0), aUnoTime
);
1836 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
1839 css::util::DateTime
SbxDateToUNODateTime( const SbxValue
* const pVal
)
1841 double aDate
= pVal
->GetDate();
1843 css::util::DateTime aUnoDT
;
1844 aUnoDT
.Day
= implGetDateDay ( aDate
);
1845 aUnoDT
.Month
= implGetDateMonth( aDate
);
1846 aUnoDT
.Year
= implGetDateYear ( aDate
);
1847 aUnoDT
.Hours
= implGetHour ( aDate
);
1848 aUnoDT
.Minutes
= implGetMinute ( aDate
);
1849 aUnoDT
.Seconds
= implGetSecond ( aDate
);
1850 aUnoDT
.NanoSeconds
= 0;
1855 void SbxDateFromUNODateTime( SbxValue
*pVal
, const css::util::DateTime
& aUnoDT
)
1858 if( implDateTimeSerial( aUnoDT
.Year
, aUnoDT
.Month
, aUnoDT
.Day
,
1859 aUnoDT
.Hours
, aUnoDT
.Minutes
, aUnoDT
.Seconds
,
1862 pVal
->PutDate( dDate
);
1866 // Function to convert date to UNO date (com.sun.star.util.Date)
1867 void SbRtl_CDateToUnoDateTime(StarBASIC
*, SbxArray
& rPar
, bool)
1869 if (rPar
.Count() != 2)
1871 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1875 unoToSbxValue(rPar
.Get(0), Any(SbxDateToUNODateTime(rPar
.Get(1))));
1878 // Function to convert date from UNO date (com.sun.star.util.Date)
1879 void SbRtl_CDateFromUnoDateTime(StarBASIC
*, SbxArray
& rPar
, bool)
1881 if (rPar
.Count() != 2 || rPar
.Get(1)->GetType() != SbxOBJECT
)
1883 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1887 Any
aAny(sbxToUnoValue(rPar
.Get(1), cppu::UnoType
<css::util::DateTime
>::get()));
1888 css::util::DateTime aUnoDT
;
1890 SbxDateFromUNODateTime(rPar
.Get(0), aUnoDT
);
1892 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
1895 // Function to convert date to ISO 8601 date format YYYYMMDD
1896 void SbRtl_CDateToIso(StarBASIC
*, SbxArray
& rPar
, bool)
1898 if (rPar
.Count() == 2)
1900 double aDate
= rPar
.Get(1)->GetDate();
1902 // Date may actually even be -YYYYYMMDD
1904 sal_Int16 nYear
= implGetDateYear( aDate
);
1905 snprintf( Buffer
, sizeof( Buffer
), (nYear
< 0 ? "%05d%02d%02d" : "%04d%02d%02d"),
1906 static_cast<int>(nYear
),
1907 static_cast<int>(implGetDateMonth( aDate
)),
1908 static_cast<int>(implGetDateDay( aDate
)) );
1909 OUString aRetStr
= OUString::createFromAscii( Buffer
);
1910 rPar
.Get(0)->PutString(aRetStr
);
1914 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1918 // Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD
1919 // And even YYMMDD for compatibility, sigh...
1920 void SbRtl_CDateFromIso(StarBASIC
*, SbxArray
& rPar
, bool)
1922 if (rPar
.Count() == 2)
1926 OUString aStr
= rPar
.Get(1)->GetOUString();
1930 // Valid formats are
1931 // YYYYMMDD -YYYMMDD YYYYYMMDD -YYYYYMMDD YYMMDD
1932 // YYYY-MM-DD -YYYY-MM-DD YYYYY-MM-DD -YYYYY-MM-DD
1934 sal_Int32 nSign
= 1;
1938 aStr
= aStr
.copy(1);
1940 const sal_Int32 nLen
= aStr
.getLength();
1942 // Signed YYMMDD two digit year is invalid.
1943 if (nLen
== 6 && nSign
== -1)
1947 // YYYYMMDD YYYYYMMDD YYMMDD
1948 // YYYY-MM-DD YYYYY-MM-DD
1949 if (nLen
!= 6 && (nLen
< 8 || 11 < nLen
))
1952 bool bUseTwoDigitYear
= false;
1953 OUString aYearStr
, aMonthStr
, aDayStr
;
1954 if (nLen
== 6 || nLen
== 8 || nLen
== 9)
1957 if (!comphelper::string::isdigitAsciiString(aStr
))
1960 const sal_Int32 nMonthPos
= (nLen
== 8 ? 4 : (nLen
== 6 ? 2 : 5));
1962 bUseTwoDigitYear
= true;
1963 aYearStr
= aStr
.copy( 0, nMonthPos
);
1964 aMonthStr
= aStr
.copy( nMonthPos
, 2 );
1965 aDayStr
= aStr
.copy( nMonthPos
+ 2, 2 );
1970 const sal_Int32 nMonthSep
= (nLen
== 11 ? 5 : 4);
1971 if (aStr
.indexOf('-') != nMonthSep
)
1973 if (aStr
.indexOf('-', nMonthSep
+ 1) != nMonthSep
+ 3)
1976 aYearStr
= aStr
.copy( 0, nMonthSep
);
1977 aMonthStr
= aStr
.copy( nMonthSep
+ 1, 2 );
1978 aDayStr
= aStr
.copy( nMonthSep
+ 4, 2 );
1979 if ( !comphelper::string::isdigitAsciiString(aYearStr
) ||
1980 !comphelper::string::isdigitAsciiString(aMonthStr
) ||
1981 !comphelper::string::isdigitAsciiString(aDayStr
))
1986 if (!implDateSerial( static_cast<sal_Int16
>(nSign
* aYearStr
.toInt32()),
1987 static_cast<sal_Int16
>(aMonthStr
.toInt32()), static_cast<sal_Int16
>(aDayStr
.toInt32()),
1988 bUseTwoDigitYear
, SbDateCorrection::None
, dDate
))
1991 rPar
.Get(0)->PutDate(dDate
);
1997 SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER
);
2001 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2005 void SbRtl_DateSerial(StarBASIC
*, SbxArray
& rPar
, bool)
2007 if (rPar
.Count() < 4)
2009 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2012 sal_Int16 nYear
= rPar
.Get(1)->GetInteger();
2013 sal_Int16 nMonth
= rPar
.Get(2)->GetInteger();
2014 sal_Int16 nDay
= rPar
.Get(3)->GetInteger();
2017 if( implDateSerial( nYear
, nMonth
, nDay
, true, SbDateCorrection::RollOver
, dDate
) )
2019 rPar
.Get(0)->PutDate(dDate
);
2023 void SbRtl_TimeSerial(StarBASIC
*, SbxArray
& rPar
, bool)
2025 if (rPar
.Count() < 4)
2027 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2030 sal_Int16 nHour
= rPar
.Get(1)->GetInteger();
2033 nHour
= 0; // because of UNO DateTimes, which go till 24 o'clock
2035 sal_Int16 nMinute
= rPar
.Get(2)->GetInteger();
2036 sal_Int16 nSecond
= rPar
.Get(3)->GetInteger();
2037 if ((nHour
< 0 || nHour
> 23) ||
2038 (nMinute
< 0 || nMinute
> 59 ) ||
2039 (nSecond
< 0 || nSecond
> 59 ))
2041 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2045 rPar
.Get(0)->PutDate(implTimeSerial(nHour
, nMinute
, nSecond
)); // JSM
2048 void SbRtl_DateValue(StarBASIC
*, SbxArray
& rPar
, bool)
2050 if (rPar
.Count() < 2)
2052 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2056 // #39629 check GetSbData()->pInst, can be called from the URL line
2057 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2058 if( GetSbData()->pInst
)
2060 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2064 sal_uInt32 n
; // Dummy
2065 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, n
, n
);
2068 LanguageType eLangType
= Application::GetSettings().GetLanguageTag().getLanguageType();
2069 sal_uInt32 nIndex
= pFormatter
->GetStandardIndex( eLangType
);
2071 OUString
aStr(rPar
.Get(1)->GetOUString());
2072 bool bSuccess
= pFormatter
->IsNumberFormat( aStr
, nIndex
, fResult
);
2073 SvNumFormatType nType
= pFormatter
->GetType( nIndex
);
2075 // DateValue("February 12, 1969") raises error if the system locale is not en_US
2076 // It seems that both locale number formatter and English number
2077 // formatter are supported in Visual Basic.
2078 if( !bSuccess
&& ( eLangType
!= LANGUAGE_ENGLISH_US
) )
2080 // Try using LANGUAGE_ENGLISH_US to get the date value.
2081 nIndex
= pFormatter
->GetStandardIndex( LANGUAGE_ENGLISH_US
);
2082 bSuccess
= pFormatter
->IsNumberFormat( aStr
, nIndex
, fResult
);
2083 nType
= pFormatter
->GetType( nIndex
);
2086 if(bSuccess
&& (nType
==SvNumFormatType::DATE
|| nType
==SvNumFormatType::DATETIME
))
2088 if ( nType
== SvNumFormatType::DATETIME
)
2091 if ( fResult
> 0.0 )
2093 fResult
= floor( fResult
);
2097 fResult
= ceil( fResult
);
2100 rPar
.Get(0)->PutDate(fResult
);
2104 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
2109 void SbRtl_TimeValue(StarBASIC
*, SbxArray
& rPar
, bool)
2111 if (rPar
.Count() < 2)
2113 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2117 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2118 if( GetSbData()->pInst
)
2119 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2123 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, n
, n
);
2126 sal_uInt32 nIndex
= 0;
2128 bool bSuccess
= pFormatter
->IsNumberFormat(rPar
.Get(1)->GetOUString(),
2130 SvNumFormatType nType
= pFormatter
->GetType(nIndex
);
2131 if(bSuccess
&& (nType
==SvNumFormatType::TIME
||nType
==SvNumFormatType::DATETIME
))
2133 if ( nType
== SvNumFormatType::DATETIME
)
2136 fResult
= fmod( fResult
, 1 );
2138 rPar
.Get(0)->PutDate(fResult
);
2142 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
2147 void SbRtl_Day(StarBASIC
*, SbxArray
& rPar
, bool)
2149 if (rPar
.Count() < 2)
2151 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2155 SbxVariableRef pArg
= rPar
.Get(1);
2156 double aDate
= pArg
->GetDate();
2158 sal_Int16 nDay
= implGetDateDay( aDate
);
2159 rPar
.Get(0)->PutInteger(nDay
);
2163 void SbRtl_Year(StarBASIC
*, SbxArray
& rPar
, bool)
2165 if (rPar
.Count() < 2)
2167 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2171 sal_Int16 nYear
= implGetDateYear(rPar
.Get(1)->GetDate());
2172 rPar
.Get(0)->PutInteger(nYear
);
2176 sal_Int16
implGetHour( double dDate
)
2178 double nFrac
= dDate
- floor( dDate
);
2180 sal_Int32 nSeconds
= static_cast<sal_Int32
>(nFrac
+ 0.5);
2181 sal_Int16 nHour
= static_cast<sal_Int16
>(nSeconds
/ 3600);
2185 void SbRtl_Hour(StarBASIC
*, SbxArray
& rPar
, bool)
2187 if (rPar
.Count() < 2)
2189 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2193 double nArg
= rPar
.Get(1)->GetDate();
2194 sal_Int16 nHour
= implGetHour( nArg
);
2195 rPar
.Get(0)->PutInteger(nHour
);
2199 void SbRtl_Minute(StarBASIC
*, SbxArray
& rPar
, bool)
2201 if (rPar
.Count() < 2)
2203 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2207 double nArg
= rPar
.Get(1)->GetDate();
2208 sal_Int16 nMin
= implGetMinute( nArg
);
2209 rPar
.Get(0)->PutInteger(nMin
);
2213 void SbRtl_Month(StarBASIC
*, SbxArray
& rPar
, bool)
2215 if (rPar
.Count() < 2)
2217 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2221 sal_Int16 nMonth
= implGetDateMonth(rPar
.Get(1)->GetDate());
2222 rPar
.Get(0)->PutInteger(nMonth
);
2226 sal_Int16
implGetSecond( double dDate
)
2228 double nFrac
= dDate
- floor( dDate
);
2230 sal_Int32 nSeconds
= static_cast<sal_Int32
>(nFrac
+ 0.5);
2231 sal_Int16 nTemp
= static_cast<sal_Int16
>(nSeconds
/ 3600);
2232 nSeconds
-= nTemp
* 3600;
2233 nTemp
= static_cast<sal_Int16
>(nSeconds
/ 60);
2234 nSeconds
-= nTemp
* 60;
2236 sal_Int16 nRet
= static_cast<sal_Int16
>(nSeconds
);
2240 void SbRtl_Second(StarBASIC
*, SbxArray
& rPar
, bool)
2242 if (rPar
.Count() < 2)
2244 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2248 double nArg
= rPar
.Get(1)->GetDate();
2249 sal_Int16 nSecond
= implGetSecond( nArg
);
2250 rPar
.Get(0)->PutInteger(nSecond
);
2256 DateTime
aDateTime( DateTime::SYSTEM
);
2257 double aSerial
= static_cast<double>(GetDayDiff( aDateTime
));
2258 tools::Long nSeconds
= aDateTime
.GetHour();
2260 nSeconds
+= aDateTime
.GetMin() * 60;
2261 nSeconds
+= aDateTime
.GetSec();
2262 double nDays
= static_cast<double>(nSeconds
) / (24.0*3600.0);
2269 void SbRtl_Now(StarBASIC
*, SbxArray
& rPar
, bool) { rPar
.Get(0)->PutDate(Now_Impl()); }
2273 void SbRtl_Time(StarBASIC
*, SbxArray
& rPar
, bool bWrite
)
2277 tools::Time
aTime( tools::Time::SYSTEM
);
2278 SbxVariable
* pMeth
= rPar
.Get(0);
2280 if( pMeth
->IsFixed() )
2284 snprintf( buf
, sizeof(buf
), "%02d:%02d:%02d",
2285 aTime
.GetHour(), aTime
.GetMin(), aTime
.GetSec() );
2286 aRes
= OUString::createFromAscii( buf
);
2290 // Time: system dependent
2291 tools::Long nSeconds
=aTime
.GetHour();
2293 nSeconds
+= aTime
.GetMin() * 60;
2294 nSeconds
+= aTime
.GetSec();
2295 double nDays
= static_cast<double>(nSeconds
) * ( 1.0 / (24.0*3600.0) );
2298 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2300 if( GetSbData()->pInst
)
2302 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2303 nIndex
= GetSbData()->pInst
->GetStdTimeIdx();
2307 sal_uInt32 n
; // Dummy
2308 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, nIndex
, n
);
2311 pFormatter
->GetOutputString( nDays
, nIndex
, aRes
, &pCol
);
2313 pMeth
->PutString( aRes
);
2317 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED
);
2321 void SbRtl_Timer(StarBASIC
*, SbxArray
& rPar
, bool)
2323 tools::Time
aTime( tools::Time::SYSTEM
);
2324 tools::Long nSeconds
= aTime
.GetHour();
2326 nSeconds
+= aTime
.GetMin() * 60;
2327 nSeconds
+= aTime
.GetSec();
2328 rPar
.Get(0)->PutDate(static_cast<double>(nSeconds
));
2332 void SbRtl_Date(StarBASIC
*, SbxArray
& rPar
, bool bWrite
)
2336 Date
aToday( Date::SYSTEM
);
2337 double nDays
= static_cast<double>(GetDayDiff( aToday
));
2338 SbxVariable
* pMeth
= rPar
.Get(0);
2339 if( pMeth
->IsString() )
2344 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2346 if( GetSbData()->pInst
)
2348 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2349 nIndex
= GetSbData()->pInst
->GetStdDateIdx();
2354 pFormatter
= SbiInstance::PrepareNumberFormatter( nIndex
, n
, n
);
2357 pFormatter
->GetOutputString( nDays
, nIndex
, aRes
, &pCol
);
2358 pMeth
->PutString( aRes
);
2362 pMeth
->PutDate( nDays
);
2367 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED
);
2371 void SbRtl_IsArray(StarBASIC
*, SbxArray
& rPar
, bool)
2373 if (rPar
.Count() < 2)
2375 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2379 rPar
.Get(0)->PutBool((rPar
.Get(1)->GetType() & SbxARRAY
) != 0);
2383 void SbRtl_IsObject(StarBASIC
*, SbxArray
& rPar
, bool)
2385 if (rPar
.Count() < 2)
2387 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2391 SbxVariable
* pVar
= rPar
.Get(1);
2392 bool bObject
= pVar
->IsObject();
2393 SbxBase
* pObj
= (bObject
? pVar
->GetObject() : nullptr);
2395 if( auto pUnoClass
= dynamic_cast<SbUnoClass
*>( pObj
) )
2397 bObject
= pUnoClass
->getUnoClass().is();
2399 rPar
.Get(0)->PutBool(bObject
);
2403 void SbRtl_IsDate(StarBASIC
*, SbxArray
& rPar
, bool)
2405 if (rPar
.Count() < 2)
2407 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2411 // #46134 only string is converted, all other types result in sal_False
2412 SbxVariableRef xArg
= rPar
.Get(1);
2413 SbxDataType eType
= xArg
->GetType();
2416 if( eType
== SbxDATE
)
2420 else if( eType
== SbxSTRING
)
2422 ErrCode nPrevError
= SbxBase::GetError();
2423 SbxBase::ResetError();
2425 // force conversion of the parameter to SbxDATE
2426 xArg
->SbxValue::GetDate();
2428 bDate
= !SbxBase::IsError();
2430 SbxBase::ResetError();
2431 SbxBase::SetError( nPrevError
);
2433 rPar
.Get(0)->PutBool(bDate
);
2437 void SbRtl_IsEmpty(StarBASIC
*, SbxArray
& rPar
, bool)
2439 if (rPar
.Count() < 2)
2441 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2445 SbxVariable
* pVar
= nullptr;
2446 if( SbiRuntime::isVBAEnabled() )
2448 pVar
= getDefaultProp(rPar
.Get(1));
2452 pVar
->Broadcast( SfxHintId::BasicDataWanted
);
2453 rPar
.Get(0)->PutBool(pVar
->IsEmpty());
2457 rPar
.Get(0)->PutBool(rPar
.Get(1)->IsEmpty());
2462 void SbRtl_IsError(StarBASIC
*, SbxArray
& rPar
, bool)
2464 if (rPar
.Count() < 2)
2466 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2470 SbxVariable
* pVar
= rPar
.Get(1);
2471 SbUnoObject
* pObj
= dynamic_cast<SbUnoObject
*>( pVar
);
2474 if ( SbxBase
* pBaseObj
= (pVar
->IsObject() ? pVar
->GetObject() : nullptr) )
2476 pObj
= dynamic_cast<SbUnoObject
*>( pBaseObj
);
2479 uno::Reference
< script::XErrorQuery
> xError
;
2482 xError
.set( pObj
->getUnoAny(), uno::UNO_QUERY
);
2486 rPar
.Get(0)->PutBool(xError
->hasError());
2490 rPar
.Get(0)->PutBool(rPar
.Get(1)->IsErr());
2495 void SbRtl_IsNull(StarBASIC
*, SbxArray
& rPar
, bool)
2497 if (rPar
.Count() < 2)
2499 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2503 // #51475 because of Uno-objects return true
2504 // even if the pObj value is NULL
2505 SbxVariableRef pArg
= rPar
.Get(1);
2506 bool bNull
= rPar
.Get(1)->IsNull();
2507 if( !bNull
&& pArg
->GetType() == SbxOBJECT
)
2509 SbxBase
* pObj
= pArg
->GetObject();
2515 rPar
.Get(0)->PutBool(bNull
);
2519 void SbRtl_IsNumeric(StarBASIC
*, SbxArray
& rPar
, bool)
2521 if (rPar
.Count() < 2)
2523 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2527 rPar
.Get(0)->PutBool(rPar
.Get(1)->IsNumericRTL());
2532 void SbRtl_IsMissing(StarBASIC
*, SbxArray
& rPar
, bool)
2534 if (rPar
.Count() < 2)
2536 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2540 // #57915 Missing is reported by an error
2541 rPar
.Get(0)->PutBool(rPar
.Get(1)->IsErr());
2545 // Function looks for wildcards, removes them and always returns the pure path
2546 static OUString
implSetupWildcard(const OUString
& rFileParam
, SbiRTLData
& rRTLData
)
2548 static const char cDelim1
= '/';
2549 static const char cDelim2
= '\\';
2550 static const char cWild1
= '*';
2551 static const char cWild2
= '?';
2553 rRTLData
.pWildCard
.reset();
2554 rRTLData
.sFullNameToBeChecked
.clear();
2556 OUString aFileParam
= rFileParam
;
2557 sal_Int32 nLastWild
= aFileParam
.lastIndexOf( cWild1
);
2560 nLastWild
= aFileParam
.lastIndexOf( cWild2
);
2562 bool bHasWildcards
= ( nLastWild
>= 0 );
2565 sal_Int32 nLastDelim
= aFileParam
.lastIndexOf( cDelim1
);
2566 if( nLastDelim
< 0 )
2568 nLastDelim
= aFileParam
.lastIndexOf( cDelim2
);
2572 // Wildcards in path?
2573 if( nLastDelim
>= 0 && nLastDelim
> nLastWild
)
2580 OUString aPathStr
= getFullPath( aFileParam
);
2581 if( nLastDelim
!= aFileParam
.getLength() - 1 )
2583 rRTLData
.sFullNameToBeChecked
= aPathStr
;
2588 OUString aPureFileName
;
2589 if( nLastDelim
< 0 )
2591 aPureFileName
= aFileParam
;
2596 aPureFileName
= aFileParam
.copy( nLastDelim
+ 1 );
2597 aFileParam
= aFileParam
.copy( 0, nLastDelim
);
2600 // Try again to get a valid URL/UNC-path with only the path
2601 OUString aPathStr
= getFullPath( aFileParam
);
2603 // Is there a pure file name left? Otherwise the path is
2604 // invalid anyway because it was not accepted by OSL before
2605 if (aPureFileName
!= "*")
2607 rRTLData
.pWildCard
= std::make_unique
<WildCard
>(aPureFileName
);
2612 static bool implCheckWildcard(std::u16string_view rName
, SbiRTLData
const& rRTLData
)
2616 if (rRTLData
.pWildCard
)
2618 bMatch
= rRTLData
.pWildCard
->Matches(rName
);
2624 static bool isRootDir( const OUString
& aDirURLStr
)
2626 INetURLObject
aDirURLObj( aDirURLStr
);
2629 // Check if it's a root directory
2630 sal_Int32 nCount
= aDirURLObj
.getSegmentCount();
2632 // No segment means Unix root directory "file:///"
2637 // Exactly one segment needs further checking, because it
2638 // can be Unix "file:///foo/" -> no root
2639 // or Windows "file:///c:/" -> root
2640 else if( nCount
== 1 )
2642 OUString aSeg1
= aDirURLObj
.getName( 0, true,
2643 INetURLObject::DecodeMechanism::WithCharset
);
2644 if( aSeg1
[1] == ':' )
2649 // More than one segments can never be root
2650 // so bRoot remains false
2655 void SbRtl_Dir(StarBASIC
*, SbxArray
& rPar
, bool)
2659 const sal_uInt32 nParCount
= rPar
.Count();
2662 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2666 SbiRTLData
& rRTLData
= GetSbData()->pInst
->GetRTLData();
2670 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
2673 if ( nParCount
>= 2 )
2675 OUString aFileParam
= rPar
.Get(1)->GetOUString();
2677 OUString aFileURLStr
= implSetupWildcard(aFileParam
, rRTLData
);
2678 if (!rRTLData
.sFullNameToBeChecked
.isEmpty())
2680 bool bExists
= false;
2681 try { bExists
= xSFI
->exists( aFileURLStr
); }
2682 catch(const Exception
& ) {}
2684 OUString aNameOnlyStr
;
2687 INetURLObject
aFileURL( aFileURLStr
);
2688 aNameOnlyStr
= aFileURL
.getName( INetURLObject::LAST_SEGMENT
,
2689 true, INetURLObject::DecodeMechanism::WithCharset
);
2691 rPar
.Get(0)->PutString(aNameOnlyStr
);
2697 OUString aDirURLStr
;
2698 bool bFolder
= xSFI
->isFolder( aFileURLStr
);
2702 aDirURLStr
= aFileURLStr
;
2706 rPar
.Get(0)->PutString("");
2709 SbAttributes nFlags
= SbAttributes::NONE
;
2710 if ( nParCount
> 2 )
2712 rRTLData
.nDirFlags
= nFlags
2713 = static_cast<SbAttributes
>(rPar
.Get(2)->GetInteger());
2717 rRTLData
.nDirFlags
= SbAttributes::NONE
;
2720 bool bIncludeFolders
= bool(nFlags
& SbAttributes::DIRECTORY
);
2721 rRTLData
.aDirSeq
= xSFI
->getFolderContents(aDirURLStr
, bIncludeFolders
);
2722 rRTLData
.nCurDirPos
= 0;
2724 // #78651 Add "." and ".." directories for VB compatibility
2725 if( bIncludeFolders
)
2727 bool bRoot
= isRootDir( aDirURLStr
);
2729 // If it's no root directory we flag the need for
2730 // the "." and ".." directories by the value -2
2731 // for the actual position. Later for -2 will be
2732 // returned "." and for -1 ".."
2735 rRTLData
.nCurDirPos
= -2;
2739 catch(const Exception
& )
2745 if (rRTLData
.aDirSeq
.hasElements())
2747 bool bFolderFlag
= bool(rRTLData
.nDirFlags
& SbAttributes::DIRECTORY
);
2749 SbiInstance
* pInst
= GetSbData()->pInst
;
2750 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
2753 if (rRTLData
.nCurDirPos
< 0)
2755 if (rRTLData
.nCurDirPos
== -2)
2759 else if (rRTLData
.nCurDirPos
== -1)
2763 rRTLData
.nCurDirPos
++;
2765 else if (rRTLData
.nCurDirPos
>= rRTLData
.aDirSeq
.getLength())
2767 rRTLData
.aDirSeq
.realloc(0);
2774 = rRTLData
.aDirSeq
.getConstArray()[rRTLData
.nCurDirPos
++];
2776 if( bCompatibility
)
2780 bool bFolder
= xSFI
->isFolder( aFile
);
2792 bool bFolder
= xSFI
->isFolder( aFile
);
2800 INetURLObject
aURL( aFile
);
2801 aPath
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true,
2802 INetURLObject::DecodeMechanism::WithCharset
);
2805 bool bMatch
= implCheckWildcard(aPath
, rRTLData
);
2813 rPar
.Get(0)->PutString(aPath
);
2819 if ( nParCount
>= 2 )
2821 OUString aFileParam
= rPar
.Get(1)->GetOUString();
2823 OUString aDirURL
= implSetupWildcard(aFileParam
, rRTLData
);
2825 SbAttributes nFlags
= SbAttributes::NONE
;
2826 if ( nParCount
> 2 )
2828 rRTLData
.nDirFlags
= nFlags
2829 = static_cast<SbAttributes
>(rPar
.Get(2)->GetInteger());
2833 rRTLData
.nDirFlags
= SbAttributes::NONE
;
2837 bool bIncludeFolders
= bool(nFlags
& SbAttributes::DIRECTORY
);
2838 rRTLData
.pDir
= std::make_unique
<Directory
>(aDirURL
);
2839 FileBase::RC nRet
= rRTLData
.pDir
->open();
2840 if( nRet
!= FileBase::E_None
)
2842 rRTLData
.pDir
.reset();
2843 rPar
.Get(0)->PutString(OUString());
2847 // #86950 Add "." and ".." directories for VB compatibility
2848 rRTLData
.nCurDirPos
= 0;
2849 if( bIncludeFolders
)
2851 bool bRoot
= isRootDir( aDirURL
);
2853 // If it's no root directory we flag the need for
2854 // the "." and ".." directories by the value -2
2855 // for the actual position. Later for -2 will be
2856 // returned "." and for -1 ".."
2859 rRTLData
.nCurDirPos
= -2;
2867 bool bFolderFlag
= bool(rRTLData
.nDirFlags
& SbAttributes::DIRECTORY
);
2870 if (rRTLData
.nCurDirPos
< 0)
2872 if (rRTLData
.nCurDirPos
== -2)
2876 else if (rRTLData
.nCurDirPos
== -1)
2880 rRTLData
.nCurDirPos
++;
2884 DirectoryItem aItem
;
2885 FileBase::RC nRet
= rRTLData
.pDir
->getNextItem(aItem
);
2886 if( nRet
!= FileBase::E_None
)
2888 rRTLData
.pDir
.reset();
2894 FileStatus
aFileStatus( osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_FileName
);
2895 nRet
= aItem
.getFileStatus( aFileStatus
);
2896 if( nRet
!= FileBase::E_None
)
2898 SAL_WARN("basic", "getFileStatus failed");
2902 // Only directories?
2905 FileStatus::Type aType
= aFileStatus
.getFileType();
2906 bool bFolder
= isFolder( aType
);
2913 aPath
= aFileStatus
.getFileName();
2916 bool bMatch
= implCheckWildcard(aPath
, rRTLData
);
2924 rPar
.Get(0)->PutString(aPath
);
2930 void SbRtl_GetAttr(StarBASIC
*, SbxArray
& rPar
, bool)
2932 if (rPar
.Count() == 2)
2934 sal_Int16 nFlags
= 0;
2936 // In Windows, we want to use Windows API to get the file attributes
2937 // for VBA interoperability.
2939 if( SbiRuntime::isVBAEnabled() )
2941 OUString aPathURL
= getFullPath(rPar
.Get(1)->GetOUString());
2943 FileBase::getSystemPathFromFileURL( aPathURL
, aPath
);
2944 DWORD nRealFlags
= GetFileAttributesW (o3tl::toW(aPath
.getStr()));
2945 if (nRealFlags
!= 0xffffffff)
2947 if (nRealFlags
== FILE_ATTRIBUTE_NORMAL
)
2951 nFlags
= static_cast<sal_Int16
>(nRealFlags
);
2955 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
2957 rPar
.Get(0)->PutInteger(nFlags
);
2965 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
2970 OUString aPath
= getFullPath(rPar
.Get(1)->GetOUString());
2971 bool bExists
= false;
2972 try { bExists
= xSFI
->exists( aPath
); }
2973 catch(const Exception
& ) {}
2976 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
2980 bool bReadOnly
= xSFI
->isReadOnly( aPath
);
2981 bool bHidden
= xSFI
->isHidden( aPath
);
2982 bool bDirectory
= xSFI
->isFolder( aPath
);
2985 nFlags
|= sal_uInt16(SbAttributes::READONLY
);
2989 nFlags
|= sal_uInt16(SbAttributes::HIDDEN
);
2993 nFlags
|= sal_uInt16(SbAttributes::DIRECTORY
);
2996 catch(const Exception
& )
2998 StarBASIC::Error( ERRCODE_IO_GENERAL
);
3004 DirectoryItem aItem
;
3005 (void)DirectoryItem::get(getFullPath(rPar
.Get(1)->GetOUString()), aItem
);
3006 FileStatus
aFileStatus( osl_FileStatus_Mask_Attributes
| osl_FileStatus_Mask_Type
);
3007 (void)aItem
.getFileStatus( aFileStatus
);
3008 sal_uInt64 nAttributes
= aFileStatus
.getAttributes();
3009 bool bReadOnly
= (nAttributes
& osl_File_Attribute_ReadOnly
) != 0;
3011 FileStatus::Type aType
= aFileStatus
.getFileType();
3012 bool bDirectory
= isFolder( aType
);
3015 nFlags
|= sal_uInt16(SbAttributes::READONLY
);
3019 nFlags
|= sal_uInt16(SbAttributes::DIRECTORY
);
3022 rPar
.Get(0)->PutInteger(nFlags
);
3026 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3031 void SbRtl_FileDateTime(StarBASIC
*, SbxArray
& rPar
, bool)
3033 if (rPar
.Count() != 2)
3035 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3039 OUString aPath
= rPar
.Get(1)->GetOUString();
3040 tools::Time
aTime( tools::Time::EMPTY
);
3041 Date
aDate( Date::EMPTY
);
3044 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
3049 util::DateTime aUnoDT
= xSFI
->getDateTimeModified( aPath
);
3050 aTime
= tools::Time( aUnoDT
);
3051 aDate
= Date( aUnoDT
);
3053 catch(const Exception
& )
3055 StarBASIC::Error( ERRCODE_IO_GENERAL
);
3061 bool bSuccess
= false;
3064 DirectoryItem aItem
;
3065 if (DirectoryItem::get( getFullPath( aPath
), aItem
) != FileBase::E_None
)
3068 FileStatus
aFileStatus( osl_FileStatus_Mask_ModifyTime
);
3069 if (aItem
.getFileStatus( aFileStatus
) != FileBase::E_None
)
3072 TimeValue aTimeVal
= aFileStatus
.getModifyTime();
3074 if (!osl_getDateTimeFromTimeValue( &aTimeVal
, &aDT
))
3075 // Strictly spoken this is not an i/o error but some other failure.
3078 aTime
= tools::Time( aDT
.Hours
, aDT
.Minutes
, aDT
.Seconds
, aDT
.NanoSeconds
);
3079 aDate
= Date( aDT
.Day
, aDT
.Month
, aDT
.Year
);
3085 StarBASIC::Error( ERRCODE_IO_GENERAL
);
3088 // An empty date shall not result in a formatted null-date (1899-12-30
3089 // or 1900-01-01) or even worse -0001-12-03 or some such due to how
3090 // GetDayDiff() treats things. There should be an error set in this
3091 // case anyway because of a missing file or other error above, but... so
3092 // do not even bother to use the number formatter.
3094 if (aDate
.IsEmpty())
3096 aRes
= "0000-00-00 00:00:00";
3100 double fSerial
= static_cast<double>(GetDayDiff( aDate
));
3101 tools::Long nSeconds
= aTime
.GetHour();
3103 nSeconds
+= aTime
.GetMin() * 60;
3104 nSeconds
+= aTime
.GetSec();
3105 double nDays
= static_cast<double>(nSeconds
) / (24.0*3600.0);
3110 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
3112 if( GetSbData()->pInst
)
3114 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
3115 nIndex
= GetSbData()->pInst
->GetStdDateTimeIdx();
3120 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, n
, nIndex
);
3123 pFormatter
->GetOutputString( fSerial
, nIndex
, aRes
, &pCol
);
3125 rPar
.Get(0)->PutString(aRes
);
3130 void SbRtl_EOF(StarBASIC
*, SbxArray
& rPar
, bool)
3132 // No changes for UCB
3133 if (rPar
.Count() != 2)
3135 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3139 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3140 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3141 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3144 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3148 SvStream
* pSvStrm
= pSbStrm
->GetStrm();
3149 if ( pSbStrm
->IsText() )
3152 (*pSvStrm
).ReadChar( cBla
); // can we read another character?
3153 beof
= pSvStrm
->eof();
3156 pSvStrm
->SeekRel( -1 );
3161 beof
= pSvStrm
->eof(); // for binary data!
3163 rPar
.Get(0)->PutBool(beof
);
3167 void SbRtl_FileAttr(StarBASIC
*, SbxArray
& rPar
, bool)
3169 // No changes for UCB
3170 // #57064 Although this function doesn't operate with DirEntry, it is
3171 // not touched by the adjustment to virtual URLs, as it only works on
3172 // already opened files and the name doesn't matter there.
3174 if (rPar
.Count() != 3)
3176 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3180 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3181 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3182 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3185 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3189 if (rPar
.Get(2)->GetInteger() == 1)
3191 nRet
= static_cast<sal_Int16
>(pSbStrm
->GetMode());
3195 nRet
= 0; // System file handle not supported
3197 rPar
.Get(0)->PutInteger(nRet
);
3200 void SbRtl_Loc(StarBASIC
*, SbxArray
& rPar
, bool)
3202 // No changes for UCB
3203 if (rPar
.Count() != 2)
3205 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3209 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3210 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3211 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3214 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3217 SvStream
* pSvStrm
= pSbStrm
->GetStrm();
3219 if( pSbStrm
->IsRandom())
3221 short nBlockLen
= pSbStrm
->GetBlockLen();
3222 nPos
= nBlockLen
? (pSvStrm
->Tell() / nBlockLen
) : 0;
3223 nPos
++; // block positions starting at 1
3225 else if ( pSbStrm
->IsText() )
3227 nPos
= pSbStrm
->GetLine();
3229 else if( pSbStrm
->IsBinary() )
3231 nPos
= pSvStrm
->Tell();
3233 else if ( pSbStrm
->IsSeq() )
3235 nPos
= ( pSvStrm
->Tell()+1 ) / 128;
3239 nPos
= pSvStrm
->Tell();
3241 rPar
.Get(0)->PutLong(static_cast<sal_Int32
>(nPos
));
3245 void SbRtl_Lof(StarBASIC
*, SbxArray
& rPar
, bool)
3247 // No changes for UCB
3248 if (rPar
.Count() != 2)
3250 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3254 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3255 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3256 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3259 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3262 SvStream
* pSvStrm
= pSbStrm
->GetStrm();
3263 sal_uInt64
const nLen
= pSvStrm
->TellEnd();
3264 rPar
.Get(0)->PutLong(static_cast<sal_Int32
>(nLen
));
3269 void SbRtl_Seek(StarBASIC
*, SbxArray
& rPar
, bool)
3271 // No changes for UCB
3272 int nArgs
= static_cast<int>(rPar
.Count());
3273 if ( nArgs
< 2 || nArgs
> 3 )
3275 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3278 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3279 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3280 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3283 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3286 SvStream
* pStrm
= pSbStrm
->GetStrm();
3288 if ( nArgs
== 2 ) // Seek-Function
3290 sal_uInt64 nPos
= pStrm
->Tell();
3291 if( pSbStrm
->IsRandom() )
3293 nPos
= nPos
/ pSbStrm
->GetBlockLen();
3295 nPos
++; // Basic counts from 1
3296 rPar
.Get(0)->PutLong(static_cast<sal_Int32
>(nPos
));
3298 else // Seek-Statement
3300 sal_Int32 nPos
= rPar
.Get(2)->GetLong();
3303 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3306 nPos
--; // Basic counts from 1, SvStreams count from 0
3307 pSbStrm
->SetExpandOnWriteTo( 0 );
3308 if ( pSbStrm
->IsRandom() )
3310 nPos
*= pSbStrm
->GetBlockLen();
3312 pStrm
->Seek( static_cast<sal_uInt64
>(nPos
) );
3313 pSbStrm
->SetExpandOnWriteTo( nPos
);
3317 void SbRtl_Format(StarBASIC
*, SbxArray
& rPar
, bool)
3319 const sal_uInt32 nArgCount
= rPar
.Count();
3320 if ( nArgCount
< 2 || nArgCount
> 3 )
3322 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3327 if( nArgCount
== 2 )
3329 rPar
.Get(1)->Format(aResult
);
3333 OUString
aFmt(rPar
.Get(2)->GetOUString());
3334 rPar
.Get(1)->Format(aResult
, &aFmt
);
3336 rPar
.Get(0)->PutString(aResult
);
3340 static void lcl_FormatNumberPercent(SbxArray
& rPar
, bool isPercent
)
3342 const sal_uInt32 nArgCount
= rPar
.Count();
3343 if (nArgCount
< 2 || nArgCount
> 6)
3345 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3349 // The UI locale never changes -> we can use static value here
3350 static const LocaleDataWrapper
localeData(Application::GetSettings().GetUILanguageTag());
3351 sal_Int16 nNumDigitsAfterDecimal
= -1;
3352 if (nArgCount
> 2 && !rPar
.Get(2)->IsEmpty())
3354 nNumDigitsAfterDecimal
= rPar
.Get(2)->GetInteger();
3355 if (nNumDigitsAfterDecimal
< -1)
3357 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3360 else if (nNumDigitsAfterDecimal
> 255)
3361 nNumDigitsAfterDecimal
%= 256;
3363 if (nNumDigitsAfterDecimal
== -1)
3364 nNumDigitsAfterDecimal
= LocaleDataWrapper::getNumDigits();
3366 bool bIncludeLeadingDigit
= LocaleDataWrapper::isNumLeadingZero();
3367 if (nArgCount
> 3 && !rPar
.Get(3)->IsEmpty())
3369 switch (rPar
.Get(3)->GetInteger())
3371 case ooo::vba::VbTriState::vbFalse
:
3372 bIncludeLeadingDigit
= false;
3374 case ooo::vba::VbTriState::vbTrue
:
3375 bIncludeLeadingDigit
= true;
3377 case ooo::vba::VbTriState::vbUseDefault
:
3381 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3386 bool bUseParensForNegativeNumbers
= false;
3387 if (nArgCount
> 4 && !rPar
.Get(4)->IsEmpty())
3389 switch (rPar
.Get(4)->GetInteger())
3391 case ooo::vba::VbTriState::vbFalse
:
3392 case ooo::vba::VbTriState::vbUseDefault
:
3395 case ooo::vba::VbTriState::vbTrue
:
3396 bUseParensForNegativeNumbers
= true;
3399 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3404 bool bGroupDigits
= false;
3405 if (nArgCount
> 5 && !rPar
.Get(5)->IsEmpty())
3407 switch (rPar
.Get(5)->GetInteger())
3409 case ooo::vba::VbTriState::vbFalse
:
3410 case ooo::vba::VbTriState::vbUseDefault
:
3413 case ooo::vba::VbTriState::vbTrue
:
3414 bGroupDigits
= true;
3417 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3422 double fVal
= rPar
.Get(1)->GetDouble();
3425 const bool bNegative
= fVal
< 0;
3427 fVal
= fabs(fVal
); // Always work with non-negatives, to easily handle leading zero
3429 static const sal_Unicode decSep
= localeData
.getNumDecimalSep().toChar();
3430 OUString aResult
= rtl::math::doubleToUString(
3431 fVal
, rtl_math_StringFormat_F
, nNumDigitsAfterDecimal
, decSep
,
3432 bGroupDigits
? localeData
.getDigitGrouping().getConstArray() : nullptr,
3433 localeData
.getNumThousandSep().toChar());
3435 if (!bIncludeLeadingDigit
&& aResult
.getLength() > 1 && aResult
.startsWith("0"))
3436 aResult
= aResult
.copy(1);
3438 if (nNumDigitsAfterDecimal
> 0)
3440 sal_Int32 nActualDigits
;
3441 const sal_Int32 nSepPos
= aResult
.indexOf(decSep
);
3445 nActualDigits
= aResult
.getLength() - nSepPos
- 1;
3447 // VBA allows up to 255 digits; rtl::math::doubleToUString outputs up to 15 digits
3448 // for ~small numbers, so pad them as appropriate.
3449 if (nActualDigits
< nNumDigitsAfterDecimal
)
3451 OUStringBuffer sBuf
;
3452 comphelper::string::padToLength(sBuf
, nNumDigitsAfterDecimal
- nActualDigits
, '0');
3459 if (bUseParensForNegativeNumbers
)
3460 aResult
= "(" + aResult
+ ")";
3462 aResult
= "-" + aResult
;
3465 rPar
.Get(0)->PutString(aResult
);
3470 // https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatnumber-function
3471 void SbRtl_FormatNumber(StarBASIC
*, SbxArray
& rPar
, bool)
3473 return lcl_FormatNumberPercent(rPar
, false);
3476 // https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatpercent-function
3477 void SbRtl_FormatPercent(StarBASIC
*, SbxArray
& rPar
, bool)
3479 return lcl_FormatNumberPercent(rPar
, true);
3484 // note: BASIC does not use comphelper::random, because
3485 // Randomize(int) must be supported and should not affect non-BASIC random use
3486 struct RandomNumberGenerator
3488 std::mt19937 global_rng
;
3490 RandomNumberGenerator()
3494 std::random_device rd
;
3495 // initialises the state of the global random number generator
3496 // should only be called once.
3497 // (note, a few std::variate_generator<> (like normal) have their
3498 // own state which would need a reset as well to guarantee identical
3499 // sequence of numbers, e.g. via myrand.distribution().reset())
3500 global_rng
.seed(rd() ^ time(nullptr));
3502 catch (std::runtime_error
& e
)
3504 SAL_WARN("basic", "Using std::random_device failed: " << e
.what());
3505 global_rng
.seed(time(nullptr));
3510 class theRandomNumberGenerator
: public rtl::Static
<RandomNumberGenerator
, theRandomNumberGenerator
> {};
3514 void SbRtl_Randomize(StarBASIC
*, SbxArray
& rPar
, bool)
3516 if (rPar
.Count() > 2)
3518 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3520 if (rPar
.Count() == 2)
3522 int nSeed
= static_cast<int>(rPar
.Get(1)->GetInteger());
3523 theRandomNumberGenerator::get().global_rng
.seed(nSeed
);
3525 // without parameter, no need to do anything - RNG is seeded at first use
3528 void SbRtl_Rnd(StarBASIC
*, SbxArray
& rPar
, bool)
3530 if (rPar
.Count() > 2)
3532 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3536 std::uniform_real_distribution
<double> dist(0.0, 1.0);
3537 double const tmp(dist(theRandomNumberGenerator::get().global_rng
));
3538 rPar
.Get(0)->PutDouble(tmp
);
3543 // Syntax: Shell("Path",[ Window-Style,[ "Params", [ bSync = sal_False ]]])
3544 // WindowStyles (VBA compatible):
3547 // 10 == Full-Screen (text mode applications OS/2, WIN95, WNT)
3548 // HACK: The WindowStyle will be passed to
3549 // Application::StartApp in Creator. Format: "xxxx2"
3552 void SbRtl_Shell(StarBASIC
*, SbxArray
& rPar
, bool)
3554 const sal_uInt32 nArgCount
= rPar
.Count();
3555 if ( nArgCount
< 2 || nArgCount
> 5 )
3557 rPar
.Get(0)->PutLong(0);
3558 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3562 oslProcessOption nOptions
= osl_Process_SEARCHPATH
| osl_Process_DETACHED
;
3564 OUString aCmdLine
= rPar
.Get(1)->GetOUString();
3565 // attach additional parameters - everything must be parsed anyway
3566 if( nArgCount
>= 4 )
3568 OUString tmp
= rPar
.Get(3)->GetOUString().trim();
3571 aCmdLine
+= " " + tmp
;
3574 else if( aCmdLine
.isEmpty() )
3576 // avoid special treatment (empty list)
3579 sal_Int32 nLen
= aCmdLine
.getLength();
3581 // #55735 if there are parameters, they have to be separated
3582 // #72471 also separate the single parameters
3583 std::vector
<OUString
> aTokenVector
;
3592 if ( c
!= ' ' && c
!= '\t' )
3598 if( c
== '\"' || c
== '\'' )
3600 sal_Int32 iFoundPos
= aCmdLine
.indexOf( c
, i
+ 1 );
3604 aToken
= aCmdLine
.copy( i
);
3609 aToken
= aCmdLine
.copy( i
+ 1, (iFoundPos
- i
- 1) );
3615 sal_Int32 iFoundSpacePos
= aCmdLine
.indexOf( ' ', i
);
3616 sal_Int32 iFoundTabPos
= aCmdLine
.indexOf( '\t', i
);
3617 sal_Int32 iFoundPos
= iFoundSpacePos
>= 0 ? iFoundTabPos
>= 0 ? std::min( iFoundSpacePos
, iFoundTabPos
) : iFoundSpacePos
: -1;
3621 aToken
= aCmdLine
.copy( i
);
3626 aToken
= aCmdLine
.copy( i
, (iFoundPos
- i
) );
3631 // insert into the list
3632 aTokenVector
.push_back( aToken
);
3634 // #55735 / #72471 end
3636 sal_Int16 nWinStyle
= 0;
3637 if( nArgCount
>= 3 )
3639 nWinStyle
= rPar
.Get(2)->GetInteger();
3643 nOptions
|= osl_Process_MINIMIZED
;
3646 nOptions
|= osl_Process_MAXIMIZED
;
3649 nOptions
|= osl_Process_FULLSCREEN
;
3654 if( nArgCount
>= 5 )
3656 bSync
= rPar
.Get(4)->GetBool();
3660 nOptions
|= osl_Process_WAIT
;
3664 // #72471 work parameter(s) up
3665 std::vector
<OUString
>::const_iterator iter
= aTokenVector
.begin();
3666 OUString aOUStrProgURL
= getFullPath( *iter
);
3670 sal_uInt16 nParamCount
= sal::static_int_cast
< sal_uInt16
>(aTokenVector
.size() - 1 );
3671 std::unique_ptr
<rtl_uString
*[]> pParamList
;
3674 pParamList
.reset( new rtl_uString
*[nParamCount
]);
3675 for(int iVector
= 0; iter
!= aTokenVector
.end(); ++iVector
, ++iter
)
3677 const OUString
& rParamStr
= *iter
;
3678 pParamList
[iVector
] = nullptr;
3679 rtl_uString_assign(&(pParamList
[iVector
]), rParamStr
.pData
);
3684 bool bSucc
= osl_executeProcess(
3685 aOUStrProgURL
.pData
,
3692 &pApp
) == osl_Process_E_None
;
3694 // 53521 only free process handle on success
3697 osl_freeProcessHandle( pApp
);
3700 for(int j
= 0; j
< nParamCount
; ++j
)
3702 rtl_uString_release(pParamList
[j
]);
3707 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
3711 rPar
.Get(0)->PutLong(0);
3716 void SbRtl_VarType(StarBASIC
*, SbxArray
& rPar
, bool)
3718 if (rPar
.Count() != 2)
3720 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3724 SbxDataType eType
= rPar
.Get(1)->GetType();
3725 rPar
.Get(0)->PutInteger(static_cast<sal_Int16
>(eType
));
3729 // Exported function
3730 OUString
getBasicTypeName( SbxDataType eType
)
3732 static const char* pTypeNames
[] =
3734 "Empty", // SbxEMPTY
3736 "Integer", // SbxINTEGER
3738 "Single", // SbxSINGLE
3739 "Double", // SbxDOUBLE
3740 "Currency", // SbxCURRENCY
3742 "String", // SbxSTRING
3743 "Object", // SbxOBJECT
3744 "Error", // SbxERROR
3745 "Boolean", // SbxBOOL
3746 "Variant", // SbxVARIANT
3747 "DataObject", // SbxDATAOBJECT
3752 "UShort", // SbxUSHORT
3753 "ULong", // SbxULONG
3754 "Long64", // SbxLONG64
3755 "ULong64", // SbxULONG64
3759 "HResult", // SbxHRESULT
3760 "Pointer", // SbxPOINTER
3761 "DimArray", // SbxDIMARRAY
3762 "CArray", // SbxCARRAY
3763 "Userdef", // SbxUSERDEF
3764 "Lpstr", // SbxLPSTR
3765 "Lpwstr", // SbxLPWSTR
3766 "Unknown Type", // SbxCoreSTRING
3767 "WString", // SbxWSTRING
3768 "WChar", // SbxWCHAR
3769 "Int64", // SbxSALINT64
3770 "UInt64", // SbxSALUINT64
3771 "Decimal", // SbxDECIMAL
3774 size_t nPos
= static_cast<size_t>(eType
) & 0x0FFF;
3775 const size_t nTypeNameCount
= SAL_N_ELEMENTS( pTypeNames
);
3776 if ( nPos
>= nTypeNameCount
)
3778 nPos
= nTypeNameCount
- 1;
3780 return OUString::createFromAscii(pTypeNames
[nPos
]);
3783 static OUString
getObjectTypeName( SbxVariable
* pVar
)
3785 OUString
sRet( "Object" );
3788 SbxBase
* pBaseObj
= pVar
->GetObject();
3795 SbUnoObject
* pUnoObj
= dynamic_cast<SbUnoObject
*>( pVar
);
3798 pUnoObj
= dynamic_cast<SbUnoObject
*>( pBaseObj
);
3802 Any aObj
= pUnoObj
->getUnoAny();
3803 // For upstreaming unless we start to build oovbaapi by default
3804 // we need to get detect the vba-ness of the object in some
3806 // note: Automation objects do not support XServiceInfo
3807 uno::Reference
< XServiceInfo
> xServInfo( aObj
, uno::UNO_QUERY
);
3808 if ( xServInfo
.is() )
3810 // is this a VBA object ?
3811 Sequence
< OUString
> sServices
= xServInfo
->getSupportedServiceNames();
3812 if ( sServices
.hasElements() )
3814 sRet
= sServices
[ 0 ];
3819 uno::Reference
< bridge::oleautomation::XAutomationObject
> xAutoMation( aObj
, uno::UNO_QUERY
);
3820 if ( xAutoMation
.is() )
3822 uno::Reference
< script::XInvocation
> xInv( aObj
, uno::UNO_QUERY
);
3827 xInv
->getValue( "$GetTypeName" ) >>= sRet
;
3829 catch(const Exception
& )
3835 sal_Int32 nDot
= sRet
.lastIndexOf( '.' );
3836 if ( nDot
!= -1 && nDot
< sRet
.getLength() )
3838 sRet
= sRet
.copy( nDot
+ 1 );
3846 void SbRtl_TypeName(StarBASIC
*, SbxArray
& rPar
, bool)
3848 if (rPar
.Count() != 2)
3850 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3854 SbxDataType eType
= rPar
.Get(1)->GetType();
3855 bool bIsArray
= ( ( eType
& SbxARRAY
) != 0 );
3858 if ( SbiRuntime::isVBAEnabled() && eType
== SbxOBJECT
)
3860 aRetStr
= getObjectTypeName(rPar
.Get(1));
3864 aRetStr
= getBasicTypeName( eType
);
3870 rPar
.Get(0)->PutString(aRetStr
);
3874 void SbRtl_Len(StarBASIC
*, SbxArray
& rPar
, bool)
3876 if (rPar
.Count() != 2)
3878 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3882 const OUString
& rStr
= rPar
.Get(1)->GetOUString();
3883 rPar
.Get(0)->PutLong(rStr
.getLength());
3887 void SbRtl_DDEInitiate(StarBASIC
*, SbxArray
& rPar
, bool)
3889 int nArgs
= static_cast<int>(rPar
.Count());
3892 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3895 const OUString
& rApp
= rPar
.Get(1)->GetOUString();
3896 const OUString
& rTopic
= rPar
.Get(2)->GetOUString();
3898 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3900 ErrCode nDdeErr
= pDDE
->Initiate( rApp
, rTopic
, nChannel
);
3903 StarBASIC::Error( nDdeErr
);
3907 rPar
.Get(0)->PutInteger(static_cast<sal_Int16
>(nChannel
));
3911 void SbRtl_DDETerminate(StarBASIC
*, SbxArray
& rPar
, bool)
3913 rPar
.Get(0)->PutEmpty();
3914 int nArgs
= static_cast<int>(rPar
.Count());
3917 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3920 size_t nChannel
= rPar
.Get(1)->GetInteger();
3921 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3922 ErrCode nDdeErr
= pDDE
->Terminate( nChannel
);
3925 StarBASIC::Error( nDdeErr
);
3929 void SbRtl_DDETerminateAll(StarBASIC
*, SbxArray
& rPar
, bool)
3931 rPar
.Get(0)->PutEmpty();
3932 int nArgs
= static_cast<int>(rPar
.Count());
3935 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3939 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3940 ErrCode nDdeErr
= pDDE
->TerminateAll();
3943 StarBASIC::Error( nDdeErr
);
3947 void SbRtl_DDERequest(StarBASIC
*, SbxArray
& rPar
, bool)
3949 int nArgs
= static_cast<int>(rPar
.Count());
3952 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3955 size_t nChannel
= rPar
.Get(1)->GetInteger();
3956 const OUString
& rItem
= rPar
.Get(2)->GetOUString();
3957 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3959 ErrCode nDdeErr
= pDDE
->Request( nChannel
, rItem
, aResult
);
3962 StarBASIC::Error( nDdeErr
);
3966 rPar
.Get(0)->PutString(aResult
);
3970 void SbRtl_DDEExecute(StarBASIC
*, SbxArray
& rPar
, bool)
3972 rPar
.Get(0)->PutEmpty();
3973 int nArgs
= static_cast<int>(rPar
.Count());
3976 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3979 size_t nChannel
= rPar
.Get(1)->GetInteger();
3980 const OUString
& rCommand
= rPar
.Get(2)->GetOUString();
3981 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3982 ErrCode nDdeErr
= pDDE
->Execute( nChannel
, rCommand
);
3985 StarBASIC::Error( nDdeErr
);
3989 void SbRtl_DDEPoke(StarBASIC
*, SbxArray
& rPar
, bool)
3991 rPar
.Get(0)->PutEmpty();
3992 int nArgs
= static_cast<int>(rPar
.Count());
3995 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3998 size_t nChannel
= rPar
.Get(1)->GetInteger();
3999 const OUString
& rItem
= rPar
.Get(2)->GetOUString();
4000 const OUString
& rData
= rPar
.Get(3)->GetOUString();
4001 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
4002 ErrCode nDdeErr
= pDDE
->Poke( nChannel
, rItem
, rData
);
4005 StarBASIC::Error( nDdeErr
);
4010 void SbRtl_FreeFile(StarBASIC
*, SbxArray
& rPar
, bool)
4012 if (rPar
.Count() != 1)
4014 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4017 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
4019 while( nChannel
< CHANNELS
)
4021 SbiStream
* pStrm
= pIO
->GetStream( nChannel
);
4024 rPar
.Get(0)->PutInteger(nChannel
);
4029 StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES
);
4032 void SbRtl_LBound(StarBASIC
*, SbxArray
& rPar
, bool)
4034 const sal_uInt32 nParCount
= rPar
.Count();
4035 if ( nParCount
!= 3 && nParCount
!= 2 )
4037 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4040 SbxBase
* pParObj
= rPar
.Get(1)->GetObject();
4041 SbxDimArray
* pArr
= dynamic_cast<SbxDimArray
*>( pParObj
);
4044 sal_Int32 nLower
, nUpper
;
4045 short nDim
= (nParCount
== 3) ? static_cast<short>(rPar
.Get(2)->GetInteger()) : 1;
4046 if (!pArr
->GetDim(nDim
, nLower
, nUpper
))
4047 StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE
);
4049 rPar
.Get(0)->PutLong(nLower
);
4052 StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS
);
4055 void SbRtl_UBound(StarBASIC
*, SbxArray
& rPar
, bool)
4057 const sal_uInt32 nParCount
= rPar
.Count();
4058 if ( nParCount
!= 3 && nParCount
!= 2 )
4060 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4064 SbxBase
* pParObj
= rPar
.Get(1)->GetObject();
4065 SbxDimArray
* pArr
= dynamic_cast<SbxDimArray
*>( pParObj
);
4068 sal_Int32 nLower
, nUpper
;
4069 short nDim
= (nParCount
== 3) ? static_cast<short>(rPar
.Get(2)->GetInteger()) : 1;
4070 if (!pArr
->GetDim(nDim
, nLower
, nUpper
))
4071 StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE
);
4073 rPar
.Get(0)->PutLong(nUpper
);
4076 StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS
);
4079 void SbRtl_RGB(StarBASIC
*, SbxArray
& rPar
, bool)
4081 if (rPar
.Count() != 4)
4083 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4087 sal_Int32 nRed
= rPar
.Get(1)->GetInteger() & 0xFF;
4088 sal_Int32 nGreen
= rPar
.Get(2)->GetInteger() & 0xFF;
4089 sal_Int32 nBlue
= rPar
.Get(3)->GetInteger() & 0xFF;
4092 SbiInstance
* pInst
= GetSbData()->pInst
;
4093 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
4094 // See discussion in tdf#145725, here's the quotation from a link indicated in the bugtracker
4095 // which explains why we need to manage RGB differently according to VB compatibility
4096 // "In other words, the individual color components are stored in the opposite order one would expect.
4097 // VB stores the red color component in the low-order byte of the long integer's low-order word,
4098 // the green color in the high-order byte of the low-order word, and the blue color in the low-order byte of the high-order word"
4099 if( bCompatibility
)
4101 nRGB
= (nBlue
<< 16) | (nGreen
<< 8) | nRed
;
4105 nRGB
= (nRed
<< 16) | (nGreen
<< 8) | nBlue
;
4107 rPar
.Get(0)->PutLong(nRGB
);
4110 void SbRtl_QBColor(StarBASIC
*, SbxArray
& rPar
, bool)
4112 static const sal_Int32 pRGB
[] =
4132 if (rPar
.Count() != 2)
4134 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4138 sal_Int16 nCol
= rPar
.Get(1)->GetInteger();
4139 if( nCol
< 0 || nCol
> 15 )
4141 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4144 sal_Int32 nRGB
= pRGB
[ nCol
];
4145 rPar
.Get(0)->PutLong(nRGB
);
4148 // StrConv(string, conversion, LCID)
4149 void SbRtl_StrConv(StarBASIC
*, SbxArray
& rPar
, bool)
4151 const sal_uInt32 nArgCount
= rPar
.Count() - 1;
4152 if( nArgCount
< 2 || nArgCount
> 3 )
4154 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4158 OUString aOldStr
= rPar
.Get(1)->GetOUString();
4159 sal_Int32 nConversion
= rPar
.Get(2)->GetLong();
4160 LanguageType nLanguage
= LANGUAGE_SYSTEM
;
4163 sal_Int32 lcid
= rPar
.Get(3)->GetLong();
4164 nLanguage
= LanguageType(lcid
);
4166 OUString sLanguage
= LanguageTag(nLanguage
).getLanguage();
4167 rtl_TextEncoding encodingVal
= utl_getWinTextEncodingFromLangStr(sLanguage
);
4169 sal_Int32 nOldLen
= aOldStr
.getLength();
4172 // null string,return
4173 rPar
.Get(0)->PutString(aOldStr
);
4177 TransliterationFlags nType
= TransliterationFlags::NONE
;
4178 if ( (nConversion
& 0x03) == 3 ) // vbProperCase
4180 const CharClass
& rCharClass
= GetCharClass();
4181 aOldStr
= rCharClass
.titlecase( aOldStr
.toAsciiLowerCase(), 0, nOldLen
);
4183 else if ( (nConversion
& 0x01) == 1 ) // vbUpperCase
4185 nType
|= TransliterationFlags::LOWERCASE_UPPERCASE
;
4187 else if ( (nConversion
& 0x02) == 2 ) // vbLowerCase
4189 nType
|= TransliterationFlags::UPPERCASE_LOWERCASE
;
4191 if ( (nConversion
& 0x04) == 4 ) // vbWide
4193 nType
|= TransliterationFlags::HALFWIDTH_FULLWIDTH
;
4195 else if ( (nConversion
& 0x08) == 8 ) // vbNarrow
4197 nType
|= TransliterationFlags::FULLWIDTH_HALFWIDTH
;
4199 if ( (nConversion
& 0x10) == 16) // vbKatakana
4201 nType
|= TransliterationFlags::HIRAGANA_KATAKANA
;
4203 else if ( (nConversion
& 0x20) == 32 ) // vbHiragana
4205 nType
|= TransliterationFlags::KATAKANA_HIRAGANA
;
4207 OUString
aNewStr( aOldStr
);
4208 if( nType
!= TransliterationFlags::NONE
)
4210 uno::Reference
< uno::XComponentContext
> xContext
= getProcessComponentContext();
4211 ::utl::TransliterationWrapper
aTransliterationWrapper( xContext
, nType
);
4212 uno::Sequence
<sal_Int32
> aOffsets
;
4213 aTransliterationWrapper
.loadModuleIfNeeded( nLanguage
);
4214 aNewStr
= aTransliterationWrapper
.transliterate( aOldStr
, nLanguage
, 0, nOldLen
, &aOffsets
);
4217 if ( (nConversion
& 0x40) == 64 ) // vbUnicode
4219 // convert the string to byte string, preserving unicode (2 bytes per character)
4220 sal_Int32 nSize
= aNewStr
.getLength()*2;
4221 const sal_Unicode
* pSrc
= aNewStr
.getStr();
4222 std::unique_ptr
<char[]> pChar(new char[nSize
+1]);
4223 for( sal_Int32 i
=0; i
< nSize
; i
++ )
4225 pChar
[i
] = static_cast< char >( (i
%2) ? ((*pSrc
) >> 8) & 0xff : (*pSrc
) & 0xff );
4231 pChar
[nSize
] = '\0';
4232 OString
aOStr(pChar
.get());
4234 // there is no concept about default codepage in unix. so it is incorrectly in unix
4235 OUString aOUStr
= OStringToOUString(aOStr
, encodingVal
);
4236 rPar
.Get(0)->PutString(aOUStr
);
4239 else if ( (nConversion
& 0x80) == 128 ) // vbFromUnicode
4241 // there is no concept about default codepage in unix. so it is incorrectly in unix
4242 OString aOStr
= OUStringToOString(aNewStr
, encodingVal
);
4243 const char* pChar
= aOStr
.getStr();
4244 sal_Int32 nArraySize
= aOStr
.getLength();
4245 SbxDimArray
* pArray
= new SbxDimArray(SbxBYTE
);
4246 bool bIncIndex
= IsBaseIndexOne();
4251 pArray
->AddDim(1, nArraySize
);
4255 pArray
->AddDim(0, nArraySize
- 1);
4260 pArray
->unoAddDim(0, -1);
4263 for( sal_Int32 i
=0; i
< nArraySize
; i
++)
4265 SbxVariable
* pNew
= new SbxVariable( SbxBYTE
);
4266 pNew
->PutByte(*pChar
);
4268 pNew
->SetFlag( SbxFlagBits::Write
);
4275 pArray
->Put(pNew
, aIdx
);
4278 SbxVariableRef refVar
= rPar
.Get(0);
4279 SbxFlagBits nFlags
= refVar
->GetFlags();
4280 refVar
->ResetFlag( SbxFlagBits::Fixed
);
4281 refVar
->PutObject( pArray
);
4282 refVar
->SetFlags( nFlags
);
4283 refVar
->SetParameters( nullptr );
4286 rPar
.Get(0)->PutString(aNewStr
);
4290 void SbRtl_Beep(StarBASIC
*, SbxArray
& rPar
, bool)
4292 if (rPar
.Count() != 1)
4294 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4300 void SbRtl_Load(StarBASIC
*, SbxArray
& rPar
, bool)
4302 if (rPar
.Count() != 2)
4304 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4309 SbxBase
* pObj
= rPar
.Get(1)->GetObject();
4313 if (SbUserFormModule
* pModule
= dynamic_cast<SbUserFormModule
*>(pObj
))
4317 else if (SbxObject
* pSbxObj
= dynamic_cast<SbxObject
*>(pObj
))
4319 SbxVariable
* pVar
= pSbxObj
->Find("Load", SbxClassType::Method
);
4327 void SbRtl_Unload(StarBASIC
*, SbxArray
& rPar
, bool)
4329 rPar
.Get(0)->PutEmpty();
4330 if (rPar
.Count() != 2)
4332 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4337 SbxBase
* pObj
= rPar
.Get(1)->GetObject();
4341 if (SbUserFormModule
* pFormModule
= dynamic_cast<SbUserFormModule
*>(pObj
))
4343 pFormModule
->Unload();
4345 else if (SbxObject
*pSbxObj
= dynamic_cast<SbxObject
*>(pObj
))
4347 SbxVariable
* pVar
= pSbxObj
->Find("Unload", SbxClassType::Method
);
4355 void SbRtl_LoadPicture(StarBASIC
*, SbxArray
& rPar
, bool)
4357 if (rPar
.Count() != 2)
4359 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4363 OUString aFileURL
= getFullPath(rPar
.Get(1)->GetOUString());
4364 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream( aFileURL
, StreamMode::READ
));
4368 ReadDIB(aBmp
, *pStream
, true);
4369 BitmapEx
aBitmapEx(aBmp
);
4370 Graphic
aGraphic(aBitmapEx
);
4372 SbxObjectRef xRef
= new SbStdPicture
;
4373 static_cast<SbStdPicture
*>(xRef
.get())->SetGraphic( aGraphic
);
4374 rPar
.Get(0)->PutObject(xRef
.get());
4378 void SbRtl_SavePicture(StarBASIC
*, SbxArray
& rPar
, bool)
4380 rPar
.Get(0)->PutEmpty();
4381 if (rPar
.Count() != 3)
4383 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4387 SbxBase
* pObj
= rPar
.Get(1)->GetObject();
4388 if (SbStdPicture
*pPicture
= dynamic_cast<SbStdPicture
*>(pObj
))
4390 SvFileStream
aOStream(rPar
.Get(2)->GetOUString(), StreamMode::WRITE
| StreamMode::TRUNC
);
4391 const Graphic
& aGraphic
= pPicture
->GetGraphic();
4392 TypeSerializer
aSerializer(aOStream
);
4393 aSerializer
.writeGraphic(aGraphic
);
4397 void SbRtl_MsgBox(StarBASIC
*, SbxArray
& rPar
, bool)
4399 const sal_uInt32 nArgCount
= rPar
.Count();
4400 if( nArgCount
< 2 || nArgCount
> 6 )
4402 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4405 WinBits nType
= 0; // MB_OK
4406 if( nArgCount
>= 3 )
4407 nType
= static_cast<WinBits
>(rPar
.Get(2)->GetInteger());
4408 WinBits nStyle
= nType
;
4409 nStyle
&= 15; // delete bits 4-16
4424 OUString aMsg
= rPar
.Get(1)->GetOUString();
4426 if( nArgCount
>= 4 )
4428 aTitle
= rPar
.Get(3)->GetOUString();
4432 aTitle
= Application::GetDisplayName();
4435 WinBits nDialogType
= nType
& (16+32+64);
4437 SolarMutexGuard aSolarGuard
;
4438 weld::Widget
* pParent
= Application::GetDefDialogParent();
4440 VclMessageType eType
= VclMessageType::Other
;
4442 switch (nDialogType
)
4445 eType
= VclMessageType::Error
;
4448 eType
= VclMessageType::Question
;
4451 eType
= VclMessageType::Warning
;
4454 eType
= VclMessageType::Info
;
4458 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pParent
,
4459 eType
, VclButtonsType::NONE
, aMsg
));
4465 xBox
->add_button(GetStandardText(StandardButtonType::OK
), BasicResponse::Ok
);
4467 case 1: // MB_OKCANCEL
4468 xBox
->add_button(GetStandardText(StandardButtonType::OK
), BasicResponse::Ok
);
4469 xBox
->add_button(GetStandardText(StandardButtonType::Cancel
), BasicResponse::Cancel
);
4471 if (nType
& 256 || nType
& 512)
4472 xBox
->set_default_response(BasicResponse::Cancel
);
4474 xBox
->set_default_response(BasicResponse::Ok
);
4477 case 2: // MB_ABORTRETRYIGNORE
4478 xBox
->add_button(GetStandardText(StandardButtonType::Abort
), BasicResponse::Abort
);
4479 xBox
->add_button(GetStandardText(StandardButtonType::Retry
), BasicResponse::Retry
);
4480 xBox
->add_button(GetStandardText(StandardButtonType::Ignore
), BasicResponse::Ignore
);
4483 xBox
->set_default_response(BasicResponse::Retry
);
4484 else if (nType
& 512)
4485 xBox
->set_default_response(BasicResponse::Ignore
);
4487 xBox
->set_default_response(BasicResponse::Cancel
);
4490 case 3: // MB_YESNOCANCEL
4491 xBox
->add_button(GetStandardText(StandardButtonType::Yes
), BasicResponse::Yes
);
4492 xBox
->add_button(GetStandardText(StandardButtonType::No
), BasicResponse::No
);
4493 xBox
->add_button(GetStandardText(StandardButtonType::Cancel
), BasicResponse::Cancel
);
4495 if (nType
& 256 || nType
& 512)
4496 xBox
->set_default_response(BasicResponse::Cancel
);
4498 xBox
->set_default_response(BasicResponse::Yes
);
4502 xBox
->add_button(GetStandardText(StandardButtonType::Yes
), BasicResponse::Yes
);
4503 xBox
->add_button(GetStandardText(StandardButtonType::No
), BasicResponse::No
);
4505 if (nType
& 256 || nType
& 512)
4506 xBox
->set_default_response(BasicResponse::No
);
4508 xBox
->set_default_response(BasicResponse::Yes
);
4511 case 5: // MB_RETRYCANCEL
4512 xBox
->add_button(GetStandardText(StandardButtonType::Retry
), BasicResponse::Retry
);
4513 xBox
->add_button(GetStandardText(StandardButtonType::Cancel
), BasicResponse::Cancel
);
4515 if (nType
& 256 || nType
& 512)
4516 xBox
->set_default_response(BasicResponse::Cancel
);
4518 xBox
->set_default_response(BasicResponse::Retry
);
4523 xBox
->set_title(aTitle
);
4524 sal_Int16 nRet
= xBox
->run();
4525 rPar
.Get(0)->PutInteger(nRet
);
4528 void SbRtl_SetAttr(StarBASIC
*, SbxArray
& rPar
, bool)
4530 rPar
.Get(0)->PutEmpty();
4531 if (rPar
.Count() == 3)
4533 OUString aStr
= rPar
.Get(1)->GetOUString();
4534 SbAttributes nFlags
= static_cast<SbAttributes
>(rPar
.Get(2)->GetInteger());
4538 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
4543 bool bReadOnly
= bool(nFlags
& SbAttributes::READONLY
);
4544 xSFI
->setReadOnly( aStr
, bReadOnly
);
4545 bool bHidden
= bool(nFlags
& SbAttributes::HIDDEN
);
4546 xSFI
->setHidden( aStr
, bHidden
);
4548 catch(const Exception
& )
4550 StarBASIC::Error( ERRCODE_IO_GENERAL
);
4557 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4561 void SbRtl_Reset(StarBASIC
*, SbxArray
&, bool)
4563 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
4570 void SbRtl_DumpAllObjects(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
4572 const sal_uInt32 nArgCount
= rPar
.Count();
4573 if( nArgCount
< 2 || nArgCount
> 3 )
4575 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4579 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
4583 SbxObject
* p
= pBasic
;
4584 while( p
->GetParent() )
4588 SvFileStream
aStrm(rPar
.Get(1)->GetOUString(),
4589 StreamMode::WRITE
| StreamMode::TRUNC
);
4590 p
->Dump(aStrm
, rPar
.Get(2)->GetBool());
4592 if( aStrm
.GetError() != ERRCODE_NONE
)
4594 StarBASIC::Error( ERRCODE_BASIC_IO_ERROR
);
4600 void SbRtl_FileExists(StarBASIC
*, SbxArray
& rPar
, bool)
4602 if (rPar
.Count() == 2)
4604 OUString aStr
= rPar
.Get(1)->GetOUString();
4605 bool bExists
= false;
4609 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
4614 bExists
= xSFI
->exists( aStr
);
4616 catch(const Exception
& )
4618 StarBASIC::Error( ERRCODE_IO_GENERAL
);
4624 DirectoryItem aItem
;
4625 FileBase::RC nRet
= DirectoryItem::get( getFullPath( aStr
), aItem
);
4626 bExists
= (nRet
== FileBase::E_None
);
4628 rPar
.Get(0)->PutBool(bExists
);
4632 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4636 void SbRtl_Partition(StarBASIC
*, SbxArray
& rPar
, bool)
4638 if (rPar
.Count() != 5)
4640 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4644 sal_Int32 nNumber
= rPar
.Get(1)->GetLong();
4645 sal_Int32 nStart
= rPar
.Get(2)->GetLong();
4646 sal_Int32 nStop
= rPar
.Get(3)->GetLong();
4647 sal_Int32 nInterval
= rPar
.Get(4)->GetLong();
4649 if( nStart
< 0 || nStop
<= nStart
|| nInterval
< 1 )
4651 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4655 // the Partition function inserts leading spaces before lowervalue and uppervalue
4656 // so that they both have the same number of characters as the string
4657 // representation of the value (Stop + 1). This ensures that if you use the output
4658 // of the Partition function with several values of Number, the resulting text
4659 // will be handled properly during any subsequent sort operation.
4661 // calculate the maximum number of characters before lowervalue and uppervalue
4662 OUString aBeforeStart
= OUString::number( nStart
- 1 );
4663 OUString aAfterStop
= OUString::number( nStop
+ 1 );
4664 sal_Int32 nLen1
= aBeforeStart
.getLength();
4665 sal_Int32 nLen2
= aAfterStop
.getLength();
4666 sal_Int32 nLen
= nLen1
>= nLen2
? nLen1
:nLen2
;
4668 OUStringBuffer
aRetStr( nLen
* 2 + 1);
4669 OUString aLowerValue
;
4670 OUString aUpperValue
;
4671 if( nNumber
< nStart
)
4673 aUpperValue
= aBeforeStart
;
4675 else if( nNumber
> nStop
)
4677 aLowerValue
= aAfterStop
;
4681 sal_Int32 nLowerValue
= nNumber
;
4682 sal_Int32 nUpperValue
= nLowerValue
;
4685 nLowerValue
= ((( nNumber
- nStart
) / nInterval
) * nInterval
) + nStart
;
4686 nUpperValue
= nLowerValue
+ nInterval
- 1;
4688 aLowerValue
= OUString::number( nLowerValue
);
4689 aUpperValue
= OUString::number( nUpperValue
);
4692 nLen1
= aLowerValue
.getLength();
4693 nLen2
= aUpperValue
.getLength();
4697 // appending the leading spaces for the lowervalue
4698 for ( sal_Int32 i
= nLen
- nLen1
; i
> 0; --i
)
4700 aRetStr
.append(" ");
4703 aRetStr
.append( aLowerValue
+ ":");
4706 // appending the leading spaces for the uppervalue
4707 for ( sal_Int32 i
= nLen
- nLen2
; i
> 0; --i
)
4709 aRetStr
.append(" ");
4712 aRetStr
.append( aUpperValue
);
4713 rPar
.Get(0)->PutString(aRetStr
.makeStringAndClear());
4718 static tools::Long
GetDayDiff( const Date
& rDate
)
4720 Date
aRefDate( 1,1,1900 );
4721 tools::Long nDiffDays
;
4722 if ( aRefDate
> rDate
)
4724 nDiffDays
= aRefDate
- rDate
;
4729 nDiffDays
= rDate
- aRefDate
;
4731 nDiffDays
+= 2; // adjustment VisualBasic: 1.Jan.1900 == 2
4735 sal_Int16
implGetDateYear( double aDate
)
4737 Date
aRefDate( 1,1,1900 );
4738 tools::Long nDays
= static_cast<tools::Long
>(aDate
);
4739 nDays
-= 2; // standardize: 1.1.1900 => 0.0
4740 aRefDate
.AddDays( nDays
);
4741 sal_Int16 nRet
= aRefDate
.GetYear();
4745 bool implDateSerial( sal_Int16 nYear
, sal_Int16 nMonth
, sal_Int16 nDay
,
4746 bool bUseTwoDigitYear
, SbDateCorrection eCorr
, double& rdRet
)
4748 // XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and
4749 // 30..99 can not be input as they are 2-digit for 2000..2029 and
4750 // 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always
4751 // true). For VBA years > 9999 are invalid.
4752 // For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99
4753 // can not be input as they are 2-digit for 1900..1999, years<0 are
4754 // accepted. If bUseTwoDigitYear==false then all years are accepted, but
4755 // year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01).
4756 #if HAVE_FEATURE_SCRIPTING
4757 if ( (nYear
< 0 || 9999 < nYear
) && SbiRuntime::isVBAEnabled() )
4759 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4762 else if ( nYear
< 30 && SbiRuntime::isVBAEnabled() )
4769 if ( 0 <= nYear
&& nYear
< 100 &&
4770 #if HAVE_FEATURE_SCRIPTING
4771 (bUseTwoDigitYear
|| SbiRuntime::isVBAEnabled())
4781 sal_Int32 nAddMonths
= 0;
4782 sal_Int32 nAddDays
= 0;
4783 // Always sanitize values to set date and to use for validity detection.
4784 if (nMonth
< 1 || 12 < nMonth
)
4786 sal_Int16 nM
= ((nMonth
< 1) ? (12 + (nMonth
% 12)) : (nMonth
% 12));
4787 nAddMonths
= nMonth
- nM
;
4790 // Day 0 would already be normalized during Date::Normalize(), include
4791 // it in negative days, also to detect non-validity. The actual day of
4792 // month is 1+(nDay-1)
4795 nAddDays
= nDay
- 1;
4800 nAddDays
= nDay
- 31;
4804 Date
aCurDate( nDay
, nMonth
, nYear
);
4806 /* TODO: we could enable the same rollover mechanism for StarBASIC to be
4807 * compatible with VBA (just with our wider supported date range), then
4808 * documentation would need to be adapted. As is, the DateSerial() runtime
4809 * function works as dumb as documented... (except that the resulting date
4810 * is checked for validity now and not just day<=31 and month<=12).
4811 * If change wanted then simply remove overriding RollOver here and adapt
4813 #if HAVE_FEATURE_SCRIPTING
4814 if (eCorr
== SbDateCorrection::RollOver
&& !SbiRuntime::isVBAEnabled())
4815 eCorr
= SbDateCorrection::None
;
4818 if (nYear
== 0 || (eCorr
== SbDateCorrection::None
&& (nAddMonths
|| nAddDays
|| !aCurDate
.IsValidDate())))
4820 #if HAVE_FEATURE_SCRIPTING
4821 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4826 if (eCorr
!= SbDateCorrection::None
)
4828 aCurDate
.Normalize();
4830 aCurDate
.AddMonths( nAddMonths
);
4832 aCurDate
.AddDays( nAddDays
);
4833 if (eCorr
== SbDateCorrection::TruncateToMonth
&& aCurDate
.GetMonth() != nMonth
)
4835 if (aCurDate
.GetYear() == SAL_MAX_INT16
&& nMonth
== 12)
4837 // Roll over and back not possible, hard max.
4838 aCurDate
.SetMonth(12);
4839 aCurDate
.SetDay(31);
4843 aCurDate
.SetMonth(nMonth
);
4845 aCurDate
.AddMonths(1);
4846 aCurDate
.AddDays(-1);
4851 tools::Long nDiffDays
= GetDayDiff( aCurDate
);
4852 rdRet
= static_cast<double>(nDiffDays
);
4856 double implTimeSerial( sal_Int16 nHours
, sal_Int16 nMinutes
, sal_Int16 nSeconds
)
4859 static_cast<double>( nHours
* ::tools::Time::secondPerHour
+
4860 nMinutes
* ::tools::Time::secondPerMinute
+
4863 static_cast<double>( ::tools::Time::secondPerDay
);
4866 bool implDateTimeSerial( sal_Int16 nYear
, sal_Int16 nMonth
, sal_Int16 nDay
,
4867 sal_Int16 nHour
, sal_Int16 nMinute
, sal_Int16 nSecond
,
4871 if(!implDateSerial(nYear
, nMonth
, nDay
, false/*bUseTwoDigitYear*/, SbDateCorrection::None
, dDate
))
4873 rdRet
+= dDate
+ implTimeSerial(nHour
, nMinute
, nSecond
);
4877 sal_Int16
implGetMinute( double dDate
)
4879 double nFrac
= dDate
- floor( dDate
);
4881 sal_Int32 nSeconds
= static_cast<sal_Int32
>(nFrac
+ 0.5);
4882 sal_Int16 nTemp
= static_cast<sal_Int16
>(nSeconds
% 3600);
4883 sal_Int16 nMin
= nTemp
/ 60;
4887 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */