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/svapp.hxx>
28 #include <vcl/settings.hxx>
29 #include <vcl/sound.hxx>
30 #include <vcl/wintypes.hxx>
31 #include <vcl/stdtext.hxx>
32 #include <vcl/weld.hxx>
33 #include <basic/sbx.hxx>
34 #include <svl/zforlist.hxx>
35 #include <rtl/character.hxx>
36 #include <rtl/math.hxx>
37 #include <tools/urlobj.hxx>
39 #include <unotools/charclass.hxx>
40 #include <unotools/ucbstreamhelper.hxx>
41 #include <unotools/wincodepage.hxx>
42 #include <tools/wldcrd.hxx>
43 #include <i18nlangtag/lang.h>
44 #include <rtl/string.hxx>
45 #include <sal/log.hxx>
46 #include <comphelper/DirectoryHelper.hxx>
48 #include <runtime.hxx>
49 #include <sbunoobj.hxx>
50 #include <osl/file.hxx>
51 #include <errobject.hxx>
53 #include <comphelper/string.hxx>
54 #include <comphelper/processfactory.hxx>
56 #include <com/sun/star/uno/Sequence.hxx>
57 #include <com/sun/star/util/DateTime.hpp>
58 #include <com/sun/star/lang/Locale.hpp>
59 #include <com/sun/star/lang/XServiceInfo.hpp>
60 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
61 #include <com/sun/star/script/XErrorQuery.hpp>
62 #include <ooo/vba/VbStrConv.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>
78 #include <sbstdobj.hxx>
79 #include <rtlproto.hxx>
82 #include "ddectrl.hxx"
83 #include <sbintern.hxx>
84 #include <basic/vbahelper.hxx>
92 #include <sbobjmod.hxx>
104 #include <vcl/TypeSerializer.hxx>
106 using namespace comphelper
;
108 using namespace com::sun::star
;
109 using namespace com::sun::star::lang
;
110 using namespace com::sun::star::uno
;
112 static sal_Int32
GetDayDiff(const Date
& rDate
) { return rDate
- Date(1899'12'30); }
114 #if HAVE_FEATURE_SCRIPTING
116 static sal_Int32
nanoSecToMilliSec(sal_Int64 nNanoSeconds
)
118 // Rounding nanoseconds to milliseconds precision to avoid comparison inaccuracies
119 return o3tl::convert(nNanoSeconds
, 1, tools::Time::nanoPerMilli
);
122 static void FilterWhiteSpace( OUString
& rStr
)
130 for (sal_Int32 i
= 0; i
< rStr
.getLength(); ++i
)
132 sal_Unicode cChar
= rStr
[i
];
133 if ((cChar
!= ' ') && (cChar
!= '\t') &&
134 (cChar
!= '\n') && (cChar
!= '\r'))
140 rStr
= aRet
.makeStringAndClear();
143 static const CharClass
& GetCharClass()
145 static CharClass
aCharClass( Application::GetSettings().GetLanguageTag() );
149 static bool isFolder( FileStatus::Type aType
)
151 return ( aType
== FileStatus::Directory
|| aType
== FileStatus::Volume
);
155 //*** UCB file access ***
157 // Converts possibly relative paths to absolute paths
158 // according to the setting done by ChDir/ChDrive
159 OUString
getFullPath( const OUString
& aRelPath
)
163 // #80204 Try first if it already is a valid URL
164 INetURLObject
aURLObj( aRelPath
);
165 aFileURL
= aURLObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
167 if( aFileURL
.isEmpty() )
169 File::getFileURLFromSystemPath( aRelPath
, aFileURL
);
175 // TODO: -> SbiGlobals
176 static uno::Reference
< ucb::XSimpleFileAccess3
> const & getFileAccess()
178 static uno::Reference
< ucb::XSimpleFileAccess3
> xSFI
= ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
183 // Properties and methods lie down the return value at the Get (bPut = sal_False) in the
184 // element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True)
186 // CreateObject( class )
188 void SbRtl_CreateObject(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
190 if( rPar
.Count() < 2 )
191 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
193 OUString
aClass(rPar
.Get(1)->GetOUString());
194 SbxObjectRef p
= SbxBase::CreateObject( aClass
);
196 return StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD
);
198 // Convenience: enter BASIC as parent
199 p
->SetParent( pBasic
);
200 rPar
.Get(0)->PutObject(p
.get());
205 void SbRtl_Error(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
208 return StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
211 ErrCode nErr
= ERRCODE_NONE
;
213 if (rPar
.Count() == 1)
215 nErr
= StarBASIC::GetErrBasic();
216 aErrorMsg
= StarBASIC::GetErrorMsg();
220 nCode
= rPar
.Get(1)->GetLong();
223 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
227 nErr
= StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16
>(nCode
) );
230 bool bVBA
= SbiRuntime::isVBAEnabled();
232 if( bVBA
&& !aErrorMsg
.isEmpty())
234 tmpErrMsg
= aErrorMsg
;
238 StarBASIC::MakeErrorText( nErr
, aErrorMsg
);
239 tmpErrMsg
= StarBASIC::GetErrorText();
241 // If this rtlfunc 'Error' passed an errcode the same as the active Err Objects's
242 // current err then return the description for the error message if it is set
243 // ( complicated isn't it ? )
244 if (bVBA
&& rPar
.Count() > 1)
246 uno::Reference
< ooo::vba::XErrObject
> xErrObj( SbxErrObject::getUnoErrObject() );
247 if ( xErrObj
.is() && xErrObj
->getNumber() == nCode
&& !xErrObj
->getDescription().isEmpty() )
249 tmpErrMsg
= xErrObj
->getDescription();
252 rPar
.Get(0)->PutString(tmpErrMsg
);
257 void SbRtl_Sin(StarBASIC
*, SbxArray
& rPar
, bool)
259 if (rPar
.Count() < 2)
260 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
262 SbxVariableRef pArg
= rPar
.Get(1);
263 rPar
.Get(0)->PutDouble(sin(pArg
->GetDouble()));
267 void SbRtl_Cos(StarBASIC
*, SbxArray
& rPar
, bool)
269 if (rPar
.Count() < 2)
270 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
272 SbxVariableRef pArg
= rPar
.Get(1);
273 rPar
.Get(0)->PutDouble(cos(pArg
->GetDouble()));
277 void SbRtl_Atn(StarBASIC
*, SbxArray
& rPar
, bool)
279 if (rPar
.Count() < 2)
280 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
282 SbxVariableRef pArg
= rPar
.Get(1);
283 rPar
.Get(0)->PutDouble(atan(pArg
->GetDouble()));
287 void SbRtl_Abs(StarBASIC
*, SbxArray
& rPar
, bool)
289 if (rPar
.Count() < 2)
290 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
292 SbxVariableRef pArg
= rPar
.Get(1);
293 rPar
.Get(0)->PutDouble(fabs(pArg
->GetDouble()));
297 void SbRtl_Asc(StarBASIC
*, SbxArray
& rPar
, bool)
299 if (rPar
.Count() < 2)
300 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
302 SbxVariableRef pArg
= rPar
.Get(1);
303 OUString
aStr( pArg
->GetOUString() );
306 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
307 rPar
.Get(0)->PutEmpty();
310 sal_Unicode aCh
= aStr
[0];
311 rPar
.Get(0)->PutLong(aCh
);
314 static void implChr( SbxArray
& rPar
, bool bChrW
)
316 if (rPar
.Count() < 2)
317 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
319 SbxVariableRef pArg
= rPar
.Get(1);
322 if( !bChrW
&& SbiRuntime::isVBAEnabled() )
324 char c
= static_cast<char>(pArg
->GetByte());
325 aStr
= OUString(&c
, 1, osl_getThreadTextEncoding());
329 // Map negative 16-bit values to large positive ones, so that code like Chr(&H8000)
330 // still works after the fix for tdf#62326 changed those four-digit hex notations to
331 // produce negative values:
332 sal_Int32 aCh
= pArg
->GetLong();
333 if (aCh
< -0x8000 || aCh
> 0xFFFF)
335 StarBASIC::Error(ERRCODE_BASIC_MATH_OVERFLOW
);
338 aStr
= OUString(static_cast<sal_Unicode
>(aCh
));
340 rPar
.Get(0)->PutString(aStr
);
343 void SbRtl_Chr(StarBASIC
*, SbxArray
& rPar
, bool)
345 implChr( rPar
, false/*bChrW*/ );
348 void SbRtl_ChrW(StarBASIC
*, SbxArray
& rPar
, bool)
350 implChr( rPar
, true/*bChrW*/ );
357 extern "C" void invalidParameterHandler(
358 wchar_t const * expression
, wchar_t const * function
, wchar_t const * file
, unsigned int line
,
363 "invalid parameter during _wgetdcwd; \""
364 << (expression
? OUString(o3tl::toU(expression
)) : OUString("???"))
365 << "\" (" << (function
? OUString(o3tl::toU(function
)) : OUString("???")) << ") at "
366 << (file
? OUString(o3tl::toU(file
)) : OUString("???")) << ":" << line
);
373 void SbRtl_CurDir(StarBASIC
*, SbxArray
& rPar
, bool)
375 // #57064 Although this function doesn't work with DirEntry, it isn't touched
376 // by the adjustment to virtual URLs, as, using the DirEntry-functionality,
377 // there's no possibility to detect the current one in a way that a virtual URL
378 // could be delivered.
380 if (rPar
.Count() > 2)
381 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
384 int nCurDir
= 0; // Current dir // JSM
385 if (rPar
.Count() == 2)
387 OUString aDrive
= rPar
.Get(1)->GetOUString();
388 if ( aDrive
.getLength() != 1 )
389 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
391 auto c
= rtl::toAsciiUpperCase(aDrive
[0]);
392 if ( !rtl::isAsciiUpperCase( c
) )
393 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
395 nCurDir
= c
- 'A' + 1;
397 wchar_t pBuffer
[ _MAX_PATH
];
398 // _wgetdcwd calls the C runtime's invalid parameter handler (which by default terminates the
399 // process) if nCurDir does not correspond to an existing drive, so temporarily set a "harmless"
401 auto const handler
= _set_thread_local_invalid_parameter_handler(&invalidParameterHandler
);
402 auto const ok
= _wgetdcwd( nCurDir
, pBuffer
, _MAX_PATH
) != nullptr;
403 _set_thread_local_invalid_parameter_handler(handler
);
405 return StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE
);
407 rPar
.Get(0)->PutString(OUString(o3tl::toU(pBuffer
)));
411 const int PATH_INCR
= 250;
413 int nSize
= PATH_INCR
;
414 std::unique_ptr
<char[]> pMem
;
417 pMem
.reset(new char[nSize
]);
419 return StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY
);
421 if( getcwd( pMem
.get(), nSize
-1 ) != nullptr )
423 rPar
.Get(0)->PutString(OUString::createFromAscii(pMem
.get()));
426 if( errno
!= ERANGE
)
427 return StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
435 void SbRtl_ChDir(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
437 rPar
.Get(0)->PutEmpty();
438 if (rPar
.Count() != 2)
439 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
441 // VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.)
442 if( SbiRuntime::isVBAEnabled() )
444 ::basic::vba::registerCurrentDirectory(getDocumentModel(pBasic
),
445 rPar
.Get(1)->GetOUString());
449 void SbRtl_ChDrive(StarBASIC
*, SbxArray
& rPar
, bool)
451 rPar
.Get(0)->PutEmpty();
452 if (rPar
.Count() != 2)
453 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
457 // Implementation of StepRENAME with UCB
458 void implStepRenameUCB( const OUString
& aSource
, const OUString
& aDest
)
460 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
466 OUString aSourceFullPath
= getFullPath( aSource
);
467 if( !xSFI
->exists( aSourceFullPath
) )
469 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
473 OUString aDestFullPath
= getFullPath( aDest
);
474 if( xSFI
->exists( aDestFullPath
) )
476 StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS
);
480 xSFI
->move( aSourceFullPath
, aDestFullPath
);
483 catch(const Exception
& )
485 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
489 // Implementation of StepRENAME with OSL
490 void implStepRenameOSL( const OUString
& aSource
, const OUString
& aDest
)
492 FileBase::RC nRet
= File::move( getFullPath( aSource
), getFullPath( aDest
) );
493 if( nRet
!= FileBase::E_None
)
495 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
499 void SbRtl_FileCopy(StarBASIC
*, SbxArray
& rPar
, bool)
501 rPar
.Get(0)->PutEmpty();
502 if (rPar
.Count() != 3)
503 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
505 OUString aSource
= rPar
.Get(1)->GetOUString();
506 OUString aDest
= rPar
.Get(2)->GetOUString();
509 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
514 xSFI
->copy( getFullPath( aSource
), getFullPath( aDest
) );
516 catch(const Exception
& )
518 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
524 FileBase::RC nRet
= File::copy( getFullPath( aSource
), getFullPath( aDest
) );
525 if( nRet
!= FileBase::E_None
)
527 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
532 void SbRtl_Kill(StarBASIC
*, SbxArray
& rPar
, bool)
534 rPar
.Get(0)->PutEmpty();
535 if (rPar
.Count() != 2)
536 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
538 OUString aFileSpec
= rPar
.Get(1)->GetOUString();
542 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
545 OUString aFullPath
= getFullPath( aFileSpec
);
546 if( !xSFI
->exists( aFullPath
) || xSFI
->isFolder( aFullPath
) )
548 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
553 xSFI
->kill( aFullPath
);
555 catch(const Exception
& )
557 StarBASIC::Error( ERRCODE_IO_GENERAL
);
563 File::remove( getFullPath( aFileSpec
) );
567 void SbRtl_MkDir(StarBASIC
* pBasic
, SbxArray
& rPar
, bool bWrite
)
569 rPar
.Get(0)->PutEmpty();
570 if (rPar
.Count() != 2)
571 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
573 OUString aPath
= rPar
.Get(1)->GetOUString();
574 if ( SbiRuntime::isVBAEnabled() )
576 // In vba if the full path is not specified then
577 // folder is created relative to the curdir
578 INetURLObject
aURLObj( getFullPath( aPath
) );
579 if ( aURLObj
.GetProtocol() != INetProtocol::File
)
581 SbxArrayRef pPar
= new SbxArray();
582 SbxVariableRef pResult
= new SbxVariable();
583 SbxVariableRef pParam
= new SbxVariable();
584 pPar
->Insert(pResult
.get(), pPar
->Count());
585 pPar
->Insert(pParam
.get(), pPar
->Count());
586 SbRtl_CurDir( pBasic
, *pPar
, bWrite
);
588 OUString sCurPathURL
;
589 File::getFileURLFromSystemPath(pPar
->Get(0)->GetOUString(), sCurPathURL
);
591 aURLObj
.SetURL( sCurPathURL
);
592 aURLObj
.Append( aPath
);
593 File::getSystemPathFromFileURL(aURLObj
.GetMainURL( INetURLObject::DecodeMechanism::ToIUri
),aPath
) ;
599 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
604 xSFI
->createFolder( getFullPath( aPath
) );
606 catch(const Exception
& )
608 StarBASIC::Error( ERRCODE_IO_GENERAL
);
614 Directory::create( getFullPath( aPath
) );
619 static void implRemoveDirRecursive( const OUString
& aDirPath
)
622 FileBase::RC nRet
= DirectoryItem::get( aDirPath
, aItem
);
623 bool bExists
= (nRet
== FileBase::E_None
);
625 FileStatus
aFileStatus( osl_FileStatus_Mask_Type
);
626 nRet
= aItem
.getFileStatus( aFileStatus
);
627 bool bFolder
= nRet
== FileBase::E_None
628 && isFolder( aFileStatus
.getFileType() );
630 if( !bExists
|| !bFolder
)
632 return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
635 Directory
aDir( aDirPath
);
637 if( nRet
!= FileBase::E_None
)
639 return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
643 comphelper::DirectoryHelper::deleteDirRecursively(aDirPath
);
647 void SbRtl_RmDir(StarBASIC
*, SbxArray
& rPar
, bool)
649 rPar
.Get(0)->PutEmpty();
650 if (rPar
.Count() == 2)
652 OUString aPath
= rPar
.Get(1)->GetOUString();
655 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
660 if( !xSFI
->isFolder( aPath
) )
662 return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
664 SbiInstance
* pInst
= GetSbData()->pInst
;
665 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
668 Sequence
< OUString
> aContent
= xSFI
->getFolderContents( aPath
, true );
669 if( aContent
.hasElements() )
671 return StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR
);
675 xSFI
->kill( getFullPath( aPath
) );
677 catch(const Exception
& )
679 StarBASIC::Error( ERRCODE_IO_GENERAL
);
685 implRemoveDirRecursive( getFullPath( aPath
) );
690 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
694 void SbRtl_SendKeys(StarBASIC
*, SbxArray
& rPar
, bool)
696 rPar
.Get(0)->PutEmpty();
697 StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED
);
700 void SbRtl_Exp(StarBASIC
*, SbxArray
& rPar
, bool)
702 if (rPar
.Count() < 2)
703 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
705 double aDouble
= rPar
.Get(1)->GetDouble();
706 aDouble
= exp( aDouble
);
707 checkArithmeticOverflow( aDouble
);
708 rPar
.Get(0)->PutDouble(aDouble
);
711 void SbRtl_FileLen(StarBASIC
*, SbxArray
& rPar
, bool)
713 if (rPar
.Count() < 2)
715 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
718 SbxVariableRef pArg
= rPar
.Get(1);
719 OUString
aStr( pArg
->GetOUString() );
723 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
728 nLen
= xSFI
->getSize( getFullPath( aStr
) );
730 catch(const Exception
& )
732 StarBASIC::Error( ERRCODE_IO_GENERAL
);
739 (void)DirectoryItem::get( getFullPath( aStr
), aItem
);
740 FileStatus
aFileStatus( osl_FileStatus_Mask_FileSize
);
741 (void)aItem
.getFileStatus( aFileStatus
);
742 nLen
= static_cast<sal_Int32
>(aFileStatus
.getFileSize());
744 rPar
.Get(0)->PutLong(nLen
);
749 void SbRtl_Hex(StarBASIC
*, SbxArray
& rPar
, bool)
751 if (rPar
.Count() < 2)
753 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
756 SbxVariableRef pArg
= rPar
.Get(1);
757 // converting value to unsigned and limit to 2 or 4 byte representation
758 sal_uInt32 nVal
= pArg
->IsInteger() ?
759 static_cast<sal_uInt16
>(pArg
->GetInteger()) :
760 static_cast<sal_uInt32
>(pArg
->GetLong());
761 rPar
.Get(0)->PutString(OUString::number(nVal
, 16).toAsciiUpperCase());
764 void SbRtl_FuncCaller(StarBASIC
*, SbxArray
& rPar
, bool)
766 if ( SbiRuntime::isVBAEnabled() && GetSbData()->pInst
&& GetSbData()->pInst
->pRun
)
768 if ( GetSbData()->pInst
->pRun
->GetExternalCaller() )
769 *rPar
.Get(0) = *GetSbData()->pInst
->pRun
->GetExternalCaller();
772 SbxVariableRef pVar
= new SbxVariable(SbxVARIANT
);
773 *rPar
.Get(0) = *pVar
;
778 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED
);
782 // InStr( [start],string,string,[compare] )
784 void SbRtl_InStr(StarBASIC
*, SbxArray
& rPar
, bool)
786 const sal_uInt32 nArgCount
= rPar
.Count() - 1;
788 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
791 sal_Int32 nStartPos
= 1;
792 sal_Int32 nFirstStringPos
= 1;
794 if ( nArgCount
>= 3 )
796 nStartPos
= rPar
.Get(1)->GetLong();
799 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
805 SbiInstance
* pInst
= GetSbData()->pInst
;
807 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
810 SbiRuntime
* pRT
= pInst
->pRun
;
811 bTextMode
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
817 if ( nArgCount
== 4 )
819 bTextMode
= rPar
.Get(4)->GetInteger();
822 const OUString aToken
= rPar
.Get(nFirstStringPos
+ 1)->GetOUString();
824 // #97545 Always find empty string
825 if( aToken
.isEmpty() )
831 const OUString aStr1
= rPar
.Get(nFirstStringPos
)->GetOUString();
832 const sal_Int32 nrStr1Len
= aStr1
.getLength();
833 if (nStartPos
> nrStr1Len
)
835 // Start position is greater than the string being searched
842 nPos
= aStr1
.indexOf( aToken
, nStartPos
- 1 ) + 1;
846 // tdf#139840 - case-insensitive operation for non-ASCII characters
847 i18nutil::SearchOptions2 aSearchOptions
;
848 aSearchOptions
.searchString
= aToken
;
849 aSearchOptions
.AlgorithmType2
= util::SearchAlgorithms2::ABSOLUTE
;
850 aSearchOptions
.transliterateFlags
|= TransliterationFlags::IGNORE_CASE
;
851 utl::TextSearch
textSearch(aSearchOptions
);
853 sal_Int32 nStart
= nStartPos
- 1;
854 sal_Int32 nEnd
= nrStr1Len
;
855 nPos
= textSearch
.SearchForward(aStr1
, &nStart
, &nEnd
) ? nStart
+ 1 : 0;
859 rPar
.Get(0)->PutLong(nPos
);
864 // InstrRev(string1, string2[, start[, compare]])
866 void SbRtl_InStrRev(StarBASIC
*, SbxArray
& rPar
, bool)
868 const sal_uInt32 nArgCount
= rPar
.Count() - 1;
871 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
874 const OUString aStr1
= rPar
.Get(1)->GetOUString();
875 const OUString aToken
= rPar
.Get(2)->GetOUString();
877 sal_Int32 nStartPos
= -1;
878 if ( nArgCount
>= 3 )
880 nStartPos
= rPar
.Get(3)->GetLong();
881 if( nStartPos
<= 0 && nStartPos
!= -1 )
883 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
888 SbiInstance
* pInst
= GetSbData()->pInst
;
890 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
893 SbiRuntime
* pRT
= pInst
->pRun
;
894 bTextMode
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
900 if ( nArgCount
== 4 )
902 bTextMode
= rPar
.Get(4)->GetInteger();
904 const sal_Int32 nStrLen
= aStr1
.getLength();
905 if( nStartPos
== -1 )
911 if( nStartPos
<= nStrLen
)
913 sal_Int32 nTokenLen
= aToken
.getLength();
916 // Always find empty string
919 else if( nStrLen
> 0 )
923 nPos
= aStr1
.lastIndexOf( aToken
, nStartPos
) + 1;
927 // tdf#143332 - case-insensitive operation for non-ASCII characters
928 i18nutil::SearchOptions2 aSearchOptions
;
929 aSearchOptions
.searchString
= aToken
;
930 aSearchOptions
.AlgorithmType2
= util::SearchAlgorithms2::ABSOLUTE
;
931 aSearchOptions
.transliterateFlags
|= TransliterationFlags::IGNORE_CASE
;
932 utl::TextSearch
textSearch(aSearchOptions
);
934 sal_Int32 nStart
= 0;
935 sal_Int32 nEnd
= nStartPos
;
936 nPos
= textSearch
.SearchBackward(aStr1
, &nEnd
, &nStart
) ? nStart
: 0;
940 rPar
.Get(0)->PutLong(nPos
);
948 Fix( -2.8 ) = -2.0 <- !!
951 void SbRtl_Int(StarBASIC
*, SbxArray
& rPar
, bool)
953 if (rPar
.Count() < 2)
954 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
956 SbxVariableRef pArg
= rPar
.Get(1);
957 double aDouble
= pArg
->GetDouble();
962 aDouble
= floor( aDouble
);
963 rPar
.Get(0)->PutDouble(aDouble
);
967 void SbRtl_Fix(StarBASIC
*, SbxArray
& rPar
, bool)
969 if (rPar
.Count() < 2)
970 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
972 SbxVariableRef pArg
= rPar
.Get(1);
973 double aDouble
= pArg
->GetDouble();
974 if ( aDouble
>= 0.0 )
975 aDouble
= floor( aDouble
);
977 aDouble
= ceil( aDouble
);
978 rPar
.Get(0)->PutDouble(aDouble
);
982 void SbRtl_LCase(StarBASIC
*, SbxArray
& rPar
, bool)
984 if (rPar
.Count() < 2)
985 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
987 const CharClass
& rCharClass
= GetCharClass();
988 OUString
aStr(rPar
.Get(1)->GetOUString());
989 aStr
= rCharClass
.lowercase(aStr
);
990 rPar
.Get(0)->PutString(aStr
);
993 void SbRtl_Left(StarBASIC
*, SbxArray
& rPar
, bool)
995 if (rPar
.Count() < 3)
996 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
998 OUString
aStr(rPar
.Get(1)->GetOUString());
999 sal_Int32 nResultLen
= rPar
.Get(2)->GetLong();
1000 if( nResultLen
< 0 )
1003 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1005 else if(nResultLen
> aStr
.getLength())
1007 nResultLen
= aStr
.getLength();
1009 aStr
= aStr
.copy(0, nResultLen
);
1010 rPar
.Get(0)->PutString(aStr
);
1013 void SbRtl_Log(StarBASIC
*, SbxArray
& rPar
, bool)
1015 if (rPar
.Count() < 2)
1016 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1018 double aArg
= rPar
.Get(1)->GetDouble();
1020 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1022 double d
= log( aArg
);
1023 checkArithmeticOverflow( d
);
1024 rPar
.Get(0)->PutDouble(d
);
1027 void SbRtl_LTrim(StarBASIC
*, SbxArray
& rPar
, bool)
1029 if (rPar
.Count() < 2)
1030 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1032 OUString
aStr(comphelper::string::stripStart(rPar
.Get(1)->GetOUString(), ' '));
1033 rPar
.Get(0)->PutString(aStr
);
1037 // Mid( String, nStart, nLength )
1039 void SbRtl_Mid(StarBASIC
*, SbxArray
& rPar
, bool bWrite
)
1041 int nArgCount
= rPar
.Count() - 1;
1042 if ( nArgCount
< 2 )
1044 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1048 // #23178: replicate the functionality of Mid$ as a command
1049 // by adding a replacement-string as a fourth parameter.
1050 // In contrast to the original the third parameter (nLength)
1051 // can't be left out here. That's considered in bWrite already.
1052 if( nArgCount
== 4 )
1056 OUString aArgStr
= rPar
.Get(1)->GetOUString();
1057 sal_Int32 nStartPos
= rPar
.Get(2)->GetLong();
1058 if ( nStartPos
< 1 )
1060 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1065 sal_Int32 nLen
= -1;
1066 bool bWriteNoLenParam
= false;
1067 if ( nArgCount
== 3 || bWrite
)
1069 sal_Int32 n
= rPar
.Get(3)->GetLong();
1070 if( bWrite
&& n
== -1 )
1072 bWriteNoLenParam
= true;
1078 sal_Int32 nArgLen
= aArgStr
.getLength();
1079 if( nStartPos
> nArgLen
)
1081 SbiInstance
* pInst
= GetSbData()->pInst
;
1082 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
1083 if( bCompatibility
)
1085 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1087 nStartPos
= nArgLen
;
1090 OUString aReplaceStr
= rPar
.Get(4)->GetOUString();
1091 sal_Int32 nReplaceStrLen
= aReplaceStr
.getLength();
1092 sal_Int32 nReplaceLen
;
1093 if( bWriteNoLenParam
)
1095 nReplaceLen
= nArgLen
- nStartPos
;
1100 if( nReplaceLen
< 0 || nReplaceLen
> nArgLen
- nStartPos
)
1102 nReplaceLen
= nArgLen
- nStartPos
;
1106 OUStringBuffer
aResultStr(aArgStr
);
1107 sal_Int32 nErase
= nReplaceLen
;
1108 aResultStr
.remove( nStartPos
, nErase
);
1110 nStartPos
, aReplaceStr
.getStr(), std::min(nReplaceLen
, nReplaceStrLen
));
1112 rPar
.Get(1)->PutString(aResultStr
.makeStringAndClear());
1116 OUString aResultStr
;
1117 if (nStartPos
> aArgStr
.getLength())
1121 else if(nArgCount
== 2)
1123 aResultStr
= aArgStr
.copy( nStartPos
);
1129 if(nStartPos
+ nLen
> aArgStr
.getLength())
1131 nLen
= aArgStr
.getLength() - nStartPos
;
1134 aResultStr
= aArgStr
.copy( nStartPos
, nLen
);
1136 rPar
.Get(0)->PutString(aResultStr
);
1142 void SbRtl_Oct(StarBASIC
*, SbxArray
& rPar
, bool)
1144 if (rPar
.Count() < 2)
1145 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1147 SbxVariableRef pArg
= rPar
.Get(1);
1148 // converting value to unsigned and limit to 2 or 4 byte representation
1149 sal_uInt32 nVal
= pArg
->IsInteger() ?
1150 static_cast<sal_uInt16
>(pArg
->GetInteger()) :
1151 static_cast<sal_uInt32
>(pArg
->GetLong());
1152 rPar
.Get(0)->PutString(OUString::number(nVal
, 8));
1155 // Replace(expression, find, replace[, start[, count[, compare]]])
1157 void SbRtl_Replace(StarBASIC
*, SbxArray
& rPar
, bool)
1159 const sal_uInt32 nArgCount
= rPar
.Count() - 1;
1160 if ( nArgCount
< 3 || nArgCount
> 6 )
1161 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1163 sal_Int32 lStartPos
= 1;
1166 if (rPar
.Get(4)->GetType() != SbxEMPTY
)
1168 lStartPos
= rPar
.Get(4)->GetLong();
1172 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
1175 --lStartPos
; // Make it 0-based
1177 sal_Int32 lCount
= -1;
1180 if (rPar
.Get(5)->GetType() != SbxEMPTY
)
1182 lCount
= rPar
.Get(5)->GetLong();
1186 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
1190 bool bCaseInsensitive
;
1193 bCaseInsensitive
= rPar
.Get(6)->GetInteger();
1197 SbiInstance
* pInst
= GetSbData()->pInst
;
1198 if (pInst
&& pInst
->IsCompatibility())
1200 SbiRuntime
* pRT
= pInst
->pRun
;
1201 bCaseInsensitive
= pRT
&& pRT
->IsImageFlag(SbiImageFlags::COMPARETEXT
);
1205 bCaseInsensitive
= true;
1209 const OUString aExpStr
= rPar
.Get(1)->GetOUString();
1210 OUString aFindStr
= rPar
.Get(2)->GetOUString();
1211 const OUString aReplaceStr
= rPar
.Get(3)->GetOUString();
1213 OUString
aSrcStr(aExpStr
);
1214 sal_Int32 nPrevPos
= std::min(lStartPos
, aSrcStr
.getLength());
1215 css::uno::Sequence
<sal_Int32
> aOffset
;
1216 if (bCaseInsensitive
)
1218 // tdf#132389: case-insensitive operation for non-ASCII characters
1219 // tdf#142487: use css::i18n::Transliteration to correctly handle ß -> ss expansion
1220 // tdf#132388: We can't use utl::TextSearch (css::i18n::XTextSearch), because each call to
1221 // css::i18n::XTextSearch::SearchForward transliterates input string, making
1222 // performance of repeated calls unacceptable
1223 auto xTrans
= css::i18n::Transliteration::create(comphelper::getProcessComponentContext());
1224 xTrans
->loadModule(css::i18n::TransliterationModules_IGNORE_CASE
, {});
1225 aFindStr
= xTrans
->transliterate(aFindStr
, 0, aFindStr
.getLength(), aOffset
);
1226 aSrcStr
= xTrans
->transliterate(aSrcStr
, nPrevPos
, aSrcStr
.getLength() - nPrevPos
, aOffset
);
1227 nPrevPos
= std::distance(aOffset
.begin(),
1228 std::lower_bound(aOffset
.begin(), aOffset
.end(), nPrevPos
));
1231 auto getExpStrPos
= [aOffset
, nExpLen
= aExpStr
.getLength()](sal_Int32 nSrcStrPos
) -> sal_Int32
1233 assert(!aOffset
.hasElements() || aOffset
.getLength() >= nSrcStrPos
);
1234 if (!aOffset
.hasElements())
1236 return aOffset
.getLength() > nSrcStrPos
? aOffset
[nSrcStrPos
] : nExpLen
;
1239 // Note: the result starts from lStartPos, removing everything to the left. See i#94895.
1240 OUStringBuffer
sResult(aSrcStr
.getLength() - nPrevPos
);
1241 sal_Int32 nCounts
= 0;
1242 while (lCount
== -1 || lCount
> nCounts
)
1244 sal_Int32 nPos
= aSrcStr
.indexOf(aFindStr
, nPrevPos
);
1248 lStartPos
= getExpStrPos(nPrevPos
);
1249 sResult
.append(aExpStr
.getStr() + lStartPos
, getExpStrPos(nPos
) - lStartPos
);
1250 sResult
.append(aReplaceStr
);
1251 nPrevPos
= nPos
+ aFindStr
.getLength();
1254 lStartPos
= getExpStrPos(nPrevPos
);
1255 sResult
.append(aExpStr
.getStr() + lStartPos
, aExpStr
.getLength() - lStartPos
);
1256 rPar
.Get(0)->PutString(sResult
.makeStringAndClear());
1259 void SbRtl_Right(StarBASIC
*, SbxArray
& rPar
, bool)
1261 if (rPar
.Count() < 3)
1262 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1264 const OUString aStr
= rPar
.Get(1)->GetOUString();
1265 int nResultLen
= rPar
.Get(2)->GetLong();
1266 if( nResultLen
< 0 )
1269 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1271 int nStrLen
= aStr
.getLength();
1272 if ( nResultLen
> nStrLen
)
1274 nResultLen
= nStrLen
;
1276 OUString aResultStr
= aStr
.copy( nStrLen
- nResultLen
);
1277 rPar
.Get(0)->PutString(aResultStr
);
1280 void SbRtl_RTL(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
1282 rPar
.Get(0)->PutObject(pBasic
->getRTL().get());
1285 void SbRtl_RTrim(StarBASIC
*, SbxArray
& rPar
, bool)
1287 if (rPar
.Count() < 2)
1288 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1290 OUString
aStr(comphelper::string::stripEnd(rPar
.Get(1)->GetOUString(), ' '));
1291 rPar
.Get(0)->PutString(aStr
);
1294 void SbRtl_Sgn(StarBASIC
*, SbxArray
& rPar
, bool)
1296 if (rPar
.Count() < 2)
1297 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1299 double aDouble
= rPar
.Get(1)->GetDouble();
1300 sal_Int16 nResult
= 0;
1305 else if ( aDouble
< 0 )
1309 rPar
.Get(0)->PutInteger(nResult
);
1312 void SbRtl_Space(StarBASIC
*, SbxArray
& rPar
, bool)
1314 if (rPar
.Count() < 2)
1316 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1320 const sal_Int32 nCount
= rPar
.Get(1)->GetLong();
1321 OUStringBuffer
aBuf(nCount
);
1322 string::padToLength(aBuf
, nCount
, ' ');
1323 rPar
.Get(0)->PutString(aBuf
.makeStringAndClear());
1327 void SbRtl_Sqr(StarBASIC
*, SbxArray
& rPar
, bool)
1329 if (rPar
.Count() < 2)
1331 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1335 double aDouble
= rPar
.Get(1)->GetDouble();
1338 rPar
.Get(0)->PutDouble(sqrt(aDouble
));
1342 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1347 void SbRtl_Str(StarBASIC
*, SbxArray
& rPar
, bool)
1349 if (rPar
.Count() < 2)
1351 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1356 OUString
aStrNew(u
""_ustr
);
1357 SbxVariableRef pArg
= rPar
.Get(1);
1358 pArg
->Format( aStr
);
1360 // Numbers start with a space
1361 if( pArg
->IsNumericRTL() )
1363 // replace commas by points so that it's symmetric to Val!
1364 aStr
= aStr
.replaceFirst( ",", "." );
1366 SbiInstance
* pInst
= GetSbData()->pInst
;
1367 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
1368 if( bCompatibility
)
1370 sal_Int32 nLen
= aStr
.getLength();
1372 const sal_Unicode
* pBuf
= aStr
.getStr();
1374 bool bNeg
= ( pBuf
[0] == '-' );
1375 sal_Int32 iZeroSearch
= 0;
1383 if( pBuf
[0] != ' ' )
1388 sal_Int32 iNext
= iZeroSearch
+ 1;
1389 if( pBuf
[iZeroSearch
] == '0' && nLen
> iNext
&& pBuf
[iNext
] == '.' )
1393 aStrNew
+= aStr
.subView(iZeroSearch
);
1397 aStrNew
= " " + aStr
;
1404 rPar
.Get(0)->PutString(aStrNew
);
1408 void SbRtl_StrComp(StarBASIC
*, SbxArray
& rPar
, bool)
1410 if (rPar
.Count() < 3)
1412 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1413 rPar
.Get(0)->PutEmpty();
1416 const OUString aStr1
= rPar
.Get(1)->GetOUString();
1417 const OUString aStr2
= rPar
.Get(2)->GetOUString();
1419 SbiInstance
* pInst
= GetSbData()->pInst
;
1421 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
1422 if( bCompatibility
)
1424 SbiRuntime
* pRT
= pInst
->pRun
;
1425 bTextCompare
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
1429 bTextCompare
= true;
1431 if (rPar
.Count() == 4)
1432 bTextCompare
= rPar
.Get(3)->GetInteger();
1434 if( !bCompatibility
)
1436 bTextCompare
= !bTextCompare
;
1438 sal_Int32 nRetValue
= 0;
1441 ::utl::TransliterationWrapper
* pTransliterationWrapper
= GetSbData()->pTransliterationWrapper
.get();
1442 if( !pTransliterationWrapper
)
1444 const uno::Reference
< uno::XComponentContext
>& xContext
= getProcessComponentContext();
1445 GetSbData()->pTransliterationWrapper
.reset(
1446 new ::utl::TransliterationWrapper( xContext
,
1447 TransliterationFlags::IGNORE_CASE
|
1448 TransliterationFlags::IGNORE_KANA
|
1449 TransliterationFlags::IGNORE_WIDTH
) );
1450 pTransliterationWrapper
= GetSbData()->pTransliterationWrapper
.get();
1453 LanguageType eLangType
= Application::GetSettings().GetLanguageTag().getLanguageType();
1454 pTransliterationWrapper
->loadModuleIfNeeded( eLangType
);
1455 nRetValue
= pTransliterationWrapper
->compareString( aStr1
, aStr2
);
1460 aResult
= aStr1
.compareTo( aStr2
);
1465 else if ( aResult
> 0)
1470 rPar
.Get(0)->PutInteger(sal::static_int_cast
<sal_Int16
>(nRetValue
));
1473 void SbRtl_String(StarBASIC
*, SbxArray
& rPar
, bool)
1475 if (rPar
.Count() < 2)
1477 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1481 sal_Unicode aFiller
;
1482 sal_Int32 lCount
= rPar
.Get(1)->GetLong();
1483 if( lCount
< 0 || lCount
> 0xffff )
1485 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1487 if (rPar
.Get(2)->GetType() == SbxINTEGER
)
1489 aFiller
= static_cast<sal_Unicode
>(rPar
.Get(2)->GetInteger());
1493 const OUString aStr
= rPar
.Get(2)->GetOUString();
1496 OUStringBuffer
aBuf(lCount
);
1497 string::padToLength(aBuf
, lCount
, aFiller
);
1498 rPar
.Get(0)->PutString(aBuf
.makeStringAndClear());
1502 void SbRtl_Tab(StarBASIC
*, SbxArray
& rPar
, bool)
1504 if (rPar
.Count() < 2)
1505 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1508 const sal_Int32 nCount
= std::max(rPar
.Get(1)->GetLong(), sal_Int32(0));
1509 OUStringBuffer
aStr(nCount
);
1510 comphelper::string::padToLength(aStr
, nCount
, '\t');
1511 rPar
.Get(0)->PutString(aStr
.makeStringAndClear());
1515 void SbRtl_Tan(StarBASIC
*, SbxArray
& rPar
, bool)
1517 if (rPar
.Count() < 2)
1519 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1523 SbxVariableRef pArg
= rPar
.Get(1);
1524 rPar
.Get(0)->PutDouble(tan(pArg
->GetDouble()));
1528 void SbRtl_UCase(StarBASIC
*, SbxArray
& rPar
, bool)
1530 if (rPar
.Count() < 2)
1532 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1536 const CharClass
& rCharClass
= GetCharClass();
1537 OUString
aStr(rPar
.Get(1)->GetOUString());
1538 aStr
= rCharClass
.uppercase( aStr
);
1539 rPar
.Get(0)->PutString(aStr
);
1544 void SbRtl_Val(StarBASIC
*, SbxArray
& rPar
, bool)
1546 if (rPar
.Count() < 2)
1548 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1552 double nResult
= 0.0;
1555 OUString
aStr(rPar
.Get(1)->GetOUString());
1557 FilterWhiteSpace( aStr
);
1558 if ( aStr
.getLength() > 1 && aStr
[0] == '&' )
1561 char aChar
= static_cast<char>(aStr
[1]);
1562 if ( aChar
== 'h' || aChar
== 'H' )
1566 else if ( aChar
== 'o' || aChar
== 'O' )
1572 OString
aByteStr(OUStringToOString(aStr
, osl_getThreadTextEncoding()));
1573 sal_Int16 nlResult
= static_cast<sal_Int16
>(strtol( aByteStr
.getStr()+2, &pEndPtr
, nRadix
));
1574 nResult
= static_cast<double>(nlResult
);
1579 rtl_math_ConversionStatus eStatus
= rtl_math_ConversionStatus_Ok
;
1580 sal_Int32 nParseEnd
= 0;
1581 nResult
= ::rtl::math::stringToDouble( aStr
, '.', ',', &eStatus
, &nParseEnd
);
1582 if ( eStatus
!= rtl_math_ConversionStatus_Ok
)
1583 StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW
);
1584 /* TODO: we should check whether all characters were parsed here,
1585 * but earlier code silently ignored trailing nonsense such as "1x"
1586 * resulting in 1 with the side effect that any alpha-only-string
1587 * like "x" resulted in 0. Not changing that now (2013-03-22) as
1588 * user macros may rely on it. */
1590 else if ( nParseEnd
!= aStr
.getLength() )
1591 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
1595 rPar
.Get(0)->PutDouble(nResult
);
1600 // Helper functions for date conversion
1601 sal_Int16
implGetDateDay( double aDate
)
1603 aDate
= floor( aDate
);
1604 Date
aRefDate(1899'12'30);
1605 aRefDate
.AddDays( aDate
);
1607 sal_Int16 nRet
= static_cast<sal_Int16
>( aRefDate
.GetDay() );
1611 sal_Int16
implGetDateMonth( double aDate
)
1613 Date
aRefDate(1899'12'30);
1614 sal_Int32 nDays
= static_cast<sal_Int32
>(aDate
);
1615 aRefDate
.AddDays( nDays
);
1616 sal_Int16 nRet
= static_cast<sal_Int16
>( aRefDate
.GetMonth() );
1620 css::util::Date
SbxDateToUNODate( const SbxValue
* const pVal
)
1622 double aDate
= pVal
->GetDate();
1624 css::util::Date aUnoDate
;
1625 aUnoDate
.Day
= implGetDateDay ( aDate
);
1626 aUnoDate
.Month
= implGetDateMonth( aDate
);
1627 aUnoDate
.Year
= implGetDateYear ( aDate
);
1632 void SbxDateFromUNODate( SbxValue
*pVal
, const css::util::Date
& aUnoDate
)
1635 if( implDateSerial( aUnoDate
.Year
, aUnoDate
.Month
, aUnoDate
.Day
, false, SbDateCorrection::None
, dDate
) )
1637 pVal
->PutDate( dDate
);
1641 // Function to convert date to UNO date (com.sun.star.util.Date)
1642 void SbRtl_CDateToUnoDate(StarBASIC
*, SbxArray
& rPar
, bool)
1644 if (rPar
.Count() != 2)
1646 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1649 unoToSbxValue(rPar
.Get(0), Any(SbxDateToUNODate(rPar
.Get(1))));
1652 // Function to convert date from UNO date (com.sun.star.util.Date)
1653 void SbRtl_CDateFromUnoDate(StarBASIC
*, SbxArray
& rPar
, bool)
1655 if (rPar
.Count() != 2 || rPar
.Get(1)->GetType() != SbxOBJECT
)
1657 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1660 Any
aAny(sbxToUnoValue(rPar
.Get(1), cppu::UnoType
<css::util::Date
>::get()));
1661 css::util::Date aUnoDate
;
1662 if(aAny
>>= aUnoDate
)
1663 SbxDateFromUNODate(rPar
.Get(0), aUnoDate
);
1665 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
1668 css::util::Time
SbxDateToUNOTime( const SbxValue
* const pVal
)
1670 double aDate
= pVal
->GetDate();
1672 css::util::Time aUnoTime
;
1673 aUnoTime
.Hours
= implGetHour ( aDate
);
1674 aUnoTime
.Minutes
= implGetMinute ( aDate
);
1675 aUnoTime
.Seconds
= implGetSecond ( aDate
);
1676 aUnoTime
.NanoSeconds
= implGetNanoSecond( aDate
);
1681 void SbxDateFromUNOTime( SbxValue
*pVal
, const css::util::Time
& aUnoTime
)
1683 pVal
->PutDate(implTimeSerial(aUnoTime
.Hours
, aUnoTime
.Minutes
, aUnoTime
.Seconds
,
1684 nanoSecToMilliSec(aUnoTime
.NanoSeconds
)));
1687 // Function to convert date to UNO time (com.sun.star.util.Time)
1688 void SbRtl_CDateToUnoTime(StarBASIC
*, SbxArray
& rPar
, bool)
1690 if (rPar
.Count() != 2)
1692 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1695 unoToSbxValue(rPar
.Get(0), Any(SbxDateToUNOTime(rPar
.Get(1))));
1698 // Function to convert date from UNO time (com.sun.star.util.Time)
1699 void SbRtl_CDateFromUnoTime(StarBASIC
*, SbxArray
& rPar
, bool)
1701 if (rPar
.Count() != 2 || rPar
.Get(1)->GetType() != SbxOBJECT
)
1703 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1706 Any
aAny(sbxToUnoValue(rPar
.Get(1), cppu::UnoType
<css::util::Time
>::get()));
1707 css::util::Time aUnoTime
;
1708 if(aAny
>>= aUnoTime
)
1709 SbxDateFromUNOTime(rPar
.Get(0), aUnoTime
);
1711 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
1714 css::util::DateTime
SbxDateToUNODateTime( const SbxValue
* const pVal
)
1716 double aDate
= pVal
->GetDate();
1718 css::util::DateTime aUnoDT
;
1719 aUnoDT
.Day
= implGetDateDay ( aDate
);
1720 aUnoDT
.Month
= implGetDateMonth( aDate
);
1721 aUnoDT
.Year
= implGetDateYear ( aDate
);
1722 aUnoDT
.Hours
= implGetHour ( aDate
);
1723 aUnoDT
.Minutes
= implGetMinute ( aDate
);
1724 aUnoDT
.Seconds
= implGetSecond ( aDate
);
1725 aUnoDT
.NanoSeconds
= implGetNanoSecond( aDate
);
1730 void SbxDateFromUNODateTime( SbxValue
*pVal
, const css::util::DateTime
& aUnoDT
)
1733 if (implDateTimeSerial(aUnoDT
.Year
, aUnoDT
.Month
, aUnoDT
.Day
, aUnoDT
.Hours
, aUnoDT
.Minutes
,
1734 aUnoDT
.Seconds
, nanoSecToMilliSec(aUnoDT
.NanoSeconds
), dDate
))
1736 pVal
->PutDate( dDate
);
1740 // Function to convert date to UNO date (com.sun.star.util.Date)
1741 void SbRtl_CDateToUnoDateTime(StarBASIC
*, SbxArray
& rPar
, bool)
1743 if (rPar
.Count() != 2)
1745 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1748 unoToSbxValue(rPar
.Get(0), Any(SbxDateToUNODateTime(rPar
.Get(1))));
1751 // Function to convert date from UNO date (com.sun.star.util.Date)
1752 void SbRtl_CDateFromUnoDateTime(StarBASIC
*, SbxArray
& rPar
, bool)
1754 if (rPar
.Count() != 2 || rPar
.Get(1)->GetType() != SbxOBJECT
)
1756 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1759 Any
aAny(sbxToUnoValue(rPar
.Get(1), cppu::UnoType
<css::util::DateTime
>::get()));
1760 css::util::DateTime aUnoDT
;
1762 SbxDateFromUNODateTime(rPar
.Get(0), aUnoDT
);
1764 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
1767 // Function to convert date to ISO 8601 date format YYYYMMDD
1768 void SbRtl_CDateToIso(StarBASIC
*, SbxArray
& rPar
, bool)
1770 if (rPar
.Count() == 2)
1772 double aDate
= rPar
.Get(1)->GetDate();
1774 // Date may actually even be -YYYYYMMDD
1776 sal_Int16 nYear
= implGetDateYear( aDate
);
1777 snprintf( Buffer
, sizeof( Buffer
), (nYear
< 0 ? "%05d%02d%02d" : "%04d%02d%02d"),
1778 static_cast<int>(nYear
),
1779 static_cast<int>(implGetDateMonth( aDate
)),
1780 static_cast<int>(implGetDateDay( aDate
)) );
1781 OUString aRetStr
= OUString::createFromAscii( Buffer
);
1782 rPar
.Get(0)->PutString(aRetStr
);
1786 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1790 // Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD
1791 // And even YYMMDD for compatibility, sigh...
1792 void SbRtl_CDateFromIso(StarBASIC
*, SbxArray
& rPar
, bool)
1794 if (rPar
.Count() == 2)
1798 OUString aStr
= rPar
.Get(1)->GetOUString();
1802 // Valid formats are
1803 // YYYYMMDD -YYYMMDD YYYYYMMDD -YYYYYMMDD YYMMDD
1804 // YYYY-MM-DD -YYYY-MM-DD YYYYY-MM-DD -YYYYY-MM-DD
1806 sal_Int32 nSign
= 1;
1810 aStr
= aStr
.copy(1);
1812 const sal_Int32 nLen
= aStr
.getLength();
1814 // Signed YYMMDD two digit year is invalid.
1815 if (nLen
== 6 && nSign
== -1)
1819 // YYYYMMDD YYYYYMMDD YYMMDD
1820 // YYYY-MM-DD YYYYY-MM-DD
1821 if (nLen
!= 6 && (nLen
< 8 || 11 < nLen
))
1824 bool bUseTwoDigitYear
= false;
1825 std::u16string_view aYearStr
, aMonthStr
, aDayStr
;
1826 if (nLen
== 6 || nLen
== 8 || nLen
== 9)
1829 if (!comphelper::string::isdigitAsciiString(aStr
))
1832 const sal_Int32 nMonthPos
= (nLen
== 8 ? 4 : (nLen
== 6 ? 2 : 5));
1834 bUseTwoDigitYear
= true;
1835 aYearStr
= aStr
.subView( 0, nMonthPos
);
1836 aMonthStr
= aStr
.subView( nMonthPos
, 2 );
1837 aDayStr
= aStr
.subView( nMonthPos
+ 2, 2 );
1842 const sal_Int32 nMonthSep
= (nLen
== 11 ? 5 : 4);
1843 if (aStr
.indexOf('-') != nMonthSep
)
1845 if (aStr
.indexOf('-', nMonthSep
+ 1) != nMonthSep
+ 3)
1848 aYearStr
= aStr
.subView( 0, nMonthSep
);
1849 aMonthStr
= aStr
.subView( nMonthSep
+ 1, 2 );
1850 aDayStr
= aStr
.subView( nMonthSep
+ 4, 2 );
1851 if ( !comphelper::string::isdigitAsciiString(aYearStr
) ||
1852 !comphelper::string::isdigitAsciiString(aMonthStr
) ||
1853 !comphelper::string::isdigitAsciiString(aDayStr
))
1858 if (!implDateSerial( static_cast<sal_Int16
>(nSign
* o3tl::toInt32(aYearStr
)),
1859 static_cast<sal_Int16
>(o3tl::toInt32(aMonthStr
)), static_cast<sal_Int16
>(o3tl::toInt32(aDayStr
)),
1860 bUseTwoDigitYear
, SbDateCorrection::None
, dDate
))
1863 rPar
.Get(0)->PutDate(dDate
);
1869 SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER
);
1873 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1877 void SbRtl_DateSerial(StarBASIC
*, SbxArray
& rPar
, bool)
1879 if (rPar
.Count() < 4)
1881 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1883 sal_Int16 nYear
= rPar
.Get(1)->GetInteger();
1884 sal_Int16 nMonth
= rPar
.Get(2)->GetInteger();
1885 sal_Int16 nDay
= rPar
.Get(3)->GetInteger();
1888 if( implDateSerial( nYear
, nMonth
, nDay
, true, SbDateCorrection::RollOver
, dDate
) )
1890 rPar
.Get(0)->PutDate(dDate
);
1894 void SbRtl_TimeSerial(StarBASIC
*, SbxArray
& rPar
, bool)
1896 if (rPar
.Count() < 4)
1898 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1900 sal_Int16 nHour
= rPar
.Get(1)->GetInteger();
1903 nHour
= 0; // because of UNO DateTimes, which go till 24 o'clock
1905 sal_Int16 nMinute
= rPar
.Get(2)->GetInteger();
1906 sal_Int16 nSecond
= rPar
.Get(3)->GetInteger();
1907 if ((nHour
< 0 || nHour
> 23) ||
1908 (nMinute
< 0 || nMinute
> 59 ) ||
1909 (nSecond
< 0 || nSecond
> 59 ))
1911 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1914 rPar
.Get(0)->PutDate(implTimeSerial(nHour
, nMinute
, nSecond
, 0)); // JSM
1917 void SbRtl_DateValue(StarBASIC
*, SbxArray
& rPar
, bool)
1919 if (rPar
.Count() < 2)
1921 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1925 // #39629 check GetSbData()->pInst, can be called from the URL line
1926 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
1927 if( GetSbData()->pInst
)
1929 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
1933 sal_uInt32 n
; // Dummy
1934 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, n
, n
);
1937 LanguageType eLangType
= Application::GetSettings().GetLanguageTag().getLanguageType();
1938 sal_uInt32 nIndex
= pFormatter
->GetStandardIndex( eLangType
);
1940 OUString
aStr(rPar
.Get(1)->GetOUString());
1941 bool bSuccess
= pFormatter
->IsNumberFormat( aStr
, nIndex
, fResult
);
1942 SvNumFormatType nType
= pFormatter
->GetType( nIndex
);
1944 // DateValue("February 12, 1969") raises error if the system locale is not en_US
1945 // It seems that both locale number formatter and English number
1946 // formatter are supported in Visual Basic.
1947 if( !bSuccess
&& ( eLangType
!= LANGUAGE_ENGLISH_US
) )
1949 // Try using LANGUAGE_ENGLISH_US to get the date value.
1950 nIndex
= pFormatter
->GetStandardIndex( LANGUAGE_ENGLISH_US
);
1951 bSuccess
= pFormatter
->IsNumberFormat( aStr
, nIndex
, fResult
);
1952 nType
= pFormatter
->GetType( nIndex
);
1955 if(bSuccess
&& (nType
==SvNumFormatType::DATE
|| nType
==SvNumFormatType::DATETIME
))
1957 if ( nType
== SvNumFormatType::DATETIME
)
1960 if ( fResult
> 0.0 )
1962 fResult
= floor( fResult
);
1966 fResult
= ceil( fResult
);
1969 rPar
.Get(0)->PutDate(fResult
);
1973 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
1978 void SbRtl_TimeValue(StarBASIC
*, SbxArray
& rPar
, bool)
1980 if (rPar
.Count() < 2)
1982 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1986 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
1987 if( GetSbData()->pInst
)
1988 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
1992 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, n
, n
);
1995 sal_uInt32 nIndex
= 0;
1997 bool bSuccess
= pFormatter
->IsNumberFormat(rPar
.Get(1)->GetOUString(),
1999 SvNumFormatType nType
= pFormatter
->GetType(nIndex
);
2000 if(bSuccess
&& (nType
==SvNumFormatType::TIME
||nType
==SvNumFormatType::DATETIME
))
2002 if ( nType
== SvNumFormatType::DATETIME
)
2005 fResult
= fmod( fResult
, 1 );
2007 rPar
.Get(0)->PutDate(fResult
);
2011 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
2016 void SbRtl_Day(StarBASIC
*, SbxArray
& rPar
, bool)
2018 if (rPar
.Count() < 2)
2020 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2024 SbxVariableRef pArg
= rPar
.Get(1);
2025 double aDate
= pArg
->GetDate();
2027 sal_Int16 nDay
= implGetDateDay( aDate
);
2028 rPar
.Get(0)->PutInteger(nDay
);
2032 void SbRtl_Year(StarBASIC
*, SbxArray
& rPar
, bool)
2034 if (rPar
.Count() < 2)
2036 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2040 sal_Int16 nYear
= implGetDateYear(rPar
.Get(1)->GetDate());
2041 rPar
.Get(0)->PutInteger(nYear
);
2045 sal_Int16
implGetHour( double dDate
)
2047 double nFrac
= (dDate
- floor(dDate
)) * ::tools::Time::milliSecPerDay
;
2048 sal_uInt64 nMilliSeconds
= static_cast<sal_uInt64
>(nFrac
+ 0.5);
2049 return static_cast<sal_Int16
>((nMilliSeconds
/ ::tools::Time::milliSecPerHour
)
2050 % ::tools::Time::hourPerDay
);
2053 void SbRtl_Hour(StarBASIC
*, SbxArray
& rPar
, bool)
2055 if (rPar
.Count() < 2)
2057 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2061 double nArg
= rPar
.Get(1)->GetDate();
2062 sal_Int16 nHour
= implGetHour( nArg
);
2063 rPar
.Get(0)->PutInteger(nHour
);
2067 void SbRtl_Minute(StarBASIC
*, SbxArray
& rPar
, bool)
2069 if (rPar
.Count() < 2)
2071 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2075 double nArg
= rPar
.Get(1)->GetDate();
2076 sal_Int16 nMin
= implGetMinute( nArg
);
2077 rPar
.Get(0)->PutInteger(nMin
);
2081 void SbRtl_Month(StarBASIC
*, SbxArray
& rPar
, bool)
2083 if (rPar
.Count() < 2)
2085 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2089 sal_Int16 nMonth
= implGetDateMonth(rPar
.Get(1)->GetDate());
2090 rPar
.Get(0)->PutInteger(nMonth
);
2094 sal_Int16
implGetSecond( double dDate
)
2096 double nFrac
= (dDate
- floor(dDate
)) * ::tools::Time::milliSecPerDay
;
2097 sal_uInt64 nMilliSeconds
= static_cast<sal_uInt64
>(nFrac
+ 0.5);
2098 return static_cast<sal_Int16
>((nMilliSeconds
/ ::tools::Time::milliSecPerSec
)
2099 % ::tools::Time::secondPerMinute
);
2102 sal_Int32
implGetNanoSecond(double dDate
)
2104 double nFrac
= (dDate
- floor(dDate
)) * ::tools::Time::milliSecPerDay
;
2105 sal_uInt64 nMilliSeconds
= static_cast<sal_uInt64
>(nFrac
+ 0.5);
2106 nMilliSeconds
%= ::tools::Time::milliSecPerSec
;
2108 return static_cast<sal_Int32
>(nMilliSeconds
* ::tools::Time ::nanoPerMilli
);
2111 void SbRtl_Second(StarBASIC
*, SbxArray
& rPar
, bool)
2113 if (rPar
.Count() < 2)
2115 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2119 double nArg
= rPar
.Get(1)->GetDate();
2120 sal_Int16 nSecond
= implGetSecond( nArg
);
2121 rPar
.Get(0)->PutInteger(nSecond
);
2127 // tdf#161469 - align implementation with the now function in calc, i.e., include subseconds
2128 DateTime
aActTime(DateTime::SYSTEM
);
2129 return static_cast<double>(GetDayDiff(aActTime
))
2130 + implTimeSerial(aActTime
.GetHour(), aActTime
.GetMin(), aActTime
.GetSec(),
2131 nanoSecToMilliSec(aActTime
.GetNanoSec()));
2136 void SbRtl_Now(StarBASIC
*, SbxArray
& rPar
, bool) { rPar
.Get(0)->PutDate(Now_Impl()); }
2140 void SbRtl_Time(StarBASIC
*, SbxArray
& rPar
, bool bWrite
)
2144 tools::Time
aTime( tools::Time::SYSTEM
);
2145 SbxVariable
* pMeth
= rPar
.Get(0);
2147 if( pMeth
->IsFixed() )
2151 snprintf( buf
, sizeof(buf
), "%02d:%02d:%02d",
2152 aTime
.GetHour(), aTime
.GetMin(), aTime
.GetSec() );
2153 aRes
= OUString::createFromAscii( buf
);
2157 // Time: system dependent
2158 tools::Long nSeconds
=aTime
.GetHour();
2160 nSeconds
+= aTime
.GetMin() * 60;
2161 nSeconds
+= aTime
.GetSec();
2162 double nDays
= static_cast<double>(nSeconds
) * ( 1.0 / (24.0*3600.0) );
2165 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2167 if( GetSbData()->pInst
)
2169 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2170 nIndex
= GetSbData()->pInst
->GetStdTimeIdx();
2174 sal_uInt32 n
; // Dummy
2175 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, nIndex
, n
);
2178 pFormatter
->GetOutputString( nDays
, nIndex
, aRes
, &pCol
);
2180 pMeth
->PutString( aRes
);
2184 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED
);
2188 void SbRtl_Timer(StarBASIC
*, SbxArray
& rPar
, bool)
2190 tools::Time
aTime( tools::Time::SYSTEM
);
2191 tools::Long nSeconds
= aTime
.GetHour();
2193 nSeconds
+= aTime
.GetMin() * 60;
2194 nSeconds
+= aTime
.GetSec();
2195 rPar
.Get(0)->PutDate(static_cast<double>(nSeconds
));
2199 void SbRtl_Date(StarBASIC
*, SbxArray
& rPar
, bool bWrite
)
2203 Date
aToday( Date::SYSTEM
);
2204 double nDays
= static_cast<double>(GetDayDiff( aToday
));
2205 SbxVariable
* pMeth
= rPar
.Get(0);
2206 if( pMeth
->IsString() )
2211 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2213 if( GetSbData()->pInst
)
2215 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2216 nIndex
= GetSbData()->pInst
->GetStdDateIdx();
2221 pFormatter
= SbiInstance::PrepareNumberFormatter( nIndex
, n
, n
);
2224 pFormatter
->GetOutputString( nDays
, nIndex
, aRes
, &pCol
);
2225 pMeth
->PutString( aRes
);
2229 pMeth
->PutDate( nDays
);
2234 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED
);
2238 void SbRtl_IsArray(StarBASIC
*, SbxArray
& rPar
, bool)
2240 if (rPar
.Count() != 2)
2241 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2243 rPar
.Get(0)->PutBool((rPar
.Get(1)->GetType() & SbxARRAY
) != 0);
2246 void SbRtl_IsObject(StarBASIC
*, SbxArray
& rPar
, bool)
2248 if (rPar
.Count() != 2)
2249 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2251 SbxVariable
* pVar
= rPar
.Get(1);
2252 bool bObject
= pVar
->IsObject();
2253 SbxBase
* pObj
= (bObject
? pVar
->GetObject() : nullptr);
2255 if( auto pUnoClass
= dynamic_cast<SbUnoClass
*>( pObj
) )
2257 bObject
= pUnoClass
->getUnoClass().is();
2259 rPar
.Get(0)->PutBool(bObject
);
2262 void SbRtl_IsDate(StarBASIC
*, SbxArray
& rPar
, bool)
2264 if (rPar
.Count() != 2)
2265 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2267 // #46134 only string is converted, all other types result in sal_False
2268 SbxVariableRef xArg
= rPar
.Get(1);
2269 SbxDataType eType
= xArg
->GetType();
2272 if( eType
== SbxDATE
)
2276 else if( eType
== SbxSTRING
)
2278 ErrCode nPrevError
= SbxBase::GetError();
2279 SbxBase::ResetError();
2281 // force conversion of the parameter to SbxDATE
2282 xArg
->SbxValue::GetDate();
2284 bDate
= !SbxBase::IsError();
2286 SbxBase::ResetError();
2287 SbxBase::SetError( nPrevError
);
2289 rPar
.Get(0)->PutBool(bDate
);
2292 void SbRtl_IsEmpty(StarBASIC
*, SbxArray
& rPar
, bool)
2294 if (rPar
.Count() != 2)
2295 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2297 SbxVariable
* pVar
= nullptr;
2298 if( SbiRuntime::isVBAEnabled() )
2300 pVar
= getDefaultProp(rPar
.Get(1));
2304 pVar
->Broadcast( SfxHintId::BasicDataWanted
);
2305 rPar
.Get(0)->PutBool(pVar
->IsEmpty());
2309 rPar
.Get(0)->PutBool(rPar
.Get(1)->IsEmpty());
2313 void SbRtl_IsError(StarBASIC
*, SbxArray
& rPar
, bool)
2315 if (rPar
.Count() != 2)
2316 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2318 SbxVariable
* pVar
= rPar
.Get(1);
2319 SbUnoObject
* pObj
= dynamic_cast<SbUnoObject
*>( pVar
);
2322 if ( SbxBase
* pBaseObj
= (pVar
->IsObject() ? pVar
->GetObject() : nullptr) )
2324 pObj
= dynamic_cast<SbUnoObject
*>( pBaseObj
);
2327 uno::Reference
< script::XErrorQuery
> xError
;
2330 xError
.set( pObj
->getUnoAny(), uno::UNO_QUERY
);
2334 rPar
.Get(0)->PutBool(xError
->hasError());
2338 rPar
.Get(0)->PutBool(rPar
.Get(1)->IsErr());
2342 void SbRtl_IsNull(StarBASIC
*, SbxArray
& rPar
, bool)
2344 if (rPar
.Count() != 2)
2345 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2347 // #51475 because of Uno-objects return true
2348 // even if the pObj value is NULL
2349 SbxVariableRef pArg
= rPar
.Get(1);
2350 bool bNull
= rPar
.Get(1)->IsNull();
2351 if( !bNull
&& pArg
->GetType() == SbxOBJECT
)
2353 SbxBase
* pObj
= pArg
->GetObject();
2359 rPar
.Get(0)->PutBool(bNull
);
2362 void SbRtl_IsNumeric(StarBASIC
*, SbxArray
& rPar
, bool)
2364 if (rPar
.Count() != 2)
2365 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2367 rPar
.Get(0)->PutBool(rPar
.Get(1)->IsNumericRTL());
2371 void SbRtl_IsMissing(StarBASIC
*, SbxArray
& rPar
, bool)
2373 if (rPar
.Count() != 2)
2374 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2376 // #57915 Missing is reported by an error
2377 rPar
.Get(0)->PutBool(rPar
.Get(1)->IsErr());
2380 // Function looks for wildcards, removes them and always returns the pure path
2381 static OUString
implSetupWildcard(const OUString
& rFileParam
, SbiRTLData
& rRTLData
)
2383 static const char cDelim1
= '/';
2384 static const char cDelim2
= '\\';
2385 static const char cWild1
= '*';
2386 static const char cWild2
= '?';
2388 rRTLData
.moWildCard
.reset();
2389 rRTLData
.sFullNameToBeChecked
.clear();
2391 OUString aFileParam
= rFileParam
;
2392 sal_Int32 nLastWild
= aFileParam
.lastIndexOf( cWild1
);
2395 nLastWild
= aFileParam
.lastIndexOf( cWild2
);
2397 bool bHasWildcards
= ( nLastWild
>= 0 );
2400 sal_Int32 nLastDelim
= aFileParam
.lastIndexOf( cDelim1
);
2401 if( nLastDelim
< 0 )
2403 nLastDelim
= aFileParam
.lastIndexOf( cDelim2
);
2407 // Wildcards in path?
2408 if( nLastDelim
>= 0 && nLastDelim
> nLastWild
)
2415 OUString aPathStr
= getFullPath( aFileParam
);
2416 if( nLastDelim
!= aFileParam
.getLength() - 1 )
2418 rRTLData
.sFullNameToBeChecked
= aPathStr
;
2423 OUString aPureFileName
;
2424 if( nLastDelim
< 0 )
2426 aPureFileName
= aFileParam
;
2431 aPureFileName
= aFileParam
.copy( nLastDelim
+ 1 );
2432 aFileParam
= aFileParam
.copy( 0, nLastDelim
);
2435 // Try again to get a valid URL/UNC-path with only the path
2436 OUString aPathStr
= getFullPath( aFileParam
);
2438 // Is there a pure file name left? Otherwise the path is
2439 // invalid anyway because it was not accepted by OSL before
2440 if (aPureFileName
!= "*")
2442 rRTLData
.moWildCard
.emplace(aPureFileName
);
2447 static bool implCheckWildcard(std::u16string_view rName
, SbiRTLData
const& rRTLData
)
2451 if (rRTLData
.moWildCard
)
2453 bMatch
= rRTLData
.moWildCard
->Matches(rName
);
2459 static bool isRootDir( std::u16string_view aDirURLStr
)
2461 INetURLObject
aDirURLObj( aDirURLStr
);
2464 // Check if it's a root directory
2465 sal_Int32 nCount
= aDirURLObj
.getSegmentCount();
2467 // No segment means Unix root directory "file:///"
2472 // Exactly one segment needs further checking, because it
2473 // can be Unix "file:///foo/" -> no root
2474 // or Windows "file:///c:/" -> root
2475 else if( nCount
== 1 )
2477 OUString aSeg1
= aDirURLObj
.getName( 0, true,
2478 INetURLObject::DecodeMechanism::WithCharset
);
2479 if( aSeg1
[1] == ':' )
2484 // More than one segments can never be root
2485 // so bRoot remains false
2490 void SbRtl_Dir(StarBASIC
*, SbxArray
& rPar
, bool)
2494 const sal_uInt32 nParCount
= rPar
.Count();
2497 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2501 SbiRTLData
& rRTLData
= GetSbData()->pInst
->GetRTLData();
2505 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
2508 if ( nParCount
>= 2 )
2510 OUString aFileParam
= rPar
.Get(1)->GetOUString();
2512 OUString aFileURLStr
= implSetupWildcard(aFileParam
, rRTLData
);
2513 if (!rRTLData
.sFullNameToBeChecked
.isEmpty())
2515 bool bExists
= false;
2516 try { bExists
= xSFI
->exists( aFileURLStr
); }
2517 catch(const Exception
& ) {}
2519 OUString aNameOnlyStr
;
2522 INetURLObject
aFileURL( aFileURLStr
);
2523 aNameOnlyStr
= aFileURL
.getName( INetURLObject::LAST_SEGMENT
,
2524 true, INetURLObject::DecodeMechanism::WithCharset
);
2526 rPar
.Get(0)->PutString(aNameOnlyStr
);
2532 OUString aDirURLStr
;
2533 bool bFolder
= xSFI
->isFolder( aFileURLStr
);
2537 aDirURLStr
= aFileURLStr
;
2541 rPar
.Get(0)->PutString(u
""_ustr
);
2544 sal_Int16 nFlags
= SbAttributes::NORMAL
;
2545 if ( nParCount
> 2 )
2547 rRTLData
.nDirFlags
= nFlags
= rPar
.Get(2)->GetInteger();
2551 rRTLData
.nDirFlags
= SbAttributes::NORMAL
;
2554 bool bIncludeFolders
= bool(nFlags
& SbAttributes::DIRECTORY
);
2555 rRTLData
.aDirSeq
= xSFI
->getFolderContents(aDirURLStr
, bIncludeFolders
);
2556 rRTLData
.nCurDirPos
= 0;
2558 // #78651 Add "." and ".." directories for VB compatibility
2559 if( bIncludeFolders
)
2561 bool bRoot
= isRootDir( aDirURLStr
);
2563 // If it's no root directory we flag the need for
2564 // the "." and ".." directories by the value -2
2565 // for the actual position. Later for -2 will be
2566 // returned "." and for -1 ".."
2569 rRTLData
.nCurDirPos
= -2;
2573 catch(const Exception
& )
2579 if (rRTLData
.aDirSeq
.hasElements())
2581 bool bFolderFlag
= bool(rRTLData
.nDirFlags
& SbAttributes::DIRECTORY
);
2583 SbiInstance
* pInst
= GetSbData()->pInst
;
2584 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
2587 if (rRTLData
.nCurDirPos
< 0)
2589 if (rRTLData
.nCurDirPos
== -2)
2593 else if (rRTLData
.nCurDirPos
== -1)
2597 rRTLData
.nCurDirPos
++;
2599 else if (rRTLData
.nCurDirPos
>= rRTLData
.aDirSeq
.getLength())
2601 rRTLData
.aDirSeq
.realloc(0);
2608 = rRTLData
.aDirSeq
.getConstArray()[rRTLData
.nCurDirPos
++];
2610 if( bCompatibility
)
2614 bool bFolder
= xSFI
->isFolder( aFile
);
2626 bool bFolder
= xSFI
->isFolder( aFile
);
2634 INetURLObject
aURL( aFile
);
2635 aPath
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true,
2636 INetURLObject::DecodeMechanism::WithCharset
);
2639 bool bMatch
= implCheckWildcard(aPath
, rRTLData
);
2647 rPar
.Get(0)->PutString(aPath
);
2653 if ( nParCount
>= 2 )
2655 OUString aFileParam
= rPar
.Get(1)->GetOUString();
2657 OUString aDirURL
= implSetupWildcard(aFileParam
, rRTLData
);
2659 sal_Int16 nFlags
= SbAttributes::NORMAL
;
2660 if ( nParCount
> 2 )
2662 rRTLData
.nDirFlags
= nFlags
= rPar
.Get(2)->GetInteger();
2666 rRTLData
.nDirFlags
= SbAttributes::NORMAL
;
2670 bool bIncludeFolders
= bool(nFlags
& SbAttributes::DIRECTORY
);
2671 rRTLData
.pDir
= std::make_unique
<Directory
>(aDirURL
);
2672 FileBase::RC nRet
= rRTLData
.pDir
->open();
2673 if( nRet
!= FileBase::E_None
)
2675 rRTLData
.pDir
.reset();
2676 rPar
.Get(0)->PutString(OUString());
2680 // #86950 Add "." and ".." directories for VB compatibility
2681 rRTLData
.nCurDirPos
= 0;
2682 if( bIncludeFolders
)
2684 bool bRoot
= isRootDir( aDirURL
);
2686 // If it's no root directory we flag the need for
2687 // the "." and ".." directories by the value -2
2688 // for the actual position. Later for -2 will be
2689 // returned "." and for -1 ".."
2692 rRTLData
.nCurDirPos
= -2;
2700 bool bFolderFlag
= bool(rRTLData
.nDirFlags
& SbAttributes::DIRECTORY
);
2703 if (rRTLData
.nCurDirPos
< 0)
2705 if (rRTLData
.nCurDirPos
== -2)
2709 else if (rRTLData
.nCurDirPos
== -1)
2713 rRTLData
.nCurDirPos
++;
2717 DirectoryItem aItem
;
2718 FileBase::RC nRet
= rRTLData
.pDir
->getNextItem(aItem
);
2719 if( nRet
!= FileBase::E_None
)
2721 rRTLData
.pDir
.reset();
2727 FileStatus
aFileStatus( osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_FileName
);
2728 nRet
= aItem
.getFileStatus( aFileStatus
);
2729 if( nRet
!= FileBase::E_None
)
2731 SAL_WARN("basic", "getFileStatus failed");
2735 // Only directories?
2738 FileStatus::Type aType
= aFileStatus
.getFileType();
2739 bool bFolder
= isFolder( aType
);
2746 aPath
= aFileStatus
.getFileName();
2749 bool bMatch
= implCheckWildcard(aPath
, rRTLData
);
2757 rPar
.Get(0)->PutString(aPath
);
2763 void SbRtl_GetAttr(StarBASIC
*, SbxArray
& rPar
, bool)
2765 if (rPar
.Count() == 2)
2767 sal_Int16 nFlags
= SbAttributes::NORMAL
;
2769 // In Windows, we want to use Windows API to get the file attributes
2770 // for VBA interoperability.
2772 if( SbiRuntime::isVBAEnabled() )
2774 OUString aPathURL
= getFullPath(rPar
.Get(1)->GetOUString());
2776 FileBase::getSystemPathFromFileURL( aPathURL
, aPath
);
2777 DWORD nRealFlags
= GetFileAttributesW (o3tl::toW(aPath
.getStr()));
2778 if (nRealFlags
!= 0xffffffff)
2780 if (nRealFlags
== FILE_ATTRIBUTE_NORMAL
)
2784 nFlags
= static_cast<sal_Int16
>(nRealFlags
);
2788 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
2790 rPar
.Get(0)->PutInteger(nFlags
);
2798 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
2803 OUString aPath
= getFullPath(rPar
.Get(1)->GetOUString());
2804 bool bExists
= false;
2805 try { bExists
= xSFI
->exists( aPath
); }
2806 catch(const Exception
& ) {}
2809 return StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
2812 bool bReadOnly
= xSFI
->isReadOnly( aPath
);
2813 bool bHidden
= xSFI
->isHidden( aPath
);
2814 bool bDirectory
= xSFI
->isFolder( aPath
);
2817 nFlags
|= SbAttributes::READONLY
;
2821 nFlags
|= SbAttributes::HIDDEN
;
2825 nFlags
|= SbAttributes::DIRECTORY
;
2828 catch(const Exception
& )
2830 StarBASIC::Error( ERRCODE_IO_GENERAL
);
2836 DirectoryItem aItem
;
2837 (void)DirectoryItem::get(getFullPath(rPar
.Get(1)->GetOUString()), aItem
);
2838 FileStatus
aFileStatus( osl_FileStatus_Mask_Attributes
| osl_FileStatus_Mask_Type
);
2839 (void)aItem
.getFileStatus( aFileStatus
);
2840 sal_uInt64 nAttributes
= aFileStatus
.getAttributes();
2841 bool bReadOnly
= (nAttributes
& osl_File_Attribute_ReadOnly
) != 0;
2843 FileStatus::Type aType
= aFileStatus
.getFileType();
2844 bool bDirectory
= isFolder( aType
);
2847 nFlags
|= SbAttributes::READONLY
;
2851 nFlags
|= SbAttributes::DIRECTORY
;
2854 rPar
.Get(0)->PutInteger(nFlags
);
2858 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2863 void SbRtl_FileDateTime(StarBASIC
*, SbxArray
& rPar
, bool)
2865 if (rPar
.Count() != 2)
2867 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2871 OUString aPath
= rPar
.Get(1)->GetOUString();
2872 tools::Time
aTime( tools::Time::EMPTY
);
2873 Date
aDate( Date::EMPTY
);
2876 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
2881 util::DateTime aUnoDT
= xSFI
->getDateTimeModified( aPath
);
2882 aTime
= tools::Time( aUnoDT
);
2883 aDate
= Date( aUnoDT
);
2885 catch(const Exception
& )
2887 StarBASIC::Error( ERRCODE_IO_GENERAL
);
2893 bool bSuccess
= false;
2896 DirectoryItem aItem
;
2897 if (DirectoryItem::get( getFullPath( aPath
), aItem
) != FileBase::E_None
)
2900 FileStatus
aFileStatus( osl_FileStatus_Mask_ModifyTime
);
2901 if (aItem
.getFileStatus( aFileStatus
) != FileBase::E_None
)
2904 TimeValue aTimeVal
= aFileStatus
.getModifyTime();
2906 if (!osl_getDateTimeFromTimeValue( &aTimeVal
, &aDT
))
2907 // Strictly spoken this is not an i/o error but some other failure.
2910 aTime
= tools::Time( aDT
.Hours
, aDT
.Minutes
, aDT
.Seconds
, aDT
.NanoSeconds
);
2911 aDate
= Date( aDT
.Day
, aDT
.Month
, aDT
.Year
);
2917 StarBASIC::Error( ERRCODE_IO_GENERAL
);
2920 // An empty date shall not result in a formatted null-date (1899-12-30
2921 // or 1900-01-01) or even worse -0001-12-03 or some such due to how
2922 // GetDayDiff() treats things. There should be an error set in this
2923 // case anyway because of a missing file or other error above, but... so
2924 // do not even bother to use the number formatter.
2926 if (aDate
.IsEmpty())
2928 aRes
= "0000-00-00 00:00:00";
2932 double fSerial
= static_cast<double>(GetDayDiff( aDate
));
2933 tools::Long nSeconds
= aTime
.GetHour();
2935 nSeconds
+= aTime
.GetMin() * 60;
2936 nSeconds
+= aTime
.GetSec();
2937 double nDays
= static_cast<double>(nSeconds
) / (24.0*3600.0);
2942 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2944 if( GetSbData()->pInst
)
2946 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2947 nIndex
= GetSbData()->pInst
->GetStdDateTimeIdx();
2952 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, n
, nIndex
);
2955 pFormatter
->GetOutputString( fSerial
, nIndex
, aRes
, &pCol
);
2957 rPar
.Get(0)->PutString(aRes
);
2962 void SbRtl_EOF(StarBASIC
*, SbxArray
& rPar
, bool)
2964 // No changes for UCB
2965 if (rPar
.Count() != 2)
2967 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2971 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
2972 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
2973 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
2976 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
2979 SvStream
* pSvStrm
= pSbStrm
->GetStrm();
2980 if ( pSbStrm
->IsText() )
2983 (*pSvStrm
).ReadChar( cBla
); // can we read another character?
2984 beof
= pSvStrm
->eof();
2987 pSvStrm
->SeekRel( -1 );
2992 beof
= pSvStrm
->eof(); // for binary data!
2994 rPar
.Get(0)->PutBool(beof
);
2998 void SbRtl_FileAttr(StarBASIC
*, SbxArray
& rPar
, bool)
3000 // No changes for UCB
3001 // #57064 Although this function doesn't operate with DirEntry, it is
3002 // not touched by the adjustment to virtual URLs, as it only works on
3003 // already opened files and the name doesn't matter there.
3005 if (rPar
.Count() != 3)
3007 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3011 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3012 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3013 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3016 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3019 if (rPar
.Get(2)->GetInteger() == 1)
3021 nRet
= static_cast<sal_Int16
>(pSbStrm
->GetMode());
3025 nRet
= 0; // System file handle not supported
3027 rPar
.Get(0)->PutInteger(nRet
);
3030 void SbRtl_Loc(StarBASIC
*, SbxArray
& rPar
, bool)
3032 // No changes for UCB
3033 if (rPar
.Count() != 2)
3035 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3039 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3040 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3041 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3044 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3046 SvStream
* pSvStrm
= pSbStrm
->GetStrm();
3048 if( pSbStrm
->IsRandom())
3050 short nBlockLen
= pSbStrm
->GetBlockLen();
3051 nPos
= nBlockLen
? (pSvStrm
->Tell() / nBlockLen
) : 0;
3052 nPos
++; // block positions starting at 1
3054 else if ( pSbStrm
->IsText() )
3056 nPos
= pSbStrm
->GetLine();
3058 else if( pSbStrm
->IsBinary() )
3060 nPos
= pSvStrm
->Tell();
3062 else if ( pSbStrm
->IsSeq() )
3064 nPos
= ( pSvStrm
->Tell()+1 ) / 128;
3068 nPos
= pSvStrm
->Tell();
3070 rPar
.Get(0)->PutLong(static_cast<sal_Int32
>(nPos
));
3074 void SbRtl_Lof(StarBASIC
*, SbxArray
& rPar
, bool)
3076 // No changes for UCB
3077 if (rPar
.Count() != 2)
3079 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3083 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3084 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3085 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3088 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3090 SvStream
* pSvStrm
= pSbStrm
->GetStrm();
3091 sal_uInt64
const nLen
= pSvStrm
->TellEnd();
3092 rPar
.Get(0)->PutLong(static_cast<sal_Int32
>(nLen
));
3097 void SbRtl_Seek(StarBASIC
*, SbxArray
& rPar
, bool)
3099 // No changes for UCB
3100 int nArgs
= static_cast<int>(rPar
.Count());
3101 if ( nArgs
< 2 || nArgs
> 3 )
3103 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3105 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3106 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3107 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3110 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3112 SvStream
* pStrm
= pSbStrm
->GetStrm();
3114 if ( nArgs
== 2 ) // Seek-Function
3116 sal_uInt64 nPos
= pStrm
->Tell();
3117 if( pSbStrm
->IsRandom() )
3119 nPos
= nPos
/ pSbStrm
->GetBlockLen();
3121 nPos
++; // Basic counts from 1
3122 rPar
.Get(0)->PutLong(static_cast<sal_Int32
>(nPos
));
3124 else // Seek-Statement
3126 sal_Int32 nPos
= rPar
.Get(2)->GetLong();
3129 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3131 nPos
--; // Basic counts from 1, SvStreams count from 0
3132 pSbStrm
->SetExpandOnWriteTo( 0 );
3133 if ( pSbStrm
->IsRandom() )
3135 nPos
*= pSbStrm
->GetBlockLen();
3137 pStrm
->Seek( static_cast<sal_uInt64
>(nPos
) );
3138 pSbStrm
->SetExpandOnWriteTo( nPos
);
3142 void SbRtl_Format(StarBASIC
*, SbxArray
& rPar
, bool)
3144 const sal_uInt32 nArgCount
= rPar
.Count();
3145 if ( nArgCount
< 2 || nArgCount
> 3 )
3147 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3152 if( nArgCount
== 2 )
3154 rPar
.Get(1)->Format(aResult
);
3158 OUString
aFmt(rPar
.Get(2)->GetOUString());
3159 rPar
.Get(1)->Format(aResult
, &aFmt
);
3161 rPar
.Get(0)->PutString(aResult
);
3165 static bool IsMissing(SbxArray
& rPar
, const sal_uInt32 i
)
3167 const sal_uInt32 nArgCount
= rPar
.Count();
3171 SbxVariable
* aPar
= rPar
.Get(i
);
3172 return (aPar
->GetType() == SbxERROR
&& SbiRuntime::IsMissing(aPar
, 1));
3175 static sal_Int16
GetOptionalIntegerParamOrDefault(SbxArray
& rPar
, const sal_uInt32 i
,
3176 const sal_Int16 defaultValue
)
3178 return IsMissing(rPar
, i
) ? defaultValue
: rPar
.Get(i
)->GetInteger();
3181 static OUString
GetOptionalOUStringParamOrDefault(SbxArray
& rPar
, const sal_uInt32 i
,
3182 const OUString
& defaultValue
)
3184 return IsMissing(rPar
, i
) ? defaultValue
: rPar
.Get(i
)->GetOUString();
3187 static void lcl_FormatNumberPercent(SbxArray
& rPar
, bool isPercent
)
3189 const sal_uInt32 nArgCount
= rPar
.Count();
3190 if (nArgCount
< 2 || nArgCount
> 6)
3192 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3195 // The UI locale never changes -> we can use static value here
3196 static const LocaleDataWrapper
localeData(Application::GetSettings().GetUILanguageTag());
3197 sal_Int16 nNumDigitsAfterDecimal
= -1;
3198 if (nArgCount
> 2 && !rPar
.Get(2)->IsEmpty())
3200 nNumDigitsAfterDecimal
= rPar
.Get(2)->GetInteger();
3201 if (nNumDigitsAfterDecimal
< -1)
3203 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3205 else if (nNumDigitsAfterDecimal
> 255)
3206 nNumDigitsAfterDecimal
%= 256;
3208 if (nNumDigitsAfterDecimal
== -1)
3209 nNumDigitsAfterDecimal
= LocaleDataWrapper::getNumDigits();
3211 bool bIncludeLeadingDigit
= LocaleDataWrapper::isNumLeadingZero();
3212 if (nArgCount
> 3 && !rPar
.Get(3)->IsEmpty())
3214 switch (rPar
.Get(3)->GetInteger())
3216 case ooo::vba::VbTriState::vbFalse
:
3217 bIncludeLeadingDigit
= false;
3219 case ooo::vba::VbTriState::vbTrue
:
3220 bIncludeLeadingDigit
= true;
3222 case ooo::vba::VbTriState::vbUseDefault
:
3226 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3230 bool bUseParensForNegativeNumbers
= false;
3231 if (nArgCount
> 4 && !rPar
.Get(4)->IsEmpty())
3233 switch (rPar
.Get(4)->GetInteger())
3235 case ooo::vba::VbTriState::vbFalse
:
3236 case ooo::vba::VbTriState::vbUseDefault
:
3239 case ooo::vba::VbTriState::vbTrue
:
3240 bUseParensForNegativeNumbers
= true;
3243 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3247 bool bGroupDigits
= false;
3248 if (nArgCount
> 5 && !rPar
.Get(5)->IsEmpty())
3250 switch (rPar
.Get(5)->GetInteger())
3252 case ooo::vba::VbTriState::vbFalse
:
3253 case ooo::vba::VbTriState::vbUseDefault
:
3256 case ooo::vba::VbTriState::vbTrue
:
3257 bGroupDigits
= true;
3260 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3264 double fVal
= rPar
.Get(1)->GetDouble();
3267 const bool bNegative
= fVal
< 0;
3269 fVal
= fabs(fVal
); // Always work with non-negatives, to easily handle leading zero
3271 static const sal_Unicode decSep
= localeData
.getNumDecimalSep().toChar();
3272 OUStringBuffer aResult
;
3273 rtl::math::doubleToUStringBuffer(aResult
,
3274 fVal
, rtl_math_StringFormat_F
, nNumDigitsAfterDecimal
, decSep
,
3275 bGroupDigits
? localeData
.getDigitGrouping().getConstArray() : nullptr,
3276 localeData
.getNumThousandSep().toChar());
3278 if (!bIncludeLeadingDigit
&& aResult
.getLength() > 1)
3279 aResult
.stripStart('0');
3281 if (nNumDigitsAfterDecimal
> 0)
3283 const sal_Int32 nSepPos
= aResult
.indexOf(decSep
);
3285 // VBA allows up to 255 digits; rtl::math::doubleToUString outputs up to 15 digits
3286 // for ~small numbers, so pad them as appropriate.
3288 comphelper::string::padToLength(aResult
, nSepPos
+ nNumDigitsAfterDecimal
+ 1, '0');
3293 if (bUseParensForNegativeNumbers
)
3294 aResult
.insert(0, '(').append(')');
3296 aResult
.insert(0, '-');
3299 aResult
.append('%');
3300 rPar
.Get(0)->PutString(aResult
.makeStringAndClear());
3303 // https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatnumber-function
3304 void SbRtl_FormatNumber(StarBASIC
*, SbxArray
& rPar
, bool)
3306 return lcl_FormatNumberPercent(rPar
, false);
3309 // https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatpercent-function
3310 void SbRtl_FormatPercent(StarBASIC
*, SbxArray
& rPar
, bool)
3312 return lcl_FormatNumberPercent(rPar
, true);
3317 // note: BASIC does not use comphelper::random, because
3318 // Randomize(int) must be supported and should not affect non-BASIC random use
3319 struct RandomNumberGenerator
3321 std::mt19937 global_rng
;
3323 RandomNumberGenerator()
3327 std::random_device rd
;
3328 // initialises the state of the global random number generator
3329 // should only be called once.
3330 // (note, a few std::variate_generator<> (like normal) have their
3331 // own state which would need a reset as well to guarantee identical
3332 // sequence of numbers, e.g. via myrand.distribution().reset())
3333 global_rng
.seed(rd() ^ time(nullptr));
3335 catch (std::runtime_error
& e
)
3337 SAL_WARN("basic", "Using std::random_device failed: " << e
.what());
3338 global_rng
.seed(time(nullptr));
3343 RandomNumberGenerator
& theRandomNumberGenerator()
3345 static RandomNumberGenerator theGenerator
;
3346 return theGenerator
;
3351 void SbRtl_Randomize(StarBASIC
*, SbxArray
& rPar
, bool)
3353 if (rPar
.Count() > 2)
3355 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3357 if (rPar
.Count() == 2)
3359 int nSeed
= static_cast<int>(rPar
.Get(1)->GetInteger());
3360 theRandomNumberGenerator().global_rng
.seed(nSeed
);
3362 // without parameter, no need to do anything - RNG is seeded at first use
3365 void SbRtl_Rnd(StarBASIC
*, SbxArray
& rPar
, bool)
3367 if (rPar
.Count() > 2)
3369 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3373 std::uniform_real_distribution
<double> dist(0.0, 1.0);
3374 double const tmp(dist(theRandomNumberGenerator().global_rng
));
3375 rPar
.Get(0)->PutDouble(tmp
);
3380 // Syntax: Shell("Path",[ Window-Style,[ "Params", [ bSync = sal_False ]]])
3381 // WindowStyles (VBA compatible):
3384 // 10 == Full-Screen (text mode applications OS/2, WIN95, WNT)
3385 // HACK: The WindowStyle will be passed to
3386 // Application::StartApp in Creator. Format: "xxxx2"
3389 void SbRtl_Shell(StarBASIC
*, SbxArray
& rPar
, bool)
3391 const sal_uInt32 nArgCount
= rPar
.Count();
3392 if ( nArgCount
< 2 || nArgCount
> 5 )
3394 rPar
.Get(0)->PutLong(0);
3395 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3399 oslProcessOption nOptions
= osl_Process_SEARCHPATH
| osl_Process_DETACHED
;
3401 OUString aCmdLine
= rPar
.Get(1)->GetOUString();
3402 // attach additional parameters - everything must be parsed anyway
3403 if( nArgCount
>= 4 )
3405 OUString tmp
= rPar
.Get(3)->GetOUString().trim();
3408 aCmdLine
+= " " + tmp
;
3411 else if( aCmdLine
.isEmpty() )
3413 // avoid special treatment (empty list)
3416 sal_Int32 nLen
= aCmdLine
.getLength();
3418 // #55735 if there are parameters, they have to be separated
3419 // #72471 also separate the single parameters
3420 std::vector
<OUString
> aTokenVector
;
3429 if ( c
!= ' ' && c
!= '\t' )
3435 if( c
== '\"' || c
== '\'' )
3437 sal_Int32 iFoundPos
= aCmdLine
.indexOf( c
, i
+ 1 );
3441 aToken
= aCmdLine
.copy( i
);
3446 aToken
= aCmdLine
.copy( i
+ 1, (iFoundPos
- i
- 1) );
3452 sal_Int32 iFoundSpacePos
= aCmdLine
.indexOf( ' ', i
);
3453 sal_Int32 iFoundTabPos
= aCmdLine
.indexOf( '\t', i
);
3454 sal_Int32 iFoundPos
= iFoundSpacePos
>= 0 ? iFoundTabPos
>= 0 ? std::min( iFoundSpacePos
, iFoundTabPos
) : iFoundSpacePos
: -1;
3458 aToken
= aCmdLine
.copy( i
);
3463 aToken
= aCmdLine
.copy( i
, (iFoundPos
- i
) );
3468 // insert into the list
3469 aTokenVector
.push_back( aToken
);
3471 // #55735 / #72471 end
3473 sal_Int16 nWinStyle
= 0;
3474 if( nArgCount
>= 3 )
3476 nWinStyle
= rPar
.Get(2)->GetInteger();
3480 nOptions
|= osl_Process_MINIMIZED
;
3483 nOptions
|= osl_Process_MAXIMIZED
;
3486 nOptions
|= osl_Process_FULLSCREEN
;
3491 if( nArgCount
>= 5 )
3493 bSync
= rPar
.Get(4)->GetBool();
3497 nOptions
|= osl_Process_WAIT
;
3501 // #72471 work parameter(s) up
3502 std::vector
<OUString
>::const_iterator iter
= aTokenVector
.begin();
3503 OUString aOUStrProgURL
= getFullPath( *iter
);
3507 sal_uInt16 nParamCount
= sal::static_int_cast
< sal_uInt16
>(aTokenVector
.size() - 1 );
3508 std::unique_ptr
<rtl_uString
*[]> pParamList
;
3511 pParamList
.reset( new rtl_uString
*[nParamCount
]);
3512 for(int iVector
= 0; iter
!= aTokenVector
.end(); ++iVector
, ++iter
)
3514 const OUString
& rParamStr
= *iter
;
3515 pParamList
[iVector
] = nullptr;
3516 rtl_uString_assign(&(pParamList
[iVector
]), rParamStr
.pData
);
3521 bool bSucc
= osl_executeProcess(
3522 aOUStrProgURL
.pData
,
3529 &pApp
) == osl_Process_E_None
;
3531 // 53521 only free process handle on success
3534 osl_freeProcessHandle( pApp
);
3537 for(int j
= 0; j
< nParamCount
; ++j
)
3539 rtl_uString_release(pParamList
[j
]);
3544 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
3548 rPar
.Get(0)->PutLong(0);
3553 void SbRtl_VarType(StarBASIC
*, SbxArray
& rPar
, bool)
3555 if (rPar
.Count() != 2)
3557 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3561 SbxDataType eType
= rPar
.Get(1)->GetType();
3562 rPar
.Get(0)->PutInteger(static_cast<sal_Int16
>(eType
));
3566 // Exported function
3567 const OUString
& getBasicTypeName( SbxDataType eType
)
3569 static constexpr OUString pTypeNames
[] =
3571 u
"Empty"_ustr
, // SbxEMPTY
3572 u
"Null"_ustr
, // SbxNULL
3573 u
"Integer"_ustr
, // SbxINTEGER
3574 u
"Long"_ustr
, // SbxLONG
3575 u
"Single"_ustr
, // SbxSINGLE
3576 u
"Double"_ustr
, // SbxDOUBLE
3577 u
"Currency"_ustr
, // SbxCURRENCY
3578 u
"Date"_ustr
, // SbxDATE
3579 u
"String"_ustr
, // SbxSTRING
3580 u
"Object"_ustr
, // SbxOBJECT
3581 u
"Error"_ustr
, // SbxERROR
3582 u
"Boolean"_ustr
, // SbxBOOL
3583 u
"Variant"_ustr
, // SbxVARIANT
3584 u
"DataObject"_ustr
, // SbxDATAOBJECT
3585 u
"Unknown Type"_ustr
,
3586 u
"Unknown Type"_ustr
,
3587 u
"Char"_ustr
, // SbxCHAR
3588 u
"Byte"_ustr
, // SbxBYTE
3589 u
"UShort"_ustr
, // SbxUSHORT
3590 u
"ULong"_ustr
, // SbxULONG
3591 u
"Long64"_ustr
, // SbxLONG64
3592 u
"ULong64"_ustr
, // SbxULONG64
3593 u
"Int"_ustr
, // SbxINT
3594 u
"UInt"_ustr
, // SbxUINT
3595 u
"Void"_ustr
, // SbxVOID
3596 u
"HResult"_ustr
, // SbxHRESULT
3597 u
"Pointer"_ustr
, // SbxPOINTER
3598 u
"DimArray"_ustr
, // SbxDIMARRAY
3599 u
"CArray"_ustr
, // SbxCARRAY
3600 u
"Userdef"_ustr
, // SbxUSERDEF
3601 u
"Lpstr"_ustr
, // SbxLPSTR
3602 u
"Lpwstr"_ustr
, // SbxLPWSTR
3603 u
"Unknown Type"_ustr
, // SbxCoreSTRING
3604 u
"WString"_ustr
, // SbxWSTRING
3605 u
"WChar"_ustr
, // SbxWCHAR
3606 u
"Int64"_ustr
, // SbxSALINT64
3607 u
"UInt64"_ustr
, // SbxSALUINT64
3608 u
"Decimal"_ustr
, // SbxDECIMAL
3611 size_t nPos
= static_cast<size_t>(eType
) & 0x0FFF;
3612 const size_t nTypeNameCount
= std::size( pTypeNames
);
3613 if ( nPos
>= nTypeNameCount
)
3615 nPos
= nTypeNameCount
- 1;
3617 return pTypeNames
[nPos
];
3620 static OUString
getObjectTypeName( SbxVariable
* pVar
)
3622 OUString
sRet( u
"Object"_ustr
);
3625 SbxBase
* pBaseObj
= pVar
->GetObject();
3632 SbUnoObject
* pUnoObj
= dynamic_cast<SbUnoObject
*>( pVar
);
3635 pUnoObj
= dynamic_cast<SbUnoObject
*>( pBaseObj
);
3639 Any aObj
= pUnoObj
->getUnoAny();
3640 // For upstreaming unless we start to build oovbaapi by default
3641 // we need to get detect the vba-ness of the object in some
3643 // note: Automation objects do not support XServiceInfo
3644 uno::Reference
< XServiceInfo
> xServInfo( aObj
, uno::UNO_QUERY
);
3645 if ( xServInfo
.is() )
3647 // is this a VBA object ?
3648 Sequence
< OUString
> sServices
= xServInfo
->getSupportedServiceNames();
3649 if ( sServices
.hasElements() )
3651 sRet
= sServices
[ 0 ];
3656 uno::Reference
< bridge::oleautomation::XAutomationObject
> xAutoMation( aObj
, uno::UNO_QUERY
);
3657 if ( xAutoMation
.is() )
3659 uno::Reference
< script::XInvocation
> xInv( aObj
, uno::UNO_QUERY
);
3664 xInv
->getValue( u
"$GetTypeName"_ustr
) >>= sRet
;
3666 catch(const Exception
& )
3672 sal_Int32 nDot
= sRet
.lastIndexOf( '.' );
3673 if ( nDot
!= -1 && nDot
< sRet
.getLength() )
3675 sRet
= sRet
.copy( nDot
+ 1 );
3683 void SbRtl_TypeName(StarBASIC
*, SbxArray
& rPar
, bool)
3685 if (rPar
.Count() != 2)
3687 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3691 SbxDataType eType
= rPar
.Get(1)->GetType();
3692 bool bIsArray
= ( ( eType
& SbxARRAY
) != 0 );
3695 if ( SbiRuntime::isVBAEnabled() && eType
== SbxOBJECT
)
3697 aRetStr
= getObjectTypeName(rPar
.Get(1));
3701 aRetStr
= getBasicTypeName( eType
);
3707 rPar
.Get(0)->PutString(aRetStr
);
3711 void SbRtl_Len(StarBASIC
*, SbxArray
& rPar
, bool)
3713 if (rPar
.Count() != 2)
3715 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3719 const OUString aStr
= rPar
.Get(1)->GetOUString();
3720 rPar
.Get(0)->PutLong(aStr
.getLength());
3724 void SbRtl_DDEInitiate(StarBASIC
*, SbxArray
& rPar
, bool)
3726 int nArgs
= static_cast<int>(rPar
.Count());
3729 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3731 const OUString aApp
= rPar
.Get(1)->GetOUString();
3732 const OUString aTopic
= rPar
.Get(2)->GetOUString();
3734 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3736 ErrCode nDdeErr
= pDDE
->Initiate( aApp
, aTopic
, nChannel
);
3739 StarBASIC::Error( nDdeErr
);
3743 rPar
.Get(0)->PutInteger(static_cast<sal_Int16
>(nChannel
));
3747 void SbRtl_DDETerminate(StarBASIC
*, SbxArray
& rPar
, bool)
3749 rPar
.Get(0)->PutEmpty();
3750 int nArgs
= static_cast<int>(rPar
.Count());
3753 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3756 size_t nChannel
= rPar
.Get(1)->GetInteger();
3757 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3758 ErrCode nDdeErr
= pDDE
->Terminate( nChannel
);
3761 StarBASIC::Error( nDdeErr
);
3765 void SbRtl_DDETerminateAll(StarBASIC
*, SbxArray
& rPar
, bool)
3767 rPar
.Get(0)->PutEmpty();
3768 int nArgs
= static_cast<int>(rPar
.Count());
3771 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3774 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3775 ErrCode nDdeErr
= pDDE
->TerminateAll();
3778 StarBASIC::Error( nDdeErr
);
3782 void SbRtl_DDERequest(StarBASIC
*, SbxArray
& rPar
, bool)
3784 int nArgs
= static_cast<int>(rPar
.Count());
3787 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3789 size_t nChannel
= rPar
.Get(1)->GetInteger();
3790 const OUString aItem
= rPar
.Get(2)->GetOUString();
3791 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3793 ErrCode nDdeErr
= pDDE
->Request( nChannel
, aItem
, aResult
);
3796 StarBASIC::Error( nDdeErr
);
3800 rPar
.Get(0)->PutString(aResult
);
3804 void SbRtl_DDEExecute(StarBASIC
*, SbxArray
& rPar
, bool)
3806 rPar
.Get(0)->PutEmpty();
3807 int nArgs
= static_cast<int>(rPar
.Count());
3810 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3812 size_t nChannel
= rPar
.Get(1)->GetInteger();
3813 const OUString aCommand
= rPar
.Get(2)->GetOUString();
3814 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3815 ErrCode nDdeErr
= pDDE
->Execute( nChannel
, aCommand
);
3818 StarBASIC::Error( nDdeErr
);
3822 void SbRtl_DDEPoke(StarBASIC
*, SbxArray
& rPar
, bool)
3824 rPar
.Get(0)->PutEmpty();
3825 int nArgs
= static_cast<int>(rPar
.Count());
3828 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3830 size_t nChannel
= rPar
.Get(1)->GetInteger();
3831 const OUString aItem
= rPar
.Get(2)->GetOUString();
3832 const OUString aData
= rPar
.Get(3)->GetOUString();
3833 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3834 ErrCode nDdeErr
= pDDE
->Poke( nChannel
, aItem
, aData
);
3837 StarBASIC::Error( nDdeErr
);
3842 void SbRtl_FreeFile(StarBASIC
*, SbxArray
& rPar
, bool)
3844 if (rPar
.Count() != 1)
3846 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3848 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3850 while( nChannel
< CHANNELS
)
3852 SbiStream
* pStrm
= pIO
->GetStream( nChannel
);
3855 rPar
.Get(0)->PutInteger(nChannel
);
3860 StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES
);
3863 void SbRtl_LBound(StarBASIC
*, SbxArray
& rPar
, bool)
3865 const sal_uInt32 nParCount
= rPar
.Count();
3866 if ( nParCount
!= 3 && nParCount
!= 2 )
3867 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3869 SbxBase
* pParObj
= rPar
.Get(1)->GetObject();
3870 SbxDimArray
* pArr
= dynamic_cast<SbxDimArray
*>( pParObj
);
3872 return StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS
);
3874 sal_Int32 nLower
, nUpper
;
3875 short nDim
= (nParCount
== 3) ? static_cast<short>(rPar
.Get(2)->GetInteger()) : 1;
3876 if (!pArr
->GetDim(nDim
, nLower
, nUpper
))
3877 return StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE
);
3878 rPar
.Get(0)->PutLong(nLower
);
3881 void SbRtl_UBound(StarBASIC
*, SbxArray
& rPar
, bool)
3883 const sal_uInt32 nParCount
= rPar
.Count();
3884 if ( nParCount
!= 3 && nParCount
!= 2 )
3885 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3887 SbxBase
* pParObj
= rPar
.Get(1)->GetObject();
3888 SbxDimArray
* pArr
= dynamic_cast<SbxDimArray
*>( pParObj
);
3890 return StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS
);
3892 sal_Int32 nLower
, nUpper
;
3893 short nDim
= (nParCount
== 3) ? static_cast<short>(rPar
.Get(2)->GetInteger()) : 1;
3894 if (!pArr
->GetDim(nDim
, nLower
, nUpper
))
3895 return StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE
);
3896 rPar
.Get(0)->PutLong(nUpper
);
3899 void SbRtl_RGB(StarBASIC
*, SbxArray
& rPar
, bool)
3901 if (rPar
.Count() != 4)
3902 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3904 sal_Int32 nRed
= rPar
.Get(1)->GetInteger() & 0xFF;
3905 sal_Int32 nGreen
= rPar
.Get(2)->GetInteger() & 0xFF;
3906 sal_Int32 nBlue
= rPar
.Get(3)->GetInteger() & 0xFF;
3909 SbiInstance
* pInst
= GetSbData()->pInst
;
3910 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
3911 // See discussion in tdf#145725, here's the quotation from a link indicated in the bugtracker
3912 // which explains why we need to manage RGB differently according to VB compatibility
3913 // "In other words, the individual color components are stored in the opposite order one would expect.
3914 // VB stores the red color component in the low-order byte of the long integer's low-order word,
3915 // 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"
3916 if( bCompatibility
)
3918 nRGB
= (nBlue
<< 16) | (nGreen
<< 8) | nRed
;
3922 nRGB
= (nRed
<< 16) | (nGreen
<< 8) | nBlue
;
3924 rPar
.Get(0)->PutLong(nRGB
);
3927 void SbRtl_QBColor(StarBASIC
*, SbxArray
& rPar
, bool)
3929 static const sal_Int32 pRGB
[] =
3949 if (rPar
.Count() != 2)
3951 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3954 sal_Int16 nCol
= rPar
.Get(1)->GetInteger();
3955 if( nCol
< 0 || nCol
> 15 )
3957 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3959 sal_Int32 nRGB
= pRGB
[ nCol
];
3960 rPar
.Get(0)->PutLong(nRGB
);
3963 static std::vector
<sal_uInt8
> byteArray2Vec(SbxArray
* pArr
)
3965 std::vector
<sal_uInt8
> result
;
3968 const sal_uInt32 nCount
= pArr
->Count();
3969 result
.reserve(nCount
+ 1); // to avoid reallocation when padding in vbFromUnicode
3970 for (sal_uInt32 i
= 0; i
< nCount
; i
++)
3971 result
.push_back(pArr
->Get(i
)->GetByte());
3976 // Makes sure to get the byte array if passed, or the string converted to the bytes using
3977 // StringToByteArray in basic/source/sbx/sbxstr.cxx
3978 static std::vector
<sal_uInt8
> getByteArray(SbxValue
& val
)
3980 if (val
.GetFullType() == SbxOBJECT
)
3981 if (auto pObj
= val
.GetObject())
3982 if (pObj
->GetType() == (SbxARRAY
| SbxBYTE
))
3983 if (auto pArr
= dynamic_cast<SbxArray
*>(pObj
))
3984 return byteArray2Vec(pArr
);
3986 // Convert to string
3987 tools::SvRef
<SbxValue
> pStringValue(new SbxValue(SbxSTRING
));
3988 *pStringValue
= val
;
3990 // Convert string to byte array
3991 tools::SvRef
<SbxValue
> pValue(new SbxValue(SbxOBJECT
));
3992 pValue
->PutObject(new SbxArray(SbxBYTE
));
3993 *pValue
= *pStringValue
; // Does the magic of conversion of strings to byte arrays
3994 return byteArray2Vec(dynamic_cast<SbxArray
*>(pValue
->GetObject()));
3997 // StrConv(string, conversion, LCID)
3998 void SbRtl_StrConv(StarBASIC
*, SbxArray
& rPar
, bool)
4000 const sal_uInt32 nArgCount
= rPar
.Count() - 1;
4001 if( nArgCount
< 2 || nArgCount
> 3 )
4003 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4006 sal_Int32 nConversion
= rPar
.Get(2)->GetLong();
4007 LanguageType nLanguage
= LANGUAGE_SYSTEM
;
4010 sal_Int32 lcid
= rPar
.Get(3)->GetLong();
4011 nLanguage
= LanguageType(lcid
);
4014 if (nConversion
== ooo::vba::VbStrConv::vbUnicode
) // This mode does not combine
4016 // Assume that the passed byte array is encoded in the defined encoding, convert to
4017 // UTF-16 and store as string. Passed strings are converted to byte array first.
4018 auto inArray
= getByteArray(*rPar
.Get(1));
4019 std::string_view
s(reinterpret_cast<char*>(inArray
.data()), inArray
.size() / sizeof(char));
4020 const auto encoding
= utl_getWinTextEncodingFromLangStr(LanguageTag(nLanguage
).getBcp47());
4021 OUString aOUStr
= OStringToOUString(s
, encoding
);
4022 rPar
.Get(0)->PutString(aOUStr
);
4026 if (nConversion
== ooo::vba::VbStrConv::vbFromUnicode
) // This mode does not combine
4028 // Assume that the passed byte array is UTF-16-encoded (system-endian), convert to specified
4029 // encoding and store as byte array. Passed strings are converted to byte array first.
4030 auto inArray
= getByteArray(*rPar
.Get(1));
4031 while (inArray
.size() % sizeof(sal_Unicode
))
4032 inArray
.push_back('\0');
4033 std::u16string_view
s(reinterpret_cast<sal_Unicode
*>(inArray
.data()),
4034 inArray
.size() / sizeof(sal_Unicode
));
4035 const auto encoding
= utl_getWinTextEncodingFromLangStr(LanguageTag(nLanguage
).getBcp47());
4036 OString aOStr
= OUStringToOString(s
, encoding
);
4037 const sal_Int32 lb
= IsBaseIndexOne() ? 1 : 0;
4038 const sal_Int32 ub
= lb
+ aOStr
.getLength() - 1;
4039 SbxDimArray
* pArray
= new SbxDimArray(SbxBYTE
);
4040 pArray
->unoAddDim(lb
, ub
);
4042 for (sal_Int32 i
= 0; i
< aOStr
.getLength(); ++i
)
4044 SbxVariable
* pNew
= new SbxVariable(SbxBYTE
);
4045 pNew
->PutByte(aOStr
[i
]);
4046 pArray
->Put(pNew
, i
);
4049 SbxVariable
* retVar
= rPar
.Get(0);
4050 SbxFlagBits nFlags
= retVar
->GetFlags();
4051 retVar
->ResetFlag(SbxFlagBits::Fixed
);
4052 retVar
->PutObject(pArray
);
4053 retVar
->SetFlags(nFlags
);
4054 retVar
->SetParameters(nullptr);
4058 std::vector
<TransliterationFlags
> aTranslitSet
;
4059 auto check
= [&nConversion
, &aTranslitSet
](sal_Int32 conv
, TransliterationFlags flag
)
4061 if ((nConversion
& conv
) != conv
)
4064 aTranslitSet
.push_back(flag
);
4065 nConversion
&= ~conv
;
4069 // Check mutually exclusive bits together
4071 if (!check(ooo::vba::VbStrConv::vbProperCase
, TransliterationFlags::TITLE_CASE
))
4072 if (!check(ooo::vba::VbStrConv::vbUpperCase
, TransliterationFlags::LOWERCASE_UPPERCASE
))
4073 check(ooo::vba::VbStrConv::vbLowerCase
, TransliterationFlags::UPPERCASE_LOWERCASE
);
4075 if (!check(ooo::vba::VbStrConv::vbWide
, TransliterationFlags::HALFWIDTH_FULLWIDTH
))
4076 check(ooo::vba::VbStrConv::vbNarrow
, TransliterationFlags::FULLWIDTH_HALFWIDTH
);
4078 if (!check(ooo::vba::VbStrConv::vbKatakana
, TransliterationFlags::HIRAGANA_KATAKANA
))
4079 check(ooo::vba::VbStrConv::vbHiragana
, TransliterationFlags::KATAKANA_HIRAGANA
);
4081 if (nConversion
) // unknown / incorrectly combined bits
4082 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
4084 OUString aStr
= rPar
.Get(1)->GetOUString();
4085 if (!aStr
.isEmpty() && !aTranslitSet
.empty())
4087 const uno::Reference
< uno::XComponentContext
>& xContext
= getProcessComponentContext();
4089 for (auto transliterationFlag
: aTranslitSet
)
4091 if (transliterationFlag
== TransliterationFlags::TITLE_CASE
)
4093 // TransliterationWrapper only handles the first character of the passed string
4094 // when handling TITLE_CASE; see Transliteration_titlecase::transliterateImpl in
4095 // i18npool/source/transliteration/transliteration_body.cxx
4096 CharClass aCharClass
{ xContext
, LanguageTag(nLanguage
) };
4097 aStr
= aCharClass
.titlecase(aCharClass
.lowercase(aStr
));
4101 utl::TransliterationWrapper
aWrapper(xContext
, transliterationFlag
);
4102 aStr
= aWrapper
.transliterate(aStr
, nLanguage
, 0, aStr
.getLength(), nullptr);
4107 rPar
.Get(0)->PutString(aStr
);
4111 void SbRtl_Beep(StarBASIC
*, SbxArray
& rPar
, bool)
4113 if (rPar
.Count() != 1)
4115 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4120 void SbRtl_Load(StarBASIC
*, SbxArray
& rPar
, bool)
4122 if (rPar
.Count() != 2)
4124 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4128 SbxBase
* pObj
= rPar
.Get(1)->GetObject();
4132 if (SbUserFormModule
* pModule
= dynamic_cast<SbUserFormModule
*>(pObj
))
4136 else if (SbxObject
* pSbxObj
= dynamic_cast<SbxObject
*>(pObj
))
4138 SbxVariable
* pVar
= pSbxObj
->Find(u
"Load"_ustr
, SbxClassType::Method
);
4146 void SbRtl_Unload(StarBASIC
*, SbxArray
& rPar
, bool)
4148 rPar
.Get(0)->PutEmpty();
4149 if (rPar
.Count() != 2)
4151 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4155 SbxBase
* pObj
= rPar
.Get(1)->GetObject();
4159 if (SbUserFormModule
* pFormModule
= dynamic_cast<SbUserFormModule
*>(pObj
))
4161 pFormModule
->Unload();
4163 else if (SbxObject
*pSbxObj
= dynamic_cast<SbxObject
*>(pObj
))
4165 SbxVariable
* pVar
= pSbxObj
->Find(u
"Unload"_ustr
, SbxClassType::Method
);
4173 void SbRtl_LoadPicture(StarBASIC
*, SbxArray
& rPar
, bool)
4175 if (rPar
.Count() != 2)
4177 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4180 OUString aFileURL
= getFullPath(rPar
.Get(1)->GetOUString());
4181 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream( aFileURL
, StreamMode::READ
));
4185 ReadDIB(aBmp
, *pStream
, true);
4186 BitmapEx
aBitmapEx(aBmp
);
4187 Graphic
aGraphic(aBitmapEx
);
4189 SbxObjectRef xRef
= new SbStdPicture
;
4190 static_cast<SbStdPicture
*>(xRef
.get())->SetGraphic( aGraphic
);
4191 rPar
.Get(0)->PutObject(xRef
.get());
4195 void SbRtl_SavePicture(StarBASIC
*, SbxArray
& rPar
, bool)
4197 rPar
.Get(0)->PutEmpty();
4198 if (rPar
.Count() != 3)
4200 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4203 SbxBase
* pObj
= rPar
.Get(1)->GetObject();
4204 if (SbStdPicture
*pPicture
= dynamic_cast<SbStdPicture
*>(pObj
))
4206 SvFileStream
aOStream(rPar
.Get(2)->GetOUString(), StreamMode::WRITE
| StreamMode::TRUNC
);
4207 const Graphic
& aGraphic
= pPicture
->GetGraphic();
4208 TypeSerializer
aSerializer(aOStream
);
4209 aSerializer
.writeGraphic(aGraphic
);
4213 void SbRtl_MsgBox(StarBASIC
*, SbxArray
& rPar
, bool)
4215 const sal_uInt32 nArgCount
= rPar
.Count();
4216 if( nArgCount
< 2 || nArgCount
> 6 )
4218 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4221 // tdf#147529 - check for missing parameters
4222 if (IsMissing(rPar
, 1))
4224 return StarBASIC::Error(ERRCODE_BASIC_NOT_OPTIONAL
);
4227 // tdf#151012 - initialize optional parameters with their default values (number of buttons)
4228 sal_Int16 nType
= GetOptionalIntegerParamOrDefault(rPar
, 2, SbMB::OK
);
4230 OUString aMsg
= rPar
.Get(1)->GetOUString();
4231 // tdf#151012 - initialize optional parameters with their default values (title of dialog box)
4232 OUString aTitle
= GetOptionalOUStringParamOrDefault(rPar
, 3, Application::GetDisplayName());
4234 sal_Int16 nDialogType
= nType
& (SbMB::ICONSTOP
| SbMB::ICONQUESTION
| SbMB::ICONINFORMATION
);
4236 SolarMutexGuard aSolarGuard
;
4237 weld::Widget
* pParent
= Application::GetDefDialogParent();
4239 VclMessageType eType
= VclMessageType::Other
;
4241 switch (nDialogType
)
4243 case SbMB::ICONSTOP
:
4244 eType
= VclMessageType::Error
;
4246 case SbMB::ICONQUESTION
:
4247 eType
= VclMessageType::Question
;
4249 case SbMB::ICONEXCLAMATION
:
4250 eType
= VclMessageType::Warning
;
4252 case SbMB::ICONINFORMATION
:
4253 eType
= VclMessageType::Info
;
4257 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pParent
,
4258 eType
, VclButtonsType::NONE
, aMsg
, GetpApp()));
4260 std::vector
<std::pair
<StandardButtonType
, sal_Int16
>> buttons
;
4261 switch (nType
& 0x0F) // delete bits 4-16
4265 buttons
.emplace_back(StandardButtonType::OK
, SbMB::Response::OK
);
4267 case SbMB::OKCANCEL
:
4268 buttons
.emplace_back(StandardButtonType::OK
, SbMB::Response::OK
);
4269 buttons
.emplace_back(StandardButtonType::Cancel
, SbMB::Response::CANCEL
);
4271 case SbMB::ABORTRETRYIGNORE
:
4272 buttons
.emplace_back(StandardButtonType::Abort
, SbMB::Response::ABORT
);
4273 buttons
.emplace_back(StandardButtonType::Retry
, SbMB::Response::RETRY
);
4274 buttons
.emplace_back(StandardButtonType::Ignore
, SbMB::Response::IGNORE
);
4276 case SbMB::YESNOCANCEL
:
4277 buttons
.emplace_back(StandardButtonType::Yes
, SbMB::Response::YES
);
4278 buttons
.emplace_back(StandardButtonType::No
, SbMB::Response::NO
);
4279 buttons
.emplace_back(StandardButtonType::Cancel
, SbMB::Response::CANCEL
);
4282 buttons
.emplace_back(StandardButtonType::Yes
, SbMB::Response::YES
);
4283 buttons
.emplace_back(StandardButtonType::No
, SbMB::Response::NO
);
4285 case SbMB::RETRYCANCEL
:
4286 buttons
.emplace_back(StandardButtonType::Retry
, SbMB::Response::RETRY
);
4287 buttons
.emplace_back(StandardButtonType::Cancel
, SbMB::Response::CANCEL
);
4291 for (auto [buttonType
, buttonResponse
] : buttons
)
4292 xBox
->add_button(GetStandardText(buttonType
), buttonResponse
);
4294 std::size_t default_button
= 0;
4295 if (nType
& SbMB::DEFBUTTON2
)
4297 else if (nType
& SbMB::DEFBUTTON3
)
4299 xBox
->set_default_response(buttons
[std::min(default_button
, buttons
.size() - 1)].second
);
4301 xBox
->set_title(aTitle
);
4302 sal_Int16 nRet
= xBox
->run();
4303 rPar
.Get(0)->PutInteger(nRet
);
4306 void SbRtl_SetAttr(StarBASIC
*, SbxArray
& rPar
, bool)
4308 rPar
.Get(0)->PutEmpty();
4309 if (rPar
.Count() == 3)
4311 OUString aStr
= rPar
.Get(1)->GetOUString();
4312 sal_Int16 nFlags
= rPar
.Get(2)->GetInteger();
4316 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
4321 bool bReadOnly
= bool(nFlags
& SbAttributes::READONLY
);
4322 xSFI
->setReadOnly( aStr
, bReadOnly
);
4323 bool bHidden
= bool(nFlags
& SbAttributes::HIDDEN
);
4324 xSFI
->setHidden( aStr
, bHidden
);
4326 catch(const Exception
& )
4328 StarBASIC::Error( ERRCODE_IO_GENERAL
);
4335 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4339 void SbRtl_Reset(StarBASIC
*, SbxArray
&, bool)
4341 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
4348 void SbRtl_DumpAllObjects(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
4350 const sal_uInt32 nArgCount
= rPar
.Count();
4351 if( nArgCount
< 2 || nArgCount
> 3 )
4353 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4357 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
4361 SbxObject
* p
= pBasic
;
4362 while( p
->GetParent() )
4366 SvFileStream
aStrm(rPar
.Get(1)->GetOUString(),
4367 StreamMode::WRITE
| StreamMode::TRUNC
);
4368 p
->Dump(aStrm
, rPar
.Get(2)->GetBool());
4370 if( aStrm
.GetError() != ERRCODE_NONE
)
4372 StarBASIC::Error( ERRCODE_BASIC_IO_ERROR
);
4378 void SbRtl_FileExists(StarBASIC
*, SbxArray
& rPar
, bool)
4380 if (rPar
.Count() == 2)
4382 OUString aStr
= rPar
.Get(1)->GetOUString();
4383 bool bExists
= false;
4387 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
4392 bExists
= xSFI
->exists( aStr
);
4394 catch(const Exception
& )
4396 StarBASIC::Error( ERRCODE_IO_GENERAL
);
4402 DirectoryItem aItem
;
4403 FileBase::RC nRet
= DirectoryItem::get( getFullPath( aStr
), aItem
);
4404 bExists
= (nRet
== FileBase::E_None
);
4406 rPar
.Get(0)->PutBool(bExists
);
4410 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4414 void SbRtl_Partition(StarBASIC
*, SbxArray
& rPar
, bool)
4416 if (rPar
.Count() != 5)
4418 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4421 sal_Int32 nNumber
= rPar
.Get(1)->GetLong();
4422 sal_Int32 nStart
= rPar
.Get(2)->GetLong();
4423 sal_Int32 nStop
= rPar
.Get(3)->GetLong();
4424 sal_Int32 nInterval
= rPar
.Get(4)->GetLong();
4426 if( nStart
< 0 || nStop
<= nStart
|| nInterval
< 1 )
4428 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4431 // the Partition function inserts leading spaces before lowervalue and uppervalue
4432 // so that they both have the same number of characters as the string
4433 // representation of the value (Stop + 1). This ensures that if you use the output
4434 // of the Partition function with several values of Number, the resulting text
4435 // will be handled properly during any subsequent sort operation.
4437 // calculate the maximum number of characters before lowervalue and uppervalue
4438 OUString aBeforeStart
= OUString::number( nStart
- 1 );
4439 OUString aAfterStop
= OUString::number( nStop
+ 1 );
4440 sal_Int32 nLen1
= aBeforeStart
.getLength();
4441 sal_Int32 nLen2
= aAfterStop
.getLength();
4442 sal_Int32 nLen
= nLen1
>= nLen2
? nLen1
:nLen2
;
4444 OUStringBuffer
aRetStr( nLen
* 2 + 1);
4445 OUString aLowerValue
;
4446 OUString aUpperValue
;
4447 if( nNumber
< nStart
)
4449 aUpperValue
= aBeforeStart
;
4451 else if( nNumber
> nStop
)
4453 aLowerValue
= aAfterStop
;
4457 sal_Int32 nLowerValue
= nNumber
;
4458 sal_Int32 nUpperValue
= nLowerValue
;
4461 nLowerValue
= ((( nNumber
- nStart
) / nInterval
) * nInterval
) + nStart
;
4462 nUpperValue
= nLowerValue
+ nInterval
- 1;
4464 aLowerValue
= OUString::number( nLowerValue
);
4465 aUpperValue
= OUString::number( nUpperValue
);
4468 nLen1
= aLowerValue
.getLength();
4469 nLen2
= aUpperValue
.getLength();
4473 // appending the leading spaces for the lowervalue
4474 for ( sal_Int32 i
= nLen
- nLen1
; i
> 0; --i
)
4476 aRetStr
.append(" ");
4479 aRetStr
.append( aLowerValue
+ ":");
4482 // appending the leading spaces for the uppervalue
4483 for ( sal_Int32 i
= nLen
- nLen2
; i
> 0; --i
)
4485 aRetStr
.append(" ");
4488 aRetStr
.append( aUpperValue
);
4489 rPar
.Get(0)->PutString(aRetStr
.makeStringAndClear());
4494 sal_Int16
implGetDateYear( double aDate
)
4496 Date
aRefDate(1899'12'30);
4497 sal_Int32 nDays
= static_cast<sal_Int32
>(aDate
);
4498 aRefDate
.AddDays( nDays
);
4499 sal_Int16 nRet
= aRefDate
.GetYear();
4503 bool implDateSerial( sal_Int16 nYear
, sal_Int16 nMonth
, sal_Int16 nDay
,
4504 bool bUseTwoDigitYear
, SbDateCorrection eCorr
, double& rdRet
)
4506 // XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and
4507 // 30..99 can not be input as they are 2-digit for 2000..2029 and
4508 // 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always
4509 // true). For VBA years > 9999 are invalid.
4510 // For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99
4511 // can not be input as they are 2-digit for 1900..1999, years<0 are
4512 // accepted. If bUseTwoDigitYear==false then all years are accepted, but
4513 // year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01).
4514 #if HAVE_FEATURE_SCRIPTING
4515 if ( (nYear
< 0 || 9999 < nYear
) && SbiRuntime::isVBAEnabled() )
4517 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4520 else if ( nYear
< 30 && SbiRuntime::isVBAEnabled() )
4527 if ( 0 <= nYear
&& nYear
< 100 &&
4528 #if HAVE_FEATURE_SCRIPTING
4529 (bUseTwoDigitYear
|| SbiRuntime::isVBAEnabled())
4539 sal_Int32 nAddMonths
= 0;
4540 sal_Int32 nAddDays
= 0;
4541 // Always sanitize values to set date and to use for validity detection.
4542 if (nMonth
< 1 || 12 < nMonth
)
4544 sal_Int16 nM
= ((nMonth
< 1) ? (12 + (nMonth
% 12)) : (nMonth
% 12));
4545 nAddMonths
= nMonth
- nM
;
4548 // Day 0 would already be normalized during Date::Normalize(), include
4549 // it in negative days, also to detect non-validity. The actual day of
4550 // month is 1+(nDay-1)
4553 nAddDays
= nDay
- 1;
4558 nAddDays
= nDay
- 31;
4562 Date
aCurDate( nDay
, nMonth
, nYear
);
4564 /* TODO: we could enable the same rollover mechanism for StarBASIC to be
4565 * compatible with VBA (just with our wider supported date range), then
4566 * documentation would need to be adapted. As is, the DateSerial() runtime
4567 * function works as dumb as documented... (except that the resulting date
4568 * is checked for validity now and not just day<=31 and month<=12).
4569 * If change wanted then simply remove overriding RollOver here and adapt
4571 #if HAVE_FEATURE_SCRIPTING
4572 if (eCorr
== SbDateCorrection::RollOver
&& !SbiRuntime::isVBAEnabled())
4573 eCorr
= SbDateCorrection::None
;
4576 if (nYear
== 0 || (eCorr
== SbDateCorrection::None
&& (nAddMonths
|| nAddDays
|| !aCurDate
.IsValidDate())))
4578 #if HAVE_FEATURE_SCRIPTING
4579 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4584 if (eCorr
!= SbDateCorrection::None
)
4586 aCurDate
.Normalize();
4588 aCurDate
.AddMonths( nAddMonths
);
4590 aCurDate
.AddDays( nAddDays
);
4591 if (eCorr
== SbDateCorrection::TruncateToMonth
&& aCurDate
.GetMonth() != nMonth
)
4593 if (aCurDate
.GetYear() == SAL_MAX_INT16
&& nMonth
== 12)
4595 // Roll over and back not possible, hard max.
4596 aCurDate
.SetMonth(12);
4597 aCurDate
.SetDay(31);
4601 aCurDate
.SetMonth(nMonth
);
4603 aCurDate
.AddMonths(1);
4604 aCurDate
.AddDays(-1);
4609 rdRet
= GetDayDiff(aCurDate
);
4613 double implTimeSerial(sal_Int16 nHours
, sal_Int16 nMinutes
, sal_Int16 nSeconds
,
4614 sal_Int32 nMilliSeconds
)
4616 return (nHours
* ::tools::Time::milliSecPerHour
+ nMinutes
* ::tools::Time::milliSecPerMinute
4617 + nSeconds
* ::tools::Time::milliSecPerSec
+ nMilliSeconds
)
4618 / static_cast<double>(::tools::Time::milliSecPerDay
);
4621 bool implDateTimeSerial(sal_Int16 nYear
, sal_Int16 nMonth
, sal_Int16 nDay
, sal_Int16 nHour
,
4622 sal_Int16 nMinute
, sal_Int16 nSecond
, sal_Int32 nMilliSecond
, double& rdRet
)
4625 if(!implDateSerial(nYear
, nMonth
, nDay
, false/*bUseTwoDigitYear*/, SbDateCorrection::None
, dDate
))
4627 rdRet
+= dDate
+ implTimeSerial(nHour
, nMinute
, nSecond
, nMilliSecond
);
4631 sal_Int16
implGetMinute( double dDate
)
4633 double nFrac
= (dDate
- floor(dDate
)) * ::tools::Time::milliSecPerDay
;
4634 sal_uInt64 nMilliSeconds
= static_cast<sal_uInt64
>(nFrac
+ 0.5);
4635 return static_cast<sal_Int16
>((nMilliSeconds
/ ::tools::Time::milliSecPerMinute
)
4636 % ::tools::Time::minutePerHour
);
4639 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */