xref: /core/basic/source/runtime/methods.cxx (revision 28fc9860)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <tools/date.hxx>
23 #include <basic/sbxvar.hxx>
24 #include <basic/sbuno.hxx>
25 #include <osl/process.h>
26 #include <vcl/dibtools.hxx>
27 #include <vcl/window.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/sound.hxx>
31 #include <tools/wintypes.hxx>
32 #include <vcl/stdtext.hxx>
33 #include <vcl/weld.hxx>
34 #include <basic/sbx.hxx>
35 #include <svl/zforlist.hxx>
36 #include <rtl/character.hxx>
37 #include <rtl/math.hxx>
38 #include <tools/urlobj.hxx>
39 #include <osl/time.h>
40 #include <unotools/charclass.hxx>
41 #include <unotools/ucbstreamhelper.hxx>
42 #include <tools/wldcrd.hxx>
43 #include <i18nlangtag/lang.h>
44 #include <rtl/string.hxx>
45 #include <rtl/strbuf.hxx>
46 #include <sal/log.hxx>
47 
48 #include <runtime.hxx>
49 #include <sbunoobj.hxx>
50 #include <osl/file.hxx>
51 #include <errobject.hxx>
52 
53 #include <comphelper/string.hxx>
54 #include <comphelper/processfactory.hxx>
55 #include <comphelper/string.hxx>
56 
57 #include <com/sun/star/uno/Sequence.hxx>
58 #include <com/sun/star/util/DateTime.hpp>
59 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
60 #include <com/sun/star/lang/Locale.hpp>
61 #include <com/sun/star/lang/XServiceInfo.hpp>
62 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
63 #include <com/sun/star/script/XErrorQuery.hpp>
64 #include <ooo/vba/XHelperInterface.hpp>
65 #include <ooo/vba/VbTriState.hpp>
66 #include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp>
67 #include <memory>
68 #include <random>
69 #include <o3tl/char16_t2wchar_t.hxx>
70 
71 using namespace comphelper;
72 using namespace osl;
73 using namespace com::sun::star;
74 using namespace com::sun::star::lang;
75 using namespace com::sun::star::uno;
76 
77 #include <date.hxx>
78 #include <stdobj.hxx>
79 #include <sbstdobj.hxx>
80 #include <rtlproto.hxx>
81 #include <image.hxx>
82 #include <iosys.hxx>
83 #include "ddectrl.hxx"
84 #include <sbintern.hxx>
85 #include <basic/vbahelper.hxx>
86 
87 #include <vector>
88 #include <math.h>
89 #include <stdio.h>
90 #include <stdlib.h>
91 #include <errno.h>
92 
93 #include <sbobjmod.hxx>
94 #include <sbxmod.hxx>
95 
96 #ifdef _WIN32
97 #include <prewin.h>
98 #include <direct.h>
99 #include <io.h>
100 #include <postwin.h>
101 #else
102 #include <unistd.h>
103 #endif
104 
105 #if HAVE_FEATURE_SCRIPTING
106 
107 static void FilterWhiteSpace( OUString& rStr )
108 {
109     if (rStr.isEmpty())
110     {
111         return;
112     }
113     OUStringBuffer aRet;
114 
115     for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
116     {
117         sal_Unicode cChar = rStr[i];
118         if ((cChar != ' ') && (cChar != '\t') &&
119            (cChar != '\n') && (cChar != '\r'))
120         {
121             aRet.append(cChar);
122         }
123     }
124 
125     rStr = aRet.makeStringAndClear();
126 }
127 
128 static long GetDayDiff( const Date& rDate );
129 
130 static const CharClass& GetCharClass()
131 {
132     static CharClass aCharClass( Application::GetSettings().GetLanguageTag() );
133     return aCharClass;
134 }
135 
136 static bool isFolder( FileStatus::Type aType )
137 {
138     return ( aType == FileStatus::Directory || aType == FileStatus::Volume );
139 }
140 
141 
142 //*** UCB file access ***
143 
144 // Converts possibly relative paths to absolute paths
145 // according to the setting done by ChDir/ChDrive
146 OUString getFullPath( const OUString& aRelPath )
147 {
148     OUString aFileURL;
149 
150     // #80204 Try first if it already is a valid URL
151     INetURLObject aURLObj( aRelPath );
152     aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
153 
154     if( aFileURL.isEmpty() )
155     {
156         File::getFileURLFromSystemPath( aRelPath, aFileURL );
157     }
158 
159     return aFileURL;
160 }
161 
162 // TODO: -> SbiGlobals
163 static uno::Reference< ucb::XSimpleFileAccess3 > const & getFileAccess()
164 {
165     static uno::Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
166     return xSFI;
167 }
168 
169 
170 // Properties and methods lie down the return value at the Get (bPut = sal_False) in the
171 // element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True)
172 
173 // CreateObject( class )
174 
175 void SbRtl_CreateObject(StarBASIC * pBasic, SbxArray & rPar, bool)
176 {
177     OUString aClass( rPar.Get( 1 )->GetOUString() );
178     SbxObjectRef p = SbxBase::CreateObject( aClass );
179     if( !p.is() )
180         StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD );
181     else
182     {
183         // Convenience: enter BASIC as parent
184         p->SetParent( pBasic );
185         rPar.Get( 0 )->PutObject( p.get() );
186     }
187 }
188 
189 // Error( n )
190 
191 void SbRtl_Error(StarBASIC * pBasic, SbxArray & rPar, bool)
192 {
193     if( !pBasic )
194         StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
195     else
196     {
197         OUString aErrorMsg;
198         ErrCode nErr = ERRCODE_NONE;
199         sal_Int32 nCode = 0;
200         if( rPar.Count() == 1 )
201         {
202             nErr = StarBASIC::GetErrBasic();
203             aErrorMsg = StarBASIC::GetErrorMsg();
204         }
205         else
206         {
207             nCode = rPar.Get( 1 )->GetLong();
208             if( nCode > 65535 )
209             {
210                 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
211             }
212             else
213             {
214                 nErr = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nCode) );
215             }
216         }
217 
218         bool bVBA = SbiRuntime::isVBAEnabled();
219         OUString tmpErrMsg;
220         if( bVBA && !aErrorMsg.isEmpty())
221         {
222             tmpErrMsg = aErrorMsg;
223         }
224         else
225         {
226             StarBASIC::MakeErrorText( nErr, aErrorMsg );
227             tmpErrMsg = StarBASIC::GetErrorText();
228         }
229         // If this rtlfunc 'Error' passed an errcode the same as the active Err Objects's
230         // current err then  return the description for the error message if it is set
231         // ( complicated isn't it ? )
232         if ( bVBA && rPar.Count() > 1 )
233         {
234             uno::Reference< ooo::vba::XErrObject > xErrObj( SbxErrObject::getUnoErrObject() );
235             if ( xErrObj.is() && xErrObj->getNumber() == nCode && !xErrObj->getDescription().isEmpty() )
236             {
237                 tmpErrMsg = xErrObj->getDescription();
238             }
239         }
240         rPar.Get( 0 )->PutString( tmpErrMsg );
241     }
242 }
243 
244 // Sinus
245 
246 void SbRtl_Sin(StarBASIC *, SbxArray & rPar, bool)
247 {
248     if ( rPar.Count() < 2 )
249         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
250     else
251     {
252         SbxVariableRef pArg = rPar.Get( 1 );
253         rPar.Get( 0 )->PutDouble( sin( pArg->GetDouble() ) );
254     }
255 }
256 
257 
258 void SbRtl_Cos(StarBASIC *, SbxArray & rPar, bool)
259 {
260     if ( rPar.Count() < 2 )
261         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
262     else
263     {
264         SbxVariableRef pArg = rPar.Get( 1 );
265         rPar.Get( 0 )->PutDouble( cos( pArg->GetDouble() ) );
266     }
267 }
268 
269 
270 void SbRtl_Atn(StarBASIC *, SbxArray & rPar, bool)
271 {
272     if ( rPar.Count() < 2 )
273         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
274     else
275     {
276         SbxVariableRef pArg = rPar.Get( 1 );
277         rPar.Get( 0 )->PutDouble( atan( pArg->GetDouble() ) );
278     }
279 }
280 
281 
282 void SbRtl_Abs(StarBASIC *, SbxArray & rPar, bool)
283 {
284     if ( rPar.Count() < 2 )
285     {
286         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
287     }
288     else
289     {
290         SbxVariableRef pArg = rPar.Get( 1 );
291         rPar.Get( 0 )->PutDouble( fabs( pArg->GetDouble() ) );
292     }
293 }
294 
295 
296 void SbRtl_Asc(StarBASIC *, SbxArray & rPar, bool)
297 {
298     if ( rPar.Count() < 2 )
299     {
300         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
301     }
302     else
303     {
304         SbxVariableRef pArg = rPar.Get( 1 );
305         OUString aStr( pArg->GetOUString() );
306         if ( aStr.isEmpty())
307         {
308             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
309             rPar.Get(0)->PutEmpty();
310         }
311         else
312         {
313             sal_Unicode aCh = aStr[0];
314             rPar.Get(0)->PutLong( aCh );
315         }
316     }
317 }
318 
319 static void implChr( SbxArray& rPar, bool bChrW )
320 {
321     if ( rPar.Count() < 2 )
322     {
323         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
324     }
325     else
326     {
327         SbxVariableRef pArg = rPar.Get( 1 );
328 
329         OUString aStr;
330         if( !bChrW && SbiRuntime::isVBAEnabled() )
331         {
332             sal_Char c = static_cast<sal_Char>(pArg->GetByte());
333             aStr = OUString(&c, 1, osl_getThreadTextEncoding());
334         }
335         else
336         {
337             sal_Unicode aCh = static_cast<sal_Unicode>(pArg->GetUShort());
338             aStr = OUString(aCh);
339         }
340         rPar.Get(0)->PutString( aStr );
341     }
342 }
343 
344 void SbRtl_Chr(StarBASIC *, SbxArray & rPar, bool)
345 {
346     implChr( rPar, false/*bChrW*/ );
347 }
348 
349 void SbRtl_ChrW(StarBASIC *, SbxArray & rPar, bool)
350 {
351     implChr( rPar, true/*bChrW*/ );
352 }
353 
354 void SbRtl_CurDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
355 {
356     (void)pBasic;
357     (void)bWrite;
358 
359     // #57064 Although this function doesn't work with DirEntry, it isn't touched
360     // by the adjustment to virtual URLs, as, using the DirEntry-functionality,
361     // there's no possibility to detect the current one in a way that a virtual URL
362     // could be delivered.
363 
364 #if defined(_WIN32)
365     int nCurDir = 0;  // Current dir // JSM
366     if ( rPar.Count() == 2 )
367     {
368         OUString aDrive = rPar.Get(1)->GetOUString();
369         if ( aDrive.getLength() != 1 )
370         {
371             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
372             return;
373         }
374         auto c = rtl::toAsciiUpperCase(aDrive[0]);
375         if ( !rtl::isAsciiUpperCase( c ) )
376         {
377             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
378             return;
379         }
380         nCurDir = c - 'A' + 1;
381     }
382     wchar_t pBuffer[ _MAX_PATH ];
383     if ( _wgetdcwd( nCurDir, pBuffer, _MAX_PATH ) != nullptr )
384     {
385         rPar.Get(0)->PutString( o3tl::toU(pBuffer) );
386     }
387     else
388     {
389         StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE );
390     }
391 
392 #else
393 
394     const int PATH_INCR = 250;
395 
396     int nSize = PATH_INCR;
397     std::unique_ptr<char[]> pMem;
398     while( true )
399       {
400         pMem.reset(new char[nSize]);
401         if( !pMem )
402           {
403             StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY );
404             return;
405           }
406         if( getcwd( pMem.get(), nSize-1 ) != nullptr )
407           {
408             rPar.Get(0)->PutString( OUString::createFromAscii(pMem.get()) );
409             return;
410           }
411         if( errno != ERANGE )
412           {
413             StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
414             return;
415           }
416         nSize += PATH_INCR;
417       };
418 
419 #endif
420 }
421 
422 void SbRtl_ChDir(StarBASIC * pBasic, SbxArray & rPar, bool)
423 {
424     rPar.Get(0)->PutEmpty();
425     if (rPar.Count() == 2)
426     {
427         // VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.)
428         if( SbiRuntime::isVBAEnabled() )
429         {
430             ::basic::vba::registerCurrentDirectory( getDocumentModel( pBasic ), rPar.Get(1)->GetOUString() );
431         }
432     }
433     else
434     {
435         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
436     }
437 }
438 
439 void SbRtl_ChDrive(StarBASIC *, SbxArray & rPar, bool)
440 {
441     rPar.Get(0)->PutEmpty();
442     if (rPar.Count() != 2)
443     {
444         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
445     }
446 }
447 
448 
449 // Implementation of StepRENAME with UCB
450 void implStepRenameUCB( const OUString& aSource, const OUString& aDest )
451 {
452     const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
453     if( xSFI.is() )
454     {
455         try
456         {
457             OUString aSourceFullPath = getFullPath( aSource );
458             if( !xSFI->exists( aSourceFullPath ) )
459             {
460                 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
461                 return;
462             }
463 
464             OUString aDestFullPath = getFullPath( aDest );
465             if( xSFI->exists( aDestFullPath ) )
466             {
467                 StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS );
468             }
469             else
470             {
471                 xSFI->move( aSourceFullPath, aDestFullPath );
472             }
473         }
474         catch(const Exception & )
475         {
476             StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
477         }
478     }
479 }
480 
481 // Implementation of StepRENAME with OSL
482 void implStepRenameOSL( const OUString& aSource, const OUString& aDest )
483 {
484     FileBase::RC nRet = File::move( getFullPath( aSource ), getFullPath( aDest ) );
485     if( nRet != FileBase::E_None )
486     {
487         StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
488     }
489 }
490 
491 void SbRtl_FileCopy(StarBASIC *, SbxArray & rPar, bool)
492 {
493     rPar.Get(0)->PutEmpty();
494     if (rPar.Count() == 3)
495     {
496         OUString aSource = rPar.Get(1)->GetOUString();
497         OUString aDest = rPar.Get(2)->GetOUString();
498         if( hasUno() )
499         {
500             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
501             if( xSFI.is() )
502             {
503                 try
504                 {
505                     xSFI->copy( getFullPath( aSource ), getFullPath( aDest ) );
506                 }
507                 catch(const Exception & )
508                 {
509                     StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
510                 }
511             }
512         }
513         else
514         {
515             FileBase::RC nRet = File::copy( getFullPath( aSource ), getFullPath( aDest ) );
516             if( nRet != FileBase::E_None )
517             {
518                 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
519             }
520         }
521     }
522     else
523         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
524 }
525 
526 void SbRtl_Kill(StarBASIC *, SbxArray & rPar, bool)
527 {
528     rPar.Get(0)->PutEmpty();
529     if (rPar.Count() == 2)
530     {
531         OUString aFileSpec = rPar.Get(1)->GetOUString();
532 
533         if( hasUno() )
534         {
535             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
536             if( xSFI.is() )
537             {
538                 OUString aFullPath = getFullPath( aFileSpec );
539                 if( !xSFI->exists( aFullPath ) || xSFI->isFolder( aFullPath ) )
540                 {
541                     StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
542                     return;
543                 }
544                 try
545                 {
546                     xSFI->kill( aFullPath );
547                 }
548                 catch(const Exception & )
549                 {
550                     StarBASIC::Error( ERRCODE_IO_GENERAL );
551                 }
552             }
553         }
554         else
555         {
556             File::remove( getFullPath( aFileSpec ) );
557         }
558     }
559     else
560     {
561         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
562     }
563 }
564 
565 void SbRtl_MkDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
566 {
567     rPar.Get(0)->PutEmpty();
568     if (rPar.Count() == 2)
569     {
570         OUString aPath = rPar.Get(1)->GetOUString();
571         if ( SbiRuntime::isVBAEnabled() )
572         {
573             // In vba if the full path is not specified then
574             // folder is created relative to the curdir
575             INetURLObject aURLObj( getFullPath( aPath ) );
576             if ( aURLObj.GetProtocol() != INetProtocol::File )
577             {
578                 SbxArrayRef pPar = new SbxArray();
579                 SbxVariableRef pResult = new SbxVariable();
580                 SbxVariableRef pParam = new SbxVariable();
581                 pPar->Insert( pResult.get(), pPar->Count() );
582                 pPar->Insert( pParam.get(), pPar->Count() );
583                 SbRtl_CurDir( pBasic, *pPar, bWrite );
584 
585                 OUString sCurPathURL;
586                 File::getFileURLFromSystemPath( pPar->Get(0)->GetOUString(), sCurPathURL );
587 
588                 aURLObj.SetURL( sCurPathURL );
589                 aURLObj.Append( aPath );
590                 File::getSystemPathFromFileURL(aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri  ),aPath ) ;
591             }
592         }
593 
594         if( hasUno() )
595         {
596             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
597             if( xSFI.is() )
598             {
599                 try
600                 {
601                     xSFI->createFolder( getFullPath( aPath ) );
602                 }
603                 catch(const Exception & )
604                 {
605                     StarBASIC::Error( ERRCODE_IO_GENERAL );
606                 }
607             }
608         }
609         else
610         {
611             Directory::create( getFullPath( aPath ) );
612         }
613     }
614     else
615     {
616         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
617     }
618 }
619 
620 
621 // In OSL only empty directories can be deleted
622 // so we have to delete all files recursively
623 static void implRemoveDirRecursive( const OUString& aDirPath )
624 {
625     DirectoryItem aItem;
626     FileBase::RC nRet = DirectoryItem::get( aDirPath, aItem );
627     bool bExists = (nRet == FileBase::E_None);
628 
629     FileStatus aFileStatus( osl_FileStatus_Mask_Type );
630     nRet = aItem.getFileStatus( aFileStatus );
631     bool bFolder = nRet == FileBase::E_None
632         && isFolder( aFileStatus.getFileType() );
633 
634     if( !bExists || !bFolder )
635     {
636         StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
637         return;
638     }
639 
640     Directory aDir( aDirPath );
641     nRet = aDir.open();
642     if( nRet != FileBase::E_None )
643     {
644         StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
645         return;
646     }
647 
648     for( ;; )
649     {
650         DirectoryItem aItem2;
651         nRet = aDir.getNextItem( aItem2 );
652         if( nRet != FileBase::E_None )
653         {
654             break;
655         }
656         // Handle flags
657         FileStatus aFileStatus2( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL );
658         nRet = aItem2.getFileStatus( aFileStatus2 );
659         if( nRet != FileBase::E_None )
660         {
661             SAL_WARN("basic", "getFileStatus failed");
662             continue;
663         }
664         OUString aPath = aFileStatus2.getFileURL();
665 
666         // Directory?
667         FileStatus::Type aType2 = aFileStatus2.getFileType();
668         bool bFolder2 = isFolder( aType2 );
669         if( bFolder2 )
670         {
671             implRemoveDirRecursive( aPath );
672         }
673         else
674         {
675             File::remove( aPath );
676         }
677     }
678     aDir.close();
679 
680     Directory::remove( aDirPath );
681 }
682 
683 
684 void SbRtl_RmDir(StarBASIC *, SbxArray & rPar, bool)
685 {
686     rPar.Get(0)->PutEmpty();
687     if (rPar.Count() == 2)
688     {
689         OUString aPath = rPar.Get(1)->GetOUString();
690         if( hasUno() )
691         {
692             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
693             if( xSFI.is() )
694             {
695                 try
696                 {
697                     if( !xSFI->isFolder( aPath ) )
698                     {
699                         StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
700                         return;
701                     }
702                     SbiInstance* pInst = GetSbData()->pInst;
703                     bool bCompatibility = ( pInst && pInst->IsCompatibility() );
704                     if( bCompatibility )
705                     {
706                         Sequence< OUString > aContent = xSFI->getFolderContents( aPath, true );
707                         if( aContent.hasElements() )
708                         {
709                             StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR );
710                             return;
711                         }
712                     }
713 
714                     xSFI->kill( getFullPath( aPath ) );
715                 }
716                 catch(const Exception & )
717                 {
718                     StarBASIC::Error( ERRCODE_IO_GENERAL );
719                 }
720             }
721         }
722         else
723         {
724             implRemoveDirRecursive( getFullPath( aPath ) );
725         }
726     }
727     else
728     {
729         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
730     }
731 }
732 
733 void SbRtl_SendKeys(StarBASIC *, SbxArray & rPar, bool)
734 {
735     rPar.Get(0)->PutEmpty();
736     StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
737 }
738 
739 void SbRtl_Exp(StarBASIC *, SbxArray & rPar, bool)
740 {
741     if( rPar.Count() < 2 )
742         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
743     else
744     {
745         double aDouble = rPar.Get( 1 )->GetDouble();
746         aDouble = exp( aDouble );
747         checkArithmeticOverflow( aDouble );
748         rPar.Get( 0 )->PutDouble( aDouble );
749     }
750 }
751 
752 void SbRtl_FileLen(StarBASIC *, SbxArray & rPar, bool)
753 {
754     if ( rPar.Count() < 2 )
755     {
756         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
757     }
758     else
759     {
760         SbxVariableRef pArg = rPar.Get( 1 );
761         OUString aStr( pArg->GetOUString() );
762         sal_Int32 nLen = 0;
763         if( hasUno() )
764         {
765             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
766             if( xSFI.is() )
767             {
768                 try
769                 {
770                     nLen = xSFI->getSize( getFullPath( aStr ) );
771                 }
772                 catch(const Exception & )
773                 {
774                     StarBASIC::Error( ERRCODE_IO_GENERAL );
775                 }
776             }
777         }
778         else
779         {
780             DirectoryItem aItem;
781             (void)DirectoryItem::get( getFullPath( aStr ), aItem );
782             FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
783             (void)aItem.getFileStatus( aFileStatus );
784             nLen = static_cast<sal_Int32>(aFileStatus.getFileSize());
785         }
786         rPar.Get(0)->PutLong( static_cast<long>(nLen) );
787     }
788 }
789 
790 
791 void SbRtl_Hex(StarBASIC *, SbxArray & rPar, bool)
792 {
793     if ( rPar.Count() < 2 )
794     {
795         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
796     }
797     else
798     {
799         SbxVariableRef pArg = rPar.Get( 1 );
800         // converting value to unsigned and limit to 2 or 4 byte representation
801         sal_uInt32 nVal = pArg->IsInteger() ?
802             static_cast<sal_uInt16>(pArg->GetInteger()) :
803             static_cast<sal_uInt32>(pArg->GetLong());
804         OUString aStr(OUString::number( nVal, 16 ));
805         aStr = aStr.toAsciiUpperCase();
806         rPar.Get(0)->PutString( aStr );
807     }
808 }
809 
810 void SbRtl_FuncCaller(StarBASIC *, SbxArray & rPar, bool)
811 {
812     if ( SbiRuntime::isVBAEnabled() &&  GetSbData()->pInst && GetSbData()->pInst->pRun )
813     {
814         if ( GetSbData()->pInst->pRun->GetExternalCaller() )
815             *rPar.Get(0) =  *GetSbData()->pInst->pRun->GetExternalCaller();
816         else
817         {
818             SbxVariableRef pVar = new SbxVariable(SbxVARIANT);
819             *rPar.Get(0) = *pVar;
820         }
821     }
822     else
823     {
824         StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
825     }
826 
827 }
828 // InStr( [start],string,string,[compare] )
829 
830 void SbRtl_InStr(StarBASIC *, SbxArray & rPar, bool)
831 {
832     std::size_t nArgCount = rPar.Count()-1;
833     if ( nArgCount < 2 )
834         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
835     else
836     {
837         sal_Int32 nStartPos = 1;
838         sal_Int32 nFirstStringPos = 1;
839 
840         if ( nArgCount >= 3 )
841         {
842             nStartPos = rPar.Get(1)->GetLong();
843             if( nStartPos <= 0 )
844             {
845                 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
846                 nStartPos = 1;
847             }
848             nFirstStringPos++;
849         }
850 
851         SbiInstance* pInst = GetSbData()->pInst;
852         bool bTextMode;
853         bool bCompatibility = ( pInst && pInst->IsCompatibility() );
854         if( bCompatibility )
855         {
856             SbiRuntime* pRT = pInst->pRun;
857             bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
858         }
859         else
860         {
861             bTextMode = true;
862         }
863         if ( nArgCount == 4 )
864         {
865             bTextMode = rPar.Get(4)->GetInteger();
866         }
867         sal_Int32 nPos;
868         const OUString& rToken = rPar.Get(nFirstStringPos+1)->GetOUString();
869 
870         // #97545 Always find empty string
871         if( rToken.isEmpty() )
872         {
873             nPos = nStartPos;
874         }
875         else
876         {
877             if( !bTextMode )
878             {
879                 const OUString& rStr1 = rPar.Get(nFirstStringPos)->GetOUString();
880                 nPos = rStr1.indexOf( rToken, nStartPos - 1 ) + 1;
881             }
882             else
883             {
884                 OUString aStr1 = rPar.Get(nFirstStringPos)->GetOUString();
885                 OUString aToken = rToken;
886 
887                 aStr1 = aStr1.toAsciiUpperCase();
888                 aToken = aToken.toAsciiUpperCase();
889 
890                 nPos = aStr1.indexOf( aToken, nStartPos-1 ) + 1;
891             }
892         }
893         rPar.Get(0)->PutLong( nPos );
894     }
895 }
896 
897 
898 // InstrRev(string1, string2[, start[, compare]])
899 
900 void SbRtl_InStrRev(StarBASIC *, SbxArray & rPar, bool)
901 {
902     std::size_t nArgCount = rPar.Count()-1;
903     if ( nArgCount < 2 )
904     {
905         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
906     }
907     else
908     {
909         OUString aStr1 = rPar.Get(1)->GetOUString();
910         OUString aToken = rPar.Get(2)->GetOUString();
911 
912         sal_Int32 nStartPos = -1;
913         if ( nArgCount >= 3 )
914         {
915             nStartPos = rPar.Get(3)->GetLong();
916             if( nStartPos <= 0 && nStartPos != -1 )
917             {
918                 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
919                 nStartPos = -1;
920             }
921         }
922 
923         SbiInstance* pInst = GetSbData()->pInst;
924         bool bTextMode;
925         bool bCompatibility = ( pInst && pInst->IsCompatibility() );
926         if( bCompatibility )
927         {
928             SbiRuntime* pRT = pInst->pRun;
929             bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
930         }
931         else
932         {
933             bTextMode = true;
934         }
935         if ( nArgCount == 4 )
936         {
937             bTextMode = rPar.Get(4)->GetInteger();
938         }
939         sal_Int32 nStrLen = aStr1.getLength();
940         if( nStartPos == -1 )
941         {
942             nStartPos = nStrLen;
943         }
944 
945         sal_Int32 nPos = 0;
946         if( nStartPos <= nStrLen )
947         {
948             sal_Int32 nTokenLen = aToken.getLength();
949             if( !nTokenLen )
950             {
951                 // Always find empty string
952                 nPos = nStartPos;
953             }
954             else if( nStrLen > 0 )
955             {
956                 if( !bTextMode )
957                 {
958                     nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
959                 }
960                 else
961                 {
962                     aStr1 = aStr1.toAsciiUpperCase();
963                     aToken = aToken.toAsciiUpperCase();
964 
965                     nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
966                 }
967             }
968         }
969         rPar.Get(0)->PutLong( nPos );
970     }
971 }
972 
973 
974 /*
975     Int( 2.8 )  =  2.0
976     Int( -2.8 ) = -3.0
977     Fix( 2.8 )  =  2.0
978     Fix( -2.8 ) = -2.0    <- !!
979 */
980 
981 void SbRtl_Int(StarBASIC *, SbxArray & rPar, bool)
982 {
983     if ( rPar.Count() < 2 )
984         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
985     else
986     {
987         SbxVariableRef pArg = rPar.Get( 1 );
988         double aDouble= pArg->GetDouble();
989         /*
990             floor( 2.8 ) =  2.0
991             floor( -2.8 ) = -3.0
992         */
993         aDouble = floor( aDouble );
994         rPar.Get(0)->PutDouble( aDouble );
995     }
996 }
997 
998 
999 void SbRtl_Fix(StarBASIC *, SbxArray & rPar, bool)
1000 {
1001     if ( rPar.Count() < 2 )
1002         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1003     else
1004     {
1005         SbxVariableRef pArg = rPar.Get( 1 );
1006         double aDouble = pArg->GetDouble();
1007         if ( aDouble >= 0.0 )
1008             aDouble = floor( aDouble );
1009         else
1010             aDouble = ceil( aDouble );
1011         rPar.Get(0)->PutDouble( aDouble );
1012     }
1013 }
1014 
1015 
1016 void SbRtl_LCase(StarBASIC *, SbxArray & rPar, bool)
1017 {
1018     if ( rPar.Count() < 2 )
1019     {
1020         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1021     }
1022     else
1023     {
1024         const CharClass& rCharClass = GetCharClass();
1025         OUString aStr( rPar.Get(1)->GetOUString() );
1026         aStr = rCharClass.lowercase(aStr);
1027         rPar.Get(0)->PutString( aStr );
1028     }
1029 }
1030 
1031 void SbRtl_Left(StarBASIC *, SbxArray & rPar, bool)
1032 {
1033     if ( rPar.Count() < 3 )
1034     {
1035         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1036     }
1037     else
1038     {
1039         OUString aStr( rPar.Get(1)->GetOUString() );
1040         sal_Int32 nResultLen = rPar.Get(2)->GetLong();
1041         if( nResultLen < 0 )
1042         {
1043             nResultLen = 0;
1044             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1045         }
1046         else if(nResultLen > aStr.getLength())
1047         {
1048             nResultLen = aStr.getLength();
1049         }
1050         aStr = aStr.copy(0, nResultLen );
1051         rPar.Get(0)->PutString( aStr );
1052     }
1053 }
1054 
1055 void SbRtl_Log(StarBASIC *, SbxArray & rPar, bool)
1056 {
1057     if ( rPar.Count() < 2 )
1058     {
1059         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1060     }
1061     else
1062     {
1063         double aArg = rPar.Get(1)->GetDouble();
1064         if ( aArg > 0 )
1065         {
1066             double d = log( aArg );
1067             checkArithmeticOverflow( d );
1068             rPar.Get( 0 )->PutDouble( d );
1069         }
1070         else
1071         {
1072             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1073         }
1074     }
1075 }
1076 
1077 void SbRtl_LTrim(StarBASIC *, SbxArray & rPar, bool)
1078 {
1079     if ( rPar.Count() < 2 )
1080     {
1081         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1082     }
1083     else
1084     {
1085         OUString aStr(comphelper::string::stripStart(rPar.Get(1)->GetOUString(), ' '));
1086         rPar.Get(0)->PutString(aStr);
1087     }
1088 }
1089 
1090 
1091 // Mid( String, nStart, nLength )
1092 
1093 void SbRtl_Mid(StarBASIC *, SbxArray & rPar, bool bWrite)
1094 {
1095     int nArgCount = rPar.Count()-1;
1096     if ( nArgCount < 2 )
1097     {
1098         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1099     }
1100     else
1101     {
1102         // #23178: replicate the functionality of Mid$ as a command
1103         // by adding a replacement-string as a fourth parameter.
1104         // In contrast to the original the third parameter (nLength)
1105         // can't be left out here. That's considered in bWrite already.
1106         if( nArgCount == 4 )
1107         {
1108             bWrite = true;
1109         }
1110         OUString aArgStr = rPar.Get(1)->GetOUString();
1111         sal_Int32 nStartPos = rPar.Get(2)->GetLong();
1112         if ( nStartPos < 1 )
1113         {
1114             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1115         }
1116         else
1117         {
1118             nStartPos--;
1119             sal_Int32 nLen = -1;
1120             bool bWriteNoLenParam = false;
1121             if ( nArgCount == 3 || bWrite )
1122             {
1123                 sal_Int32 n = rPar.Get(3)->GetLong();
1124                 if( bWrite && n == -1 )
1125                 {
1126                     bWriteNoLenParam = true;
1127                 }
1128                 nLen = n;
1129             }
1130             if ( bWrite )
1131             {
1132                 sal_Int32 nArgLen = aArgStr.getLength();
1133                 if( nStartPos > nArgLen )
1134                 {
1135                     SbiInstance* pInst = GetSbData()->pInst;
1136                     bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1137                     if( bCompatibility )
1138                     {
1139                         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1140                         return;
1141                     }
1142                     nStartPos = nArgLen;
1143                 }
1144 
1145                 OUString aReplaceStr = rPar.Get(4)->GetOUString();
1146                 sal_Int32 nReplaceStrLen = aReplaceStr.getLength();
1147                 sal_Int32 nReplaceLen;
1148                 if( bWriteNoLenParam )
1149                 {
1150                     nReplaceLen = nArgLen - nStartPos;
1151                 }
1152                 else
1153                 {
1154                     nReplaceLen = nLen;
1155                     if( nReplaceLen < 0 || nReplaceLen > nArgLen - nStartPos )
1156                     {
1157                         nReplaceLen = nArgLen - nStartPos;
1158                     }
1159                 }
1160 
1161                 OUStringBuffer aResultStr = aArgStr;
1162                 sal_Int32 nErase = nReplaceLen;
1163                 aResultStr.remove( nStartPos, nErase );
1164                 aResultStr.insert(
1165                     nStartPos, aReplaceStr.getStr(), std::min(nReplaceLen, nReplaceStrLen));
1166 
1167                 rPar.Get(1)->PutString( aResultStr.makeStringAndClear() );
1168             }
1169             else
1170             {
1171                 OUString aResultStr;
1172                 if (nStartPos > aArgStr.getLength())
1173                 {
1174                     // do nothing
1175                 }
1176                 else if(nArgCount == 2)
1177                 {
1178                     aResultStr = aArgStr.copy( nStartPos);
1179                 }
1180                 else
1181                 {
1182                     if (nLen < 0)
1183                         nLen = 0;
1184                     if(nStartPos + nLen > aArgStr.getLength())
1185                     {
1186                         nLen = aArgStr.getLength() - nStartPos;
1187                     }
1188                     if (nLen > 0)
1189                         aResultStr = aArgStr.copy( nStartPos, nLen );
1190                 }
1191                 rPar.Get(0)->PutString( aResultStr );
1192             }
1193         }
1194     }
1195 }
1196 
1197 void SbRtl_Oct(StarBASIC *, SbxArray & rPar, bool)
1198 {
1199     if ( rPar.Count() < 2 )
1200     {
1201         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1202     }
1203     else
1204     {
1205         char aBuffer[16];
1206         SbxVariableRef pArg = rPar.Get( 1 );
1207         if ( pArg->IsInteger() )
1208         {
1209             snprintf( aBuffer, sizeof(aBuffer), "%o", pArg->GetInteger() );
1210         }
1211         else
1212         {
1213             snprintf( aBuffer, sizeof(aBuffer), "%lo", static_cast<long unsigned int>(pArg->GetLong()) );
1214         }
1215         rPar.Get(0)->PutString( OUString::createFromAscii( aBuffer ) );
1216     }
1217 }
1218 
1219 // Replace(expression, find, replace[, start[, count[, compare]]])
1220 
1221 void SbRtl_Replace(StarBASIC *, SbxArray & rPar, bool)
1222 {
1223     std::size_t nArgCount = rPar.Count()-1;
1224     if ( nArgCount < 3 || nArgCount > 6 )
1225     {
1226         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1227     }
1228     else
1229     {
1230         OUString aExpStr = rPar.Get(1)->GetOUString();
1231         OUString aFindStr = rPar.Get(2)->GetOUString();
1232         OUString aReplaceStr = rPar.Get(3)->GetOUString();
1233 
1234         sal_Int32 lStartPos = 1;
1235         if ( nArgCount >= 4 )
1236         {
1237             if( rPar.Get(4)->GetType() != SbxEMPTY )
1238             {
1239                 lStartPos = rPar.Get(4)->GetLong();
1240             }
1241             if( lStartPos < 1)
1242             {
1243                 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1244                 lStartPos = 1;
1245             }
1246         }
1247 
1248         sal_Int32 lCount = -1;
1249         if( nArgCount >=5 )
1250         {
1251             if( rPar.Get(5)->GetType() != SbxEMPTY )
1252             {
1253                 lCount = rPar.Get(5)->GetLong();
1254             }
1255             if( lCount < -1)
1256             {
1257                 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1258                 lCount = -1;
1259             }
1260         }
1261 
1262         SbiInstance* pInst = GetSbData()->pInst;
1263         bool bTextMode;
1264         bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1265         if( bCompatibility )
1266         {
1267             SbiRuntime* pRT = pInst->pRun;
1268             bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
1269         }
1270         else
1271         {
1272             bTextMode = true;
1273         }
1274         if ( nArgCount == 6 )
1275         {
1276             bTextMode = rPar.Get(6)->GetInteger();
1277         }
1278         sal_Int32 nExpStrLen = aExpStr.getLength();
1279         sal_Int32 nFindStrLen = aFindStr.getLength();
1280         sal_Int32 nReplaceStrLen = aReplaceStr.getLength();
1281 
1282         if( lStartPos <= nExpStrLen )
1283         {
1284             sal_Int32 nPos = lStartPos - 1;
1285             sal_Int32 nCounts = 0;
1286             while( lCount == -1 || lCount > nCounts )
1287             {
1288                 OUString aSrcStr( aExpStr );
1289                 if( bTextMode )
1290                 {
1291                     aSrcStr = aSrcStr.toAsciiUpperCase();
1292                     aFindStr = aFindStr.toAsciiUpperCase();
1293                 }
1294                 nPos = aSrcStr.indexOf( aFindStr, nPos );
1295                 if( nPos >= 0 )
1296                 {
1297                     aExpStr = aExpStr.replaceAt( nPos, nFindStrLen, aReplaceStr );
1298                     nPos = nPos + nReplaceStrLen;
1299                     nCounts++;
1300                 }
1301                 else
1302                 {
1303                     break;
1304                 }
1305             }
1306         }
1307         rPar.Get(0)->PutString( aExpStr.copy( lStartPos - 1 )  );
1308     }
1309 }
1310 
1311 void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool)
1312 {
1313     if ( rPar.Count() < 3 )
1314     {
1315         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1316     }
1317     else
1318     {
1319         const OUString& rStr = rPar.Get(1)->GetOUString();
1320         int nResultLen = rPar.Get(2)->GetLong();
1321         if( nResultLen < 0 )
1322         {
1323             nResultLen = 0;
1324             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1325         }
1326         int nStrLen = rStr.getLength();
1327         if ( nResultLen > nStrLen )
1328         {
1329             nResultLen = nStrLen;
1330         }
1331         OUString aResultStr = rStr.copy( nStrLen - nResultLen );
1332         rPar.Get(0)->PutString( aResultStr );
1333     }
1334 }
1335 
1336 void SbRtl_RTL(StarBASIC * pBasic, SbxArray & rPar, bool)
1337 {
1338     rPar.Get( 0 )->PutObject( pBasic->getRTL().get() );
1339 }
1340 
1341 void SbRtl_RTrim(StarBASIC *, SbxArray & rPar, bool)
1342 {
1343     if ( rPar.Count() < 2 )
1344     {
1345         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1346     }
1347     else
1348     {
1349         OUString aStr(comphelper::string::stripEnd(rPar.Get(1)->GetOUString(), ' '));
1350         rPar.Get(0)->PutString(aStr);
1351     }
1352 }
1353 
1354 void SbRtl_Sgn(StarBASIC *, SbxArray & rPar, bool)
1355 {
1356     if ( rPar.Count() < 2 )
1357     {
1358         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1359     }
1360     else
1361     {
1362         double aDouble = rPar.Get(1)->GetDouble();
1363         sal_Int16 nResult = 0;
1364         if ( aDouble > 0 )
1365         {
1366             nResult = 1;
1367         }
1368         else if ( aDouble < 0 )
1369         {
1370             nResult = -1;
1371         }
1372         rPar.Get(0)->PutInteger( nResult );
1373     }
1374 }
1375 
1376 void SbRtl_Space(StarBASIC *, SbxArray & rPar, bool)
1377 {
1378     if ( rPar.Count() < 2 )
1379     {
1380         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1381     }
1382     else
1383     {
1384         OUStringBuffer aBuf;
1385         string::padToLength(aBuf, rPar.Get(1)->GetLong(), ' ');
1386         rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1387     }
1388 }
1389 
1390 void SbRtl_Spc(StarBASIC *, SbxArray & rPar, bool)
1391 {
1392     if ( rPar.Count() < 2 )
1393     {
1394         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1395     }
1396     else
1397     {
1398         OUStringBuffer aBuf;
1399         string::padToLength(aBuf, rPar.Get(1)->GetLong(), ' ');
1400         rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1401     }
1402 }
1403 
1404 void SbRtl_Sqr(StarBASIC *, SbxArray & rPar, bool)
1405 {
1406     if ( rPar.Count() < 2 )
1407     {
1408         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1409     }
1410     else
1411     {
1412         double aDouble = rPar.Get(1)->GetDouble();
1413         if ( aDouble >= 0 )
1414         {
1415             rPar.Get(0)->PutDouble( sqrt( aDouble ));
1416         }
1417         else
1418         {
1419             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1420         }
1421     }
1422 }
1423 
1424 void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
1425 {
1426     if ( rPar.Count() < 2 )
1427     {
1428         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1429     }
1430     else
1431     {
1432         OUString aStr;
1433         OUString aStrNew("");
1434         SbxVariableRef pArg = rPar.Get( 1 );
1435         pArg->Format( aStr );
1436 
1437         // Numbers start with a space
1438         if( pArg->IsNumericRTL() )
1439         {
1440             // replace commas by points so that it's symmetric to Val!
1441             aStr = aStr.replaceFirst( ",", "." );
1442 
1443             SbiInstance* pInst = GetSbData()->pInst;
1444             bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1445             if( bCompatibility )
1446             {
1447                 sal_Int32 nLen = aStr.getLength();
1448 
1449                 const sal_Unicode* pBuf = aStr.getStr();
1450 
1451                 bool bNeg = ( pBuf[0] == '-' );
1452                 sal_Int32 iZeroSearch = 0;
1453                 if( bNeg )
1454                 {
1455                     aStrNew += "-";
1456                     iZeroSearch++;
1457                 }
1458                 else
1459                 {
1460                     if( pBuf[0] != ' ' )
1461                     {
1462                         aStrNew += " ";
1463                     }
1464                 }
1465                 sal_Int32 iNext = iZeroSearch + 1;
1466                 if( pBuf[iZeroSearch] == '0' && nLen > iNext && pBuf[iNext] == '.' )
1467                 {
1468                     iZeroSearch += 1;
1469                 }
1470                 aStrNew += aStr.copy(iZeroSearch);
1471             }
1472             else
1473             {
1474                 aStrNew = " " + aStr;
1475             }
1476         }
1477         else
1478         {
1479             aStrNew = aStr;
1480         }
1481         rPar.Get(0)->PutString( aStrNew );
1482     }
1483 }
1484 
1485 void SbRtl_StrComp(StarBASIC *, SbxArray & rPar, bool)
1486 {
1487     if ( rPar.Count() < 3 )
1488     {
1489         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1490         rPar.Get(0)->PutEmpty();
1491         return;
1492     }
1493     const OUString& rStr1 = rPar.Get(1)->GetOUString();
1494     const OUString& rStr2 = rPar.Get(2)->GetOUString();
1495 
1496     SbiInstance* pInst = GetSbData()->pInst;
1497     bool bTextCompare;
1498     bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1499     if( bCompatibility )
1500     {
1501         SbiRuntime* pRT = pInst->pRun;
1502         bTextCompare = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
1503     }
1504     else
1505     {
1506         bTextCompare = true;
1507     }
1508     if ( rPar.Count() == 4 )
1509         bTextCompare = rPar.Get(3)->GetInteger();
1510 
1511     if( !bCompatibility )
1512     {
1513         bTextCompare = !bTextCompare;
1514     }
1515     sal_Int32 nRetValue = 0;
1516     if( bTextCompare )
1517     {
1518         ::utl::TransliterationWrapper* pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
1519         if( !pTransliterationWrapper )
1520         {
1521             uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
1522             GetSbData()->pTransliterationWrapper.reset(
1523                 new ::utl::TransliterationWrapper( xContext,
1524                     TransliterationFlags::IGNORE_CASE |
1525                     TransliterationFlags::IGNORE_KANA |
1526                     TransliterationFlags::IGNORE_WIDTH ) );
1527             pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
1528         }
1529 
1530         LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
1531         pTransliterationWrapper->loadModuleIfNeeded( eLangType );
1532         nRetValue = pTransliterationWrapper->compareString( rStr1, rStr2 );
1533     }
1534     else
1535     {
1536         sal_Int32 aResult;
1537         aResult = rStr1.compareTo( rStr2 );
1538         if ( aResult < 0  )
1539         {
1540             nRetValue = -1;
1541         }
1542         else if ( aResult > 0)
1543         {
1544             nRetValue = 1;
1545         }
1546     }
1547     rPar.Get(0)->PutInteger( sal::static_int_cast< sal_Int16 >( nRetValue ) );
1548 }
1549 
1550 void SbRtl_String(StarBASIC *, SbxArray & rPar, bool)
1551 {
1552     if ( rPar.Count() < 2 )
1553     {
1554         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1555     }
1556     else
1557     {
1558         sal_Unicode aFiller;
1559         sal_Int32 lCount = rPar.Get(1)->GetLong();
1560         if( lCount < 0 || lCount > 0xffff )
1561         {
1562             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1563         }
1564         if( rPar.Get(2)->GetType() == SbxINTEGER )
1565         {
1566             aFiller = static_cast<sal_Unicode>(rPar.Get(2)->GetInteger());
1567         }
1568         else
1569         {
1570             const OUString& rStr = rPar.Get(2)->GetOUString();
1571             aFiller = rStr[0];
1572         }
1573         OUStringBuffer aBuf(lCount);
1574         string::padToLength(aBuf, lCount, aFiller);
1575         rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1576     }
1577 }
1578 
1579 void SbRtl_Tab(StarBASIC *, SbxArray & rPar, bool)
1580 {
1581     if ( rPar.Count() < 2 )
1582         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1583     else
1584     {
1585         OUStringBuffer aStr;
1586         comphelper::string::padToLength(aStr, rPar.Get(1)->GetLong(), '\t');
1587         rPar.Get(0)->PutString(aStr.makeStringAndClear());
1588     }
1589 }
1590 
1591 void SbRtl_Tan(StarBASIC *, SbxArray & rPar, bool)
1592 {
1593     if ( rPar.Count() < 2 )
1594     {
1595         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1596     }
1597     else
1598     {
1599         SbxVariableRef pArg = rPar.Get( 1 );
1600         rPar.Get( 0 )->PutDouble( tan( pArg->GetDouble() ) );
1601     }
1602 }
1603 
1604 void SbRtl_UCase(StarBASIC *, SbxArray & rPar, bool)
1605 {
1606     if ( rPar.Count() < 2 )
1607     {
1608         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1609     }
1610     else
1611     {
1612         const CharClass& rCharClass = GetCharClass();
1613         OUString aStr( rPar.Get(1)->GetOUString() );
1614         aStr = rCharClass.uppercase( aStr );
1615         rPar.Get(0)->PutString( aStr );
1616     }
1617 }
1618 
1619 
1620 void SbRtl_Val(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
1621 {
1622     (void)pBasic;
1623     (void)bWrite;
1624 
1625     if ( rPar.Count() < 2 )
1626     {
1627         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1628     }
1629     else
1630     {
1631         double nResult = 0.0;
1632         char* pEndPtr;
1633 
1634         OUString aStr( rPar.Get(1)->GetOUString() );
1635 
1636         FilterWhiteSpace( aStr );
1637         if ( aStr.getLength() > 1 && aStr[0] == '&' )
1638         {
1639             int nRadix = 10;
1640             char aChar = static_cast<char>(aStr[1]);
1641             if ( aChar == 'h' || aChar == 'H' )
1642             {
1643                 nRadix = 16;
1644             }
1645             else if ( aChar == 'o' || aChar == 'O' )
1646             {
1647                 nRadix = 8;
1648             }
1649             if ( nRadix != 10 )
1650             {
1651                 OString aByteStr(OUStringToOString(aStr, osl_getThreadTextEncoding()));
1652                 sal_Int16 nlResult = static_cast<sal_Int16>(strtol( aByteStr.getStr()+2, &pEndPtr, nRadix));
1653                 nResult = static_cast<double>(nlResult);
1654             }
1655         }
1656         else
1657         {
1658             rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
1659             sal_Int32 nParseEnd = 0;
1660             nResult = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd );
1661             if ( eStatus != rtl_math_ConversionStatus_Ok )
1662                 StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW );
1663             /* TODO: we should check whether all characters were parsed here,
1664              * but earlier code silently ignored trailing nonsense such as "1x"
1665              * resulting in 1 with the side effect that any alpha-only-string
1666              * like "x" resulted in 0. Not changing that now (2013-03-22) as
1667              * user macros may rely on it. */
1668 #if 0
1669             else if ( nParseEnd != aStr.getLength() )
1670                 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
1671 #endif
1672         }
1673 
1674         rPar.Get(0)->PutDouble( nResult );
1675     }
1676 }
1677 
1678 
1679 // Helper functions for date conversion
1680 sal_Int16 implGetDateDay( double aDate )
1681 {
1682     aDate -= 2.0; // standardize: 1.1.1900 => 0.0
1683     aDate = floor( aDate );
1684     Date aRefDate( 1, 1, 1900 );
1685     aRefDate.AddDays( aDate );
1686 
1687     sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetDay() );
1688     return nRet;
1689 }
1690 
1691 sal_Int16 implGetDateMonth( double aDate )
1692 {
1693     Date aRefDate( 1,1,1900 );
1694     sal_Int32 nDays = static_cast<sal_Int32>(aDate);
1695     nDays -= 2; // standardize: 1.1.1900 => 0.0
1696     aRefDate.AddDays( nDays );
1697     sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetMonth() );
1698     return nRet;
1699 }
1700 
1701 css::util::Date SbxDateToUNODate( const SbxValue* const pVal )
1702 {
1703     double aDate = pVal->GetDate();
1704 
1705     css::util::Date aUnoDate;
1706     aUnoDate.Day   = implGetDateDay  ( aDate );
1707     aUnoDate.Month = implGetDateMonth( aDate );
1708     aUnoDate.Year  = implGetDateYear ( aDate );
1709 
1710     return aUnoDate;
1711 }
1712 
1713 void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate)
1714 {
1715     double dDate;
1716     if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, SbDateCorrection::None, dDate ) )
1717     {
1718         pVal->PutDate( dDate );
1719     }
1720 }
1721 
1722 // Function to convert date to UNO date (com.sun.star.util.Date)
1723 void SbRtl_CDateToUnoDate(StarBASIC *, SbxArray & rPar, bool)
1724 {
1725     if ( rPar.Count() != 2 )
1726     {
1727         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1728         return;
1729     }
1730 
1731     unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODate(rPar.Get(1))));
1732 }
1733 
1734 // Function to convert date from UNO date (com.sun.star.util.Date)
1735 void SbRtl_CDateFromUnoDate(StarBASIC *, SbxArray & rPar, bool)
1736 {
1737     if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
1738     {
1739         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1740         return;
1741     }
1742 
1743     Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Date>::get()));
1744     css::util::Date aUnoDate;
1745     if(aAny >>= aUnoDate)
1746         SbxDateFromUNODate(rPar.Get(0), aUnoDate);
1747     else
1748         SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1749 }
1750 
1751 css::util::Time SbxDateToUNOTime( const SbxValue* const pVal )
1752 {
1753     double aDate = pVal->GetDate();
1754 
1755     css::util::Time aUnoTime;
1756     aUnoTime.Hours       = implGetHour      ( aDate );
1757     aUnoTime.Minutes     = implGetMinute    ( aDate );
1758     aUnoTime.Seconds     = implGetSecond    ( aDate );
1759     aUnoTime.NanoSeconds = 0;
1760 
1761     return aUnoTime;
1762 }
1763 
1764 void SbxDateFromUNOTime( SbxValue *pVal, const css::util::Time& aUnoTime)
1765 {
1766     pVal->PutDate( implTimeSerial(aUnoTime.Hours, aUnoTime.Minutes, aUnoTime.Seconds) );
1767 }
1768 
1769 // Function to convert date to UNO time (com.sun.star.util.Time)
1770 void SbRtl_CDateToUnoTime(StarBASIC *, SbxArray & rPar, bool)
1771 {
1772     if ( rPar.Count() != 2 )
1773     {
1774         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1775         return;
1776     }
1777 
1778     unoToSbxValue(rPar.Get(0), Any(SbxDateToUNOTime(rPar.Get(1))));
1779 }
1780 
1781 // Function to convert date from UNO time (com.sun.star.util.Time)
1782 void SbRtl_CDateFromUnoTime(StarBASIC *, SbxArray & rPar, bool)
1783 {
1784     if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
1785     {
1786         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1787         return;
1788     }
1789 
1790     Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Time>::get()));
1791     css::util::Time aUnoTime;
1792     if(aAny >>= aUnoTime)
1793         SbxDateFromUNOTime(rPar.Get(0), aUnoTime);
1794     else
1795         SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1796 }
1797 
1798 css::util::DateTime SbxDateToUNODateTime( const SbxValue* const pVal )
1799 {
1800     double aDate = pVal->GetDate();
1801 
1802     css::util::DateTime aUnoDT;
1803     aUnoDT.Day   = implGetDateDay  ( aDate );
1804     aUnoDT.Month = implGetDateMonth( aDate );
1805     aUnoDT.Year  = implGetDateYear ( aDate );
1806     aUnoDT.Hours       = implGetHour      ( aDate );
1807     aUnoDT.Minutes     = implGetMinute    ( aDate );
1808     aUnoDT.Seconds     = implGetSecond    ( aDate );
1809     aUnoDT.NanoSeconds = 0;
1810 
1811     return aUnoDT;
1812 }
1813 
1814 void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT)
1815 {
1816     double dDate(0.0);
1817     if( implDateTimeSerial( aUnoDT.Year, aUnoDT.Month, aUnoDT.Day,
1818                             aUnoDT.Hours, aUnoDT.Minutes, aUnoDT.Seconds,
1819                             dDate ) )
1820     {
1821         pVal->PutDate( dDate );
1822     }
1823 }
1824 
1825 // Function to convert date to UNO date (com.sun.star.util.Date)
1826 void SbRtl_CDateToUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
1827 {
1828     if ( rPar.Count() != 2 )
1829     {
1830         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1831         return;
1832     }
1833 
1834     unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODateTime(rPar.Get(1))));
1835 }
1836 
1837 // Function to convert date from UNO date (com.sun.star.util.Date)
1838 void SbRtl_CDateFromUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
1839 {
1840     if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
1841     {
1842         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1843         return;
1844     }
1845 
1846     Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::DateTime>::get()));
1847     css::util::DateTime aUnoDT;
1848     if(aAny >>= aUnoDT)
1849         SbxDateFromUNODateTime(rPar.Get(0), aUnoDT);
1850     else
1851         SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1852 }
1853 
1854 // Function to convert date to ISO 8601 date format YYYYMMDD
1855 void SbRtl_CDateToIso(StarBASIC *, SbxArray & rPar, bool)
1856 {
1857     if ( rPar.Count() == 2 )
1858     {
1859         double aDate = rPar.Get(1)->GetDate();
1860 
1861         // Date may actually even be -YYYYYMMDD
1862         char Buffer[11];
1863         sal_Int16 nYear = implGetDateYear( aDate );
1864         snprintf( Buffer, sizeof( Buffer ), (nYear < 0 ? "%05d%02d%02d" : "%04d%02d%02d"),
1865                 static_cast<int>(nYear),
1866                 static_cast<int>(implGetDateMonth( aDate )),
1867                 static_cast<int>(implGetDateDay( aDate )) );
1868         OUString aRetStr = OUString::createFromAscii( Buffer );
1869         rPar.Get(0)->PutString( aRetStr );
1870     }
1871     else
1872     {
1873         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1874     }
1875 }
1876 
1877 // Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD
1878 // And even YYMMDD for compatibility, sigh...
1879 void SbRtl_CDateFromIso(StarBASIC *, SbxArray & rPar, bool)
1880 {
1881     if ( rPar.Count() == 2 )
1882     {
1883         do
1884         {
1885             OUString aStr = rPar.Get(1)->GetOUString();
1886             if (aStr.isEmpty())
1887                 break;
1888 
1889             // Valid formats are
1890             // YYYYMMDD    -YYYMMDD     YYYYYMMDD    -YYYYYMMDD    YYMMDD
1891             // YYYY-MM-DD  -YYYY-MM-DD  YYYYY-MM-DD  -YYYYY-MM-DD
1892 
1893             sal_Int32 nSign = 1;
1894             if (aStr[0] == '-')
1895             {
1896                 nSign = -1;
1897                 aStr = aStr.copy(1);
1898             }
1899             const sal_Int32 nLen = aStr.getLength();
1900 
1901             // Signed YYMMDD two digit year is invalid.
1902             if (nLen == 6 && nSign == -1)
1903                 break;
1904 
1905             // Now valid
1906             // YYYYMMDD    YYYYYMMDD    YYMMDD
1907             // YYYY-MM-DD  YYYYY-MM-DD
1908             if (nLen != 6 && (nLen < 8 || 11 < nLen))
1909                 break;
1910 
1911             bool bUseTwoDigitYear = false;
1912             OUString aYearStr, aMonthStr, aDayStr;
1913             if (nLen == 6 || nLen == 8 || nLen == 9)
1914             {
1915                 // ((Y)YY)YYMMDD
1916                 if (!comphelper::string::isdigitAsciiString(aStr))
1917                     break;
1918 
1919                 const sal_Int32 nMonthPos = (nLen == 8 ? 4 : (nLen == 6 ? 2 : 5));
1920                 if (nMonthPos == 2)
1921                     bUseTwoDigitYear = true;
1922                 aYearStr  = aStr.copy( 0, nMonthPos );
1923                 aMonthStr = aStr.copy( nMonthPos, 2 );
1924                 aDayStr   = aStr.copy( nMonthPos + 2, 2 );
1925             }
1926             else
1927             {
1928                 // (Y)YYYY-MM-DD
1929                 const sal_Int32 nMonthSep = (nLen == 11 ? 5 : 4);
1930                 if (aStr.indexOf('-') != nMonthSep)
1931                     break;
1932                 if (aStr.indexOf('-', nMonthSep + 1) != nMonthSep + 3)
1933                     break;
1934 
1935                 aYearStr  = aStr.copy( 0, nMonthSep );
1936                 aMonthStr = aStr.copy( nMonthSep + 1, 2 );
1937                 aDayStr   = aStr.copy( nMonthSep + 4, 2 );
1938                 if (    !comphelper::string::isdigitAsciiString(aYearStr) ||
1939                         !comphelper::string::isdigitAsciiString(aMonthStr) ||
1940                         !comphelper::string::isdigitAsciiString(aDayStr))
1941                     break;
1942             }
1943 
1944             double dDate;
1945             if (!implDateSerial( static_cast<sal_Int16>(nSign * aYearStr.toInt32()),
1946                         static_cast<sal_Int16>(aMonthStr.toInt32()), static_cast<sal_Int16>(aDayStr.toInt32()),
1947                         bUseTwoDigitYear, SbDateCorrection::None, dDate ))
1948                 break;
1949 
1950             rPar.Get(0)->PutDate( dDate );
1951 
1952             return;
1953         }
1954         while (false);
1955 
1956         SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER );
1957     }
1958     else
1959     {
1960         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1961     }
1962 }
1963 
1964 void SbRtl_DateSerial(StarBASIC *, SbxArray & rPar, bool)
1965 {
1966     if ( rPar.Count() < 4 )
1967     {
1968         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1969         return;
1970     }
1971     sal_Int16 nYear = rPar.Get(1)->GetInteger();
1972     sal_Int16 nMonth = rPar.Get(2)->GetInteger();
1973     sal_Int16 nDay = rPar.Get(3)->GetInteger();
1974 
1975     double dDate;
1976     if( implDateSerial( nYear, nMonth, nDay, true, SbDateCorrection::RollOver, dDate ) )
1977     {
1978         rPar.Get(0)->PutDate( dDate );
1979     }
1980 }
1981 
1982 void SbRtl_TimeSerial(StarBASIC *, SbxArray & rPar, bool)
1983 {
1984     if ( rPar.Count() < 4 )
1985     {
1986         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1987         return;
1988     }
1989     sal_Int16 nHour = rPar.Get(1)->GetInteger();
1990     if ( nHour == 24 )
1991     {
1992         nHour = 0;                      // because of UNO DateTimes, which go till 24 o'clock
1993     }
1994     sal_Int16 nMinute = rPar.Get(2)->GetInteger();
1995     sal_Int16 nSecond = rPar.Get(3)->GetInteger();
1996     if ((nHour < 0 || nHour > 23)   ||
1997         (nMinute < 0 || nMinute > 59 )  ||
1998         (nSecond < 0 || nSecond > 59 ))
1999     {
2000         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2001         return;
2002     }
2003 
2004     rPar.Get(0)->PutDate( implTimeSerial(nHour, nMinute, nSecond) ); // JSM
2005 }
2006 
2007 void SbRtl_DateValue(StarBASIC *, SbxArray & rPar, bool)
2008 {
2009     if ( rPar.Count() < 2 )
2010     {
2011         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2012     }
2013     else
2014     {
2015         // #39629 check GetSbData()->pInst, can be called from the URL line
2016         std::shared_ptr<SvNumberFormatter> pFormatter;
2017         if( GetSbData()->pInst )
2018         {
2019             pFormatter = GetSbData()->pInst->GetNumberFormatter();
2020         }
2021         else
2022         {
2023             sal_uInt32 n;   // Dummy
2024             pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
2025         }
2026 
2027         LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
2028         sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
2029         double fResult;
2030         OUString aStr( rPar.Get(1)->GetOUString() );
2031         bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
2032         SvNumFormatType nType = pFormatter->GetType( nIndex );
2033 
2034         // DateValue("February 12, 1969") raises error if the system locale is not en_US
2035         // It seems that both locale number formatter and English number
2036         // formatter are supported in Visual Basic.
2037         if( !bSuccess && ( eLangType != LANGUAGE_ENGLISH_US ) )
2038         {
2039             // Try using LANGUAGE_ENGLISH_US to get the date value.
2040             nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US);
2041             bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
2042             nType = pFormatter->GetType( nIndex );
2043         }
2044 
2045         if(bSuccess && (nType==SvNumFormatType::DATE || nType==SvNumFormatType::DATETIME))
2046         {
2047             if ( nType == SvNumFormatType::DATETIME )
2048             {
2049                 // cut time
2050                 if ( fResult  > 0.0 )
2051                 {
2052                     fResult = floor( fResult );
2053                 }
2054                 else
2055                 {
2056                     fResult = ceil( fResult );
2057                 }
2058             }
2059             rPar.Get(0)->PutDate( fResult );
2060         }
2061         else
2062         {
2063             StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
2064         }
2065     }
2066 }
2067 
2068 void SbRtl_TimeValue(StarBASIC *, SbxArray & rPar, bool)
2069 {
2070     if ( rPar.Count() < 2 )
2071     {
2072         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2073     }
2074     else
2075     {
2076         std::shared_ptr<SvNumberFormatter> pFormatter;
2077         if( GetSbData()->pInst )
2078             pFormatter = GetSbData()->pInst->GetNumberFormatter();
2079         else
2080         {
2081             sal_uInt32 n;
2082             pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
2083         }
2084 
2085         sal_uInt32 nIndex = 0;
2086         double fResult;
2087         bool bSuccess = pFormatter->IsNumberFormat( rPar.Get(1)->GetOUString(),
2088                                                    nIndex, fResult );
2089         SvNumFormatType nType = pFormatter->GetType(nIndex);
2090         if(bSuccess && (nType==SvNumFormatType::TIME||nType==SvNumFormatType::DATETIME))
2091         {
2092             if ( nType == SvNumFormatType::DATETIME )
2093             {
2094                 // cut days
2095                 fResult = fmod( fResult, 1 );
2096             }
2097             rPar.Get(0)->PutDate( fResult );
2098         }
2099         else
2100         {
2101             StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
2102         }
2103     }
2104 }
2105 
2106 void SbRtl_Day(StarBASIC *, SbxArray & rPar, bool)
2107 {
2108     if ( rPar.Count() < 2 )
2109     {
2110         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2111     }
2112     else
2113     {
2114         SbxVariableRef pArg = rPar.Get( 1 );
2115         double aDate = pArg->GetDate();
2116 
2117         sal_Int16 nDay = implGetDateDay( aDate );
2118         rPar.Get(0)->PutInteger( nDay );
2119     }
2120 }
2121 
2122 void SbRtl_Year(StarBASIC *, SbxArray & rPar, bool)
2123 {
2124     if ( rPar.Count() < 2 )
2125     {
2126         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2127     }
2128     else
2129     {
2130         sal_Int16 nYear = implGetDateYear( rPar.Get(1)->GetDate() );
2131         rPar.Get(0)->PutInteger( nYear );
2132     }
2133 }
2134 
2135 sal_Int16 implGetHour( double dDate )
2136 {
2137     double nFrac = dDate - floor( dDate );
2138     nFrac *= 86400.0;
2139     sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
2140     sal_Int16 nHour = static_cast<sal_Int16>(nSeconds / 3600);
2141     return nHour;
2142 }
2143 
2144 void SbRtl_Hour(StarBASIC *, SbxArray & rPar, bool)
2145 {
2146     if ( rPar.Count() < 2 )
2147     {
2148         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2149     }
2150     else
2151     {
2152         double nArg = rPar.Get(1)->GetDate();
2153         sal_Int16 nHour = implGetHour( nArg );
2154         rPar.Get(0)->PutInteger( nHour );
2155     }
2156 }
2157 
2158 void SbRtl_Minute(StarBASIC *, SbxArray & rPar, bool)
2159 {
2160     if ( rPar.Count() < 2 )
2161     {
2162         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2163     }
2164     else
2165     {
2166         double nArg = rPar.Get(1)->GetDate();
2167         sal_Int16 nMin = implGetMinute( nArg );
2168         rPar.Get(0)->PutInteger( nMin );
2169     }
2170 }
2171 
2172 void SbRtl_Month(StarBASIC *, SbxArray & rPar, bool)
2173 {
2174     if ( rPar.Count() < 2 )
2175     {
2176         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2177     }
2178     else
2179     {
2180         sal_Int16 nMonth = implGetDateMonth( rPar.Get(1)->GetDate() );
2181         rPar.Get(0)->PutInteger( nMonth );
2182     }
2183 }
2184 
2185 sal_Int16 implGetSecond( double dDate )
2186 {
2187     double nFrac = dDate - floor( dDate );
2188     nFrac *= 86400.0;
2189     sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
2190     sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds / 3600);
2191     nSeconds -= nTemp * 3600;
2192     nTemp = static_cast<sal_Int16>(nSeconds / 60);
2193     nSeconds -= nTemp * 60;
2194 
2195     sal_Int16 nRet = static_cast<sal_Int16>(nSeconds);
2196     return nRet;
2197 }
2198 
2199 void SbRtl_Second(StarBASIC *, SbxArray & rPar, bool)
2200 {
2201     if ( rPar.Count() < 2 )
2202     {
2203         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2204     }
2205     else
2206     {
2207         double nArg = rPar.Get(1)->GetDate();
2208         sal_Int16 nSecond = implGetSecond( nArg );
2209         rPar.Get(0)->PutInteger( nSecond );
2210     }
2211 }
2212 
2213 double Now_Impl()
2214 {
2215     DateTime aDateTime( DateTime::SYSTEM );
2216     double aSerial = static_cast<double>(GetDayDiff( aDateTime ));
2217     long nSeconds = aDateTime.GetHour();
2218     nSeconds *= 3600;
2219     nSeconds += aDateTime.GetMin() * 60;
2220     nSeconds += aDateTime.GetSec();
2221     double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
2222     aSerial += nDays;
2223     return aSerial;
2224 }
2225 
2226 // Date Now()
2227 
2228 void SbRtl_Now(StarBASIC *, SbxArray & rPar, bool)
2229 {
2230     rPar.Get(0)->PutDate( Now_Impl() );
2231 }
2232 
2233 // Date Time()
2234 
2235 void SbRtl_Time(StarBASIC *, SbxArray & rPar, bool bWrite)
2236 {
2237     if ( !bWrite )
2238     {
2239         tools::Time aTime( tools::Time::SYSTEM );
2240         SbxVariable* pMeth = rPar.Get( 0 );
2241         OUString aRes;
2242         if( pMeth->IsFixed() )
2243         {
2244             // Time$: hh:mm:ss
2245             char buf[ 20 ];
2246             snprintf( buf, sizeof(buf), "%02d:%02d:%02d",
2247                       aTime.GetHour(), aTime.GetMin(), aTime.GetSec() );
2248             aRes = OUString::createFromAscii( buf );
2249         }
2250         else
2251         {
2252             // Time: system dependent
2253             long nSeconds=aTime.GetHour();
2254             nSeconds *= 3600;
2255             nSeconds += aTime.GetMin() * 60;
2256             nSeconds += aTime.GetSec();
2257             double nDays = static_cast<double>(nSeconds) * ( 1.0 / (24.0*3600.0) );
2258             Color* pCol;
2259 
2260             std::shared_ptr<SvNumberFormatter> pFormatter;
2261             sal_uInt32 nIndex;
2262             if( GetSbData()->pInst )
2263             {
2264                 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2265                 nIndex = GetSbData()->pInst->GetStdTimeIdx();
2266             }
2267             else
2268             {
2269                 sal_uInt32 n;   // Dummy
2270                 pFormatter = SbiInstance::PrepareNumberFormatter( n, nIndex, n );
2271             }
2272 
2273             pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
2274         }
2275         pMeth->PutString( aRes );
2276     }
2277     else
2278     {
2279         StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
2280     }
2281 }
2282 
2283 void SbRtl_Timer(StarBASIC *, SbxArray & rPar, bool)
2284 {
2285     tools::Time aTime( tools::Time::SYSTEM );
2286     long nSeconds = aTime.GetHour();
2287     nSeconds *= 3600;
2288     nSeconds += aTime.GetMin() * 60;
2289     nSeconds += aTime.GetSec();
2290     rPar.Get(0)->PutDate( static_cast<double>(nSeconds) );
2291 }
2292 
2293 
2294 void SbRtl_Date(StarBASIC *, SbxArray & rPar, bool bWrite)
2295 {
2296     if ( !bWrite )
2297     {
2298         Date aToday( Date::SYSTEM );
2299         double nDays = static_cast<double>(GetDayDiff( aToday ));
2300         SbxVariable* pMeth = rPar.Get( 0 );
2301         if( pMeth->IsString() )
2302         {
2303             OUString aRes;
2304             Color* pCol;
2305 
2306             std::shared_ptr<SvNumberFormatter> pFormatter;
2307             sal_uInt32 nIndex;
2308             if( GetSbData()->pInst )
2309             {
2310                 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2311                 nIndex = GetSbData()->pInst->GetStdDateIdx();
2312             }
2313             else
2314             {
2315                 sal_uInt32 n;
2316                 pFormatter = SbiInstance::PrepareNumberFormatter( nIndex, n, n );
2317             }
2318 
2319             pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
2320             pMeth->PutString( aRes );
2321         }
2322         else
2323         {
2324             pMeth->PutDate( nDays );
2325         }
2326     }
2327     else
2328     {
2329         StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
2330     }
2331 }
2332 
2333 void SbRtl_IsArray(StarBASIC *, SbxArray & rPar, bool)
2334 {
2335     if ( rPar.Count() < 2 )
2336     {
2337         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2338     }
2339     else
2340     {
2341         rPar.Get(0)->PutBool((rPar.Get(1)->GetType() & SbxARRAY) != 0);
2342     }
2343 }
2344 
2345 void SbRtl_IsObject(StarBASIC *, SbxArray & rPar, bool)
2346 {
2347     if ( rPar.Count() < 2 )
2348     {
2349         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2350     }
2351     else
2352     {
2353         SbxVariable* pVar = rPar.Get(1);
2354         bool bObject = pVar->IsObject();
2355         SbxBase* pObj = (bObject ? pVar->GetObject() : nullptr);
2356 
2357         if( auto pUnoClass = dynamic_cast<SbUnoClass*>( pObj) )
2358         {
2359             bObject = pUnoClass->getUnoClass().is();
2360         }
2361         rPar.Get( 0 )->PutBool( bObject );
2362     }
2363 }
2364 
2365 void SbRtl_IsDate(StarBASIC *, SbxArray & rPar, bool)
2366 {
2367     if ( rPar.Count() < 2 )
2368     {
2369         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2370     }
2371     else
2372     {
2373         // #46134 only string is converted, all other types result in sal_False
2374         SbxVariableRef xArg = rPar.Get( 1 );
2375         SbxDataType eType = xArg->GetType();
2376         bool bDate = false;
2377 
2378         if( eType == SbxDATE )
2379         {
2380             bDate = true;
2381         }
2382         else if( eType == SbxSTRING )
2383         {
2384             ErrCode nPrevError = SbxBase::GetError();
2385             SbxBase::ResetError();
2386 
2387             // force conversion of the parameter to SbxDATE
2388             xArg->SbxValue::GetDate();
2389 
2390             bDate = !SbxBase::IsError();
2391 
2392             SbxBase::ResetError();
2393             SbxBase::SetError( nPrevError );
2394         }
2395         rPar.Get( 0 )->PutBool( bDate );
2396     }
2397 }
2398 
2399 void SbRtl_IsEmpty(StarBASIC *, SbxArray & rPar, bool)
2400 {
2401     if ( rPar.Count() < 2 )
2402     {
2403         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2404     }
2405     else
2406     {
2407         SbxVariable* pVar = nullptr;
2408         if( SbiRuntime::isVBAEnabled() )
2409         {
2410             pVar = getDefaultProp( rPar.Get(1) );
2411         }
2412         if ( pVar )
2413         {
2414             pVar->Broadcast( SfxHintId::BasicDataWanted );
2415             rPar.Get( 0 )->PutBool( pVar->IsEmpty() );
2416         }
2417         else
2418         {
2419             rPar.Get( 0 )->PutBool( rPar.Get(1)->IsEmpty() );
2420         }
2421     }
2422 }
2423 
2424 void SbRtl_IsError(StarBASIC *, SbxArray & rPar, bool)
2425 {
2426     if ( rPar.Count() < 2 )
2427     {
2428         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2429     }
2430     else
2431     {
2432         SbxVariable* pVar =rPar.Get( 1 );
2433         SbUnoObject* pObj = dynamic_cast<SbUnoObject*>( pVar  );
2434         if ( !pObj )
2435         {
2436             if ( SbxBase* pBaseObj = (pVar->IsObject() ? pVar->GetObject() : nullptr) )
2437             {
2438                 pObj = dynamic_cast<SbUnoObject*>( pBaseObj  );
2439             }
2440         }
2441         uno::Reference< script::XErrorQuery > xError;
2442         if ( pObj )
2443         {
2444             xError.set( pObj->getUnoAny(), uno::UNO_QUERY );
2445         }
2446         if ( xError.is() )
2447         {
2448             rPar.Get( 0 )->PutBool( xError->hasError() );
2449         }
2450         else
2451         {
2452             rPar.Get( 0 )->PutBool( rPar.Get(1)->IsErr() );
2453         }
2454     }
2455 }
2456 
2457 void SbRtl_IsNull(StarBASIC *, SbxArray & rPar, bool)
2458 {
2459     if ( rPar.Count() < 2 )
2460     {
2461         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2462     }
2463     else
2464     {
2465         // #51475 because of Uno-objects return true
2466         // even if the pObj value is NULL
2467         SbxVariableRef pArg = rPar.Get( 1 );
2468         bool bNull = rPar.Get(1)->IsNull();
2469         if( !bNull && pArg->GetType() == SbxOBJECT )
2470         {
2471             SbxBase* pObj = pArg->GetObject();
2472             if( !pObj )
2473             {
2474                 bNull = true;
2475             }
2476         }
2477         rPar.Get( 0 )->PutBool( bNull );
2478     }
2479 }
2480 
2481 void SbRtl_IsNumeric(StarBASIC *, SbxArray & rPar, bool)
2482 {
2483     if ( rPar.Count() < 2 )
2484     {
2485         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2486     }
2487     else
2488     {
2489         rPar.Get( 0 )->PutBool( rPar.Get( 1 )->IsNumericRTL() );
2490     }
2491 }
2492 
2493 
2494 void SbRtl_IsMissing(StarBASIC *, SbxArray & rPar, bool)
2495 {
2496     if ( rPar.Count() < 2 )
2497     {
2498         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2499     }
2500     else
2501     {
2502         // #57915 Missing is reported by an error
2503         rPar.Get( 0 )->PutBool( rPar.Get(1)->IsErr() );
2504     }
2505 }
2506 
2507 // Function looks for wildcards, removes them and always returns the pure path
2508 static OUString implSetupWildcard(const OUString& rFileParam, SbiRTLData& rRTLData)
2509 {
2510     static const sal_Char cDelim1 = '/';
2511     static const sal_Char cDelim2 = '\\';
2512     static const sal_Char cWild1 = '*';
2513     static const sal_Char cWild2 = '?';
2514 
2515     rRTLData.pWildCard.reset();
2516     rRTLData.sFullNameToBeChecked.clear();
2517 
2518     OUString aFileParam = rFileParam;
2519     sal_Int32 nLastWild = aFileParam.lastIndexOf( cWild1 );
2520     if( nLastWild < 0 )
2521     {
2522         nLastWild = aFileParam.lastIndexOf( cWild2 );
2523     }
2524     bool bHasWildcards = ( nLastWild >= 0 );
2525 
2526 
2527     sal_Int32 nLastDelim = aFileParam.lastIndexOf( cDelim1 );
2528     if( nLastDelim < 0 )
2529     {
2530         nLastDelim = aFileParam.lastIndexOf( cDelim2 );
2531     }
2532     if( bHasWildcards )
2533     {
2534         // Wildcards in path?
2535         if( nLastDelim >= 0 && nLastDelim > nLastWild )
2536         {
2537             return aFileParam;
2538         }
2539     }
2540     else
2541     {
2542         OUString aPathStr = getFullPath( aFileParam );
2543         if( nLastDelim != aFileParam.getLength() - 1 )
2544         {
2545             rRTLData.sFullNameToBeChecked = aPathStr;
2546         }
2547         return aPathStr;
2548     }
2549 
2550     OUString aPureFileName;
2551     if( nLastDelim < 0 )
2552     {
2553         aPureFileName = aFileParam;
2554         aFileParam.clear();
2555     }
2556     else
2557     {
2558         aPureFileName = aFileParam.copy( nLastDelim + 1 );
2559         aFileParam = aFileParam.copy( 0, nLastDelim );
2560     }
2561 
2562     // Try again to get a valid URL/UNC-path with only the path
2563     OUString aPathStr = getFullPath( aFileParam );
2564 
2565     // Is there a pure file name left? Otherwise the path is
2566     // invalid anyway because it was not accepted by OSL before
2567     if (aPureFileName != "*")
2568     {
2569         rRTLData.pWildCard = std::make_unique<WildCard>(aPureFileName);
2570     }
2571     return aPathStr;
2572 }
2573 
2574 static bool implCheckWildcard(const OUString& rName, SbiRTLData const& rRTLData)
2575 {
2576     bool bMatch = true;
2577 
2578     if (rRTLData.pWildCard)
2579     {
2580         bMatch = rRTLData.pWildCard->Matches(rName);
2581     }
2582     return bMatch;
2583 }
2584 
2585 
2586 static bool isRootDir( const OUString& aDirURLStr )
2587 {
2588     INetURLObject aDirURLObj( aDirURLStr );
2589     bool bRoot = false;
2590 
2591     // Check if it's a root directory
2592     sal_Int32 nCount = aDirURLObj.getSegmentCount();
2593 
2594     // No segment means Unix root directory "file:///"
2595     if( nCount == 0 )
2596     {
2597         bRoot = true;
2598     }
2599     // Exactly one segment needs further checking, because it
2600     // can be Unix "file:///foo/" -> no root
2601     // or Windows  "file:///c:/"  -> root
2602     else if( nCount == 1 )
2603     {
2604         OUString aSeg1 = aDirURLObj.getName( 0, true,
2605                                              INetURLObject::DecodeMechanism::WithCharset );
2606         if( aSeg1[1] == ':' )
2607         {
2608             bRoot = true;
2609         }
2610     }
2611     // More than one segments can never be root
2612     // so bRoot remains false
2613 
2614     return bRoot;
2615 }
2616 
2617 void SbRtl_Dir(StarBASIC *, SbxArray & rPar, bool)
2618 {
2619     OUString aPath;
2620 
2621     sal_uInt16 nParCount = rPar.Count();
2622     if( nParCount > 3 )
2623     {
2624         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2625     }
2626     else
2627     {
2628         SbiRTLData& rRTLData = GetSbData()->pInst->GetRTLData();
2629 
2630         if( hasUno() )
2631         {
2632             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2633             if( xSFI.is() )
2634             {
2635                 if ( nParCount >= 2 )
2636                 {
2637                     OUString aFileParam = rPar.Get(1)->GetOUString();
2638 
2639                     OUString aFileURLStr = implSetupWildcard(aFileParam, rRTLData);
2640                     if (!rRTLData.sFullNameToBeChecked.isEmpty())
2641                     {
2642                         bool bExists = false;
2643                         try { bExists = xSFI->exists( aFileURLStr ); }
2644                         catch(const Exception & ) {}
2645 
2646                         OUString aNameOnlyStr;
2647                         if( bExists )
2648                         {
2649                             INetURLObject aFileURL( aFileURLStr );
2650                             aNameOnlyStr = aFileURL.getName( INetURLObject::LAST_SEGMENT,
2651                                                              true, INetURLObject::DecodeMechanism::WithCharset );
2652                         }
2653                         rPar.Get(0)->PutString( aNameOnlyStr );
2654                         return;
2655                     }
2656 
2657                     try
2658                     {
2659                         OUString aDirURLStr;
2660                         bool bFolder = xSFI->isFolder( aFileURLStr );
2661 
2662                         if( bFolder )
2663                         {
2664                             aDirURLStr = aFileURLStr;
2665                         }
2666                         else
2667                         {
2668                             rPar.Get(0)->PutString( "" );
2669                         }
2670 
2671                         SbAttributes nFlags = SbAttributes::NONE;
2672                         if ( nParCount > 2 )
2673                         {
2674                             rRTLData.nDirFlags = nFlags
2675                                 = static_cast<SbAttributes>(rPar.Get(2)->GetInteger());
2676                         }
2677                         else
2678                         {
2679                             rRTLData.nDirFlags = SbAttributes::NONE;
2680                         }
2681                         // Read directory
2682                         bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
2683                         rRTLData.aDirSeq = xSFI->getFolderContents(aDirURLStr, bIncludeFolders);
2684                         rRTLData.nCurDirPos = 0;
2685 
2686                         // #78651 Add "." and ".." directories for VB compatibility
2687                         if( bIncludeFolders )
2688                         {
2689                             bool bRoot = isRootDir( aDirURLStr );
2690 
2691                             // If it's no root directory we flag the need for
2692                             // the "." and ".." directories by the value -2
2693                             // for the actual position. Later for -2 will be
2694                             // returned "." and for -1 ".."
2695                             if( !bRoot )
2696                             {
2697                                 rRTLData.nCurDirPos = -2;
2698                             }
2699                         }
2700                     }
2701                     catch(const Exception & )
2702                     {
2703                     }
2704                 }
2705 
2706 
2707                 if (rRTLData.aDirSeq.hasElements())
2708                 {
2709                     bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY);
2710 
2711                     SbiInstance* pInst = GetSbData()->pInst;
2712                     bool bCompatibility = ( pInst && pInst->IsCompatibility() );
2713                     for( ;; )
2714                     {
2715                         if (rRTLData.nCurDirPos < 0)
2716                         {
2717                             if (rRTLData.nCurDirPos == -2)
2718                             {
2719                                 aPath = ".";
2720                             }
2721                             else if (rRTLData.nCurDirPos == -1)
2722                             {
2723                                 aPath = "..";
2724                             }
2725                             rRTLData.nCurDirPos++;
2726                         }
2727                         else if (rRTLData.nCurDirPos >= rRTLData.aDirSeq.getLength())
2728                         {
2729                             rRTLData.aDirSeq.realloc(0);
2730                             aPath.clear();
2731                             break;
2732                         }
2733                         else
2734                         {
2735                             OUString aFile
2736                                 = rRTLData.aDirSeq.getConstArray()[rRTLData.nCurDirPos++];
2737 
2738                             if( bCompatibility )
2739                             {
2740                                 if( !bFolderFlag )
2741                                 {
2742                                     bool bFolder = xSFI->isFolder( aFile );
2743                                     if( bFolder )
2744                                     {
2745                                         continue;
2746                                     }
2747                                 }
2748                             }
2749                             else
2750                             {
2751                                 // Only directories
2752                                 if( bFolderFlag )
2753                                 {
2754                                     bool bFolder = xSFI->isFolder( aFile );
2755                                     if( !bFolder )
2756                                     {
2757                                         continue;
2758                                     }
2759                                 }
2760                             }
2761 
2762                             INetURLObject aURL( aFile );
2763                             aPath = aURL.getName( INetURLObject::LAST_SEGMENT, true,
2764                                                   INetURLObject::DecodeMechanism::WithCharset );
2765                         }
2766 
2767                         bool bMatch = implCheckWildcard(aPath, rRTLData);
2768                         if( !bMatch )
2769                         {
2770                             continue;
2771                         }
2772                         break;
2773                     }
2774                 }
2775                 rPar.Get(0)->PutString( aPath );
2776             }
2777         }
2778         else
2779         {
2780             // TODO: OSL
2781             if ( nParCount >= 2 )
2782             {
2783                 OUString aFileParam = rPar.Get(1)->GetOUString();
2784 
2785                 OUString aDirURL = implSetupWildcard(aFileParam, rRTLData);
2786 
2787                 SbAttributes nFlags = SbAttributes::NONE;
2788                 if ( nParCount > 2 )
2789                 {
2790                     rRTLData.nDirFlags = nFlags
2791                         = static_cast<SbAttributes>(rPar.Get(2)->GetInteger());
2792                 }
2793                 else
2794                 {
2795                     rRTLData.nDirFlags = SbAttributes::NONE;
2796                 }
2797 
2798                 // Read directory
2799                 bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
2800                 rRTLData.pDir = std::make_unique<Directory>(aDirURL);
2801                 FileBase::RC nRet = rRTLData.pDir->open();
2802                 if( nRet != FileBase::E_None )
2803                 {
2804                     rRTLData.pDir.reset();
2805                     rPar.Get(0)->PutString( OUString() );
2806                     return;
2807                 }
2808 
2809                 // #86950 Add "." and ".." directories for VB compatibility
2810                 rRTLData.nCurDirPos = 0;
2811                 if( bIncludeFolders )
2812                 {
2813                     bool bRoot = isRootDir( aDirURL );
2814 
2815                     // If it's no root directory we flag the need for
2816                     // the "." and ".." directories by the value -2
2817                     // for the actual position. Later for -2 will be
2818                     // returned "." and for -1 ".."
2819                     if( !bRoot )
2820                     {
2821                         rRTLData.nCurDirPos = -2;
2822                     }
2823                 }
2824 
2825             }
2826 
2827             if (rRTLData.pDir)
2828             {
2829                 bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY);
2830                 for( ;; )
2831                 {
2832                     if (rRTLData.nCurDirPos < 0)
2833                     {
2834                         if (rRTLData.nCurDirPos == -2)
2835                         {
2836                             aPath = ".";
2837                         }
2838                         else if (rRTLData.nCurDirPos == -1)
2839                         {
2840                             aPath = "..";
2841                         }
2842                         rRTLData.nCurDirPos++;
2843                     }
2844                     else
2845                     {
2846                         DirectoryItem aItem;
2847                         FileBase::RC nRet = rRTLData.pDir->getNextItem(aItem);
2848                         if( nRet != FileBase::E_None )
2849                         {
2850                             rRTLData.pDir.reset();
2851                             aPath.clear();
2852                             break;
2853                         }
2854 
2855                         // Handle flags
2856                         FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName );
2857                         nRet = aItem.getFileStatus( aFileStatus );
2858                         if( nRet != FileBase::E_None )
2859                         {
2860                             SAL_WARN("basic", "getFileStatus failed");
2861                             continue;
2862                         }
2863 
2864                         // Only directories?
2865                         if( bFolderFlag )
2866                         {
2867                             FileStatus::Type aType = aFileStatus.getFileType();
2868                             bool bFolder = isFolder( aType );
2869                             if( !bFolder )
2870                             {
2871                                 continue;
2872                             }
2873                         }
2874 
2875                         aPath = aFileStatus.getFileName();
2876                     }
2877 
2878                     bool bMatch = implCheckWildcard(aPath, rRTLData);
2879                     if( !bMatch )
2880                     {
2881                         continue;
2882                     }
2883                     break;
2884                 }
2885             }
2886             rPar.Get(0)->PutString( aPath );
2887         }
2888     }
2889 }
2890 
2891 
2892 void SbRtl_GetAttr(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
2893 {
2894     (void)pBasic;
2895     (void)bWrite;
2896 
2897     if ( rPar.Count() == 2 )
2898     {
2899         sal_Int16 nFlags = 0;
2900 
2901         // In Windows, we want to use Windows API to get the file attributes
2902         // for VBA interoperability.
2903     #if defined(_WIN32)
2904         if( SbiRuntime::isVBAEnabled() )
2905         {
2906             OUString aPathURL = getFullPath( rPar.Get(1)->GetOUString() );
2907             OUString aPath;
2908             FileBase::getSystemPathFromFileURL( aPathURL, aPath );
2909             DWORD nRealFlags = GetFileAttributesW (o3tl::toW(aPath.getStr()));
2910             if (nRealFlags != 0xffffffff)
2911             {
2912                 if (nRealFlags == FILE_ATTRIBUTE_NORMAL)
2913                 {
2914                     nRealFlags = 0;
2915                 }
2916                 nFlags = static_cast<sal_Int16>(nRealFlags);
2917             }
2918             else
2919             {
2920                 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
2921             }
2922             rPar.Get(0)->PutInteger( nFlags );
2923 
2924             return;
2925         }
2926     #endif
2927 
2928         if( hasUno() )
2929         {
2930             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2931             if( xSFI.is() )
2932             {
2933                 try
2934                 {
2935                     OUString aPath = getFullPath( rPar.Get(1)->GetOUString() );
2936                     bool bExists = false;
2937                     try { bExists = xSFI->exists( aPath ); }
2938                     catch(const Exception & ) {}
2939                     if( !bExists )
2940                     {
2941                         StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
2942                         return;
2943                     }
2944 
2945                     bool bReadOnly = xSFI->isReadOnly( aPath );
2946                     bool bHidden = xSFI->isHidden( aPath );
2947                     bool bDirectory = xSFI->isFolder( aPath );
2948                     if( bReadOnly )
2949                     {
2950                         nFlags |= sal_uInt16(SbAttributes::READONLY);
2951                     }
2952                     if( bHidden )
2953                     {
2954                         nFlags |= sal_uInt16(SbAttributes::HIDDEN);
2955                     }
2956                     if( bDirectory )
2957                     {
2958                         nFlags |= sal_uInt16(SbAttributes::DIRECTORY);
2959                     }
2960                 }
2961                 catch(const Exception & )
2962                 {
2963                     StarBASIC::Error( ERRCODE_IO_GENERAL );
2964                 }
2965             }
2966         }
2967         else
2968         {
2969             DirectoryItem aItem;
2970             (void)DirectoryItem::get( getFullPath( rPar.Get(1)->GetOUString() ), aItem );
2971             FileStatus aFileStatus( osl_FileStatus_Mask_Attributes | osl_FileStatus_Mask_Type );
2972             (void)aItem.getFileStatus( aFileStatus );
2973             sal_uInt64 nAttributes = aFileStatus.getAttributes();
2974             bool bReadOnly = (nAttributes & osl_File_Attribute_ReadOnly) != 0;
2975 
2976             FileStatus::Type aType = aFileStatus.getFileType();
2977             bool bDirectory = isFolder( aType );
2978             if( bReadOnly )
2979             {
2980                 nFlags |= sal_uInt16(SbAttributes::READONLY);
2981             }
2982             if( bDirectory )
2983             {
2984                 nFlags |= sal_uInt16(SbAttributes::DIRECTORY);
2985             }
2986         }
2987         rPar.Get(0)->PutInteger( nFlags );
2988     }
2989     else
2990     {
2991         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2992     }
2993 }
2994 
2995 
2996 void SbRtl_FileDateTime(StarBASIC *, SbxArray & rPar, bool)
2997 {
2998     if ( rPar.Count() != 2 )
2999     {
3000         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3001     }
3002     else
3003     {
3004         OUString aPath = rPar.Get(1)->GetOUString();
3005         tools::Time aTime( tools::Time::EMPTY );
3006         Date aDate( Date::EMPTY );
3007         if( hasUno() )
3008         {
3009             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
3010             if( xSFI.is() )
3011             {
3012                 try
3013                 {
3014                     util::DateTime aUnoDT = xSFI->getDateTimeModified( aPath );
3015                     aTime = tools::Time( aUnoDT );
3016                     aDate = Date( aUnoDT );
3017                 }
3018                 catch(const Exception & )
3019                 {
3020                     StarBASIC::Error( ERRCODE_IO_GENERAL );
3021                 }
3022             }
3023         }
3024         else
3025         {
3026             bool bSuccess = false;
3027             do
3028             {
3029                 DirectoryItem aItem;
3030                 if (DirectoryItem::get( getFullPath( aPath ), aItem ) != FileBase::E_None)
3031                     break;
3032 
3033                 FileStatus aFileStatus( osl_FileStatus_Mask_ModifyTime );
3034                 if (aItem.getFileStatus( aFileStatus ) != FileBase::E_None)
3035                     break;
3036 
3037                 TimeValue aTimeVal = aFileStatus.getModifyTime();
3038                 oslDateTime aDT;
3039                 if (!osl_getDateTimeFromTimeValue( &aTimeVal, &aDT ))
3040                     // Strictly spoken this is not an i/o error but some other failure.
3041                     break;
3042 
3043                 aTime = tools::Time( aDT.Hours, aDT.Minutes, aDT.Seconds, aDT.NanoSeconds );
3044                 aDate = Date( aDT.Day, aDT.Month, aDT.Year );
3045                 bSuccess = true;
3046             }
3047             while(false);
3048 
3049             if (!bSuccess)
3050                 StarBASIC::Error( ERRCODE_IO_GENERAL );
3051         }
3052 
3053         // An empty date shall not result in a formatted null-date (1899-12-30
3054         // or 1900-01-01) or even worse -0001-12-03 or some such due to how
3055         // GetDayDiff() treats things. There should be an error set in this
3056         // case anyway because of a missing file or other error above, but... so
3057         // do not even bother to use the number formatter.
3058         OUString aRes;
3059         if (aDate.IsEmpty())
3060         {
3061             aRes = "0000-00-00 00:00:00";
3062         }
3063         else
3064         {
3065             double fSerial = static_cast<double>(GetDayDiff( aDate ));
3066             long nSeconds = aTime.GetHour();
3067             nSeconds *= 3600;
3068             nSeconds += aTime.GetMin() * 60;
3069             nSeconds += aTime.GetSec();
3070             double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
3071             fSerial += nDays;
3072 
3073             Color* pCol;
3074 
3075             std::shared_ptr<SvNumberFormatter> pFormatter;
3076             sal_uInt32 nIndex;
3077             if( GetSbData()->pInst )
3078             {
3079                 pFormatter = GetSbData()->pInst->GetNumberFormatter();
3080                 nIndex = GetSbData()->pInst->GetStdDateTimeIdx();
3081             }
3082             else
3083             {
3084                 sal_uInt32 n;
3085                 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, nIndex );
3086             }
3087 
3088             pFormatter->GetOutputString( fSerial, nIndex, aRes, &pCol );
3089         }
3090         rPar.Get(0)->PutString( aRes );
3091     }
3092 }
3093 
3094 
3095 void SbRtl_EOF(StarBASIC *, SbxArray & rPar, bool)
3096 {
3097     // No changes for UCB
3098     if ( rPar.Count() != 2 )
3099     {
3100         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3101     }
3102     else
3103     {
3104         sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3105         SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3106         SbiStream* pSbStrm = pIO->GetStream( nChannel );
3107         if ( !pSbStrm )
3108         {
3109             StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3110             return;
3111         }
3112         bool beof;
3113         SvStream* pSvStrm = pSbStrm->GetStrm();
3114         if ( pSbStrm->IsText() )
3115         {
3116             char cBla;
3117             (*pSvStrm).ReadChar( cBla ); // can we read another character?
3118             beof = pSvStrm->eof();
3119             if ( !beof )
3120             {
3121                 pSvStrm->SeekRel( -1 );
3122             }
3123         }
3124         else
3125         {
3126             beof = pSvStrm->eof();  // for binary data!
3127         }
3128         rPar.Get(0)->PutBool( beof );
3129     }
3130 }
3131 
3132 void SbRtl_FileAttr(StarBASIC *, SbxArray & rPar, bool)
3133 {
3134     // No changes for UCB
3135     // #57064 Although this function doesn't operate with DirEntry, it is
3136     // not touched by the adjustment to virtual URLs, as it only works on
3137     // already opened files and the name doesn't matter there.
3138 
3139     if ( rPar.Count() != 3 )
3140     {
3141         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3142     }
3143     else
3144     {
3145         sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3146         SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3147         SbiStream* pSbStrm = pIO->GetStream( nChannel );
3148         if ( !pSbStrm )
3149         {
3150             StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3151             return;
3152         }
3153         sal_Int16 nRet;
3154         if ( rPar.Get(2)->GetInteger() == 1 )
3155         {
3156             nRet = static_cast<sal_Int16>(pSbStrm->GetMode());
3157         }
3158         else
3159         {
3160             nRet = 0; // System file handle not supported
3161         }
3162         rPar.Get(0)->PutInteger( nRet );
3163     }
3164 }
3165 void SbRtl_Loc(StarBASIC *, SbxArray & rPar, bool)
3166 {
3167     // No changes for UCB
3168     if ( rPar.Count() != 2 )
3169     {
3170         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3171     }
3172     else
3173     {
3174         sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3175         SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3176         SbiStream* pSbStrm = pIO->GetStream( nChannel );
3177         if ( !pSbStrm )
3178         {
3179             StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3180             return;
3181         }
3182         SvStream* pSvStrm = pSbStrm->GetStrm();
3183         std::size_t nPos;
3184         if( pSbStrm->IsRandom())
3185         {
3186             short nBlockLen = pSbStrm->GetBlockLen();
3187             nPos = nBlockLen ? (pSvStrm->Tell() / nBlockLen) : 0;
3188             nPos++; // block positions starting at 1
3189         }
3190         else if ( pSbStrm->IsText() )
3191         {
3192             nPos = pSbStrm->GetLine();
3193         }
3194         else if( pSbStrm->IsBinary() )
3195         {
3196             nPos = pSvStrm->Tell();
3197         }
3198         else if ( pSbStrm->IsSeq() )
3199         {
3200             nPos = ( pSvStrm->Tell()+1 ) / 128;
3201         }
3202         else
3203         {
3204             nPos = pSvStrm->Tell();
3205         }
3206         rPar.Get(0)->PutLong( static_cast<sal_Int32>(nPos) );
3207     }
3208 }
3209 
3210 void SbRtl_Lof(StarBASIC *, SbxArray & rPar, bool)
3211 {
3212     // No changes for UCB
3213     if ( rPar.Count() != 2 )
3214     {
3215         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3216     }
3217     else
3218     {
3219         sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3220         SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3221         SbiStream* pSbStrm = pIO->GetStream( nChannel );
3222         if ( !pSbStrm )
3223         {
3224             StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3225             return;
3226         }
3227         SvStream* pSvStrm = pSbStrm->GetStrm();
3228         sal_uInt64 const nLen = pSvStrm->TellEnd();
3229         rPar.Get(0)->PutLong( static_cast<sal_Int32>(nLen) );
3230     }
3231 }
3232 
3233 
3234 void SbRtl_Seek(StarBASIC *, SbxArray & rPar, bool)
3235 {
3236     // No changes for UCB
3237     int nArgs = static_cast<int>(rPar.Count());
3238     if ( nArgs < 2 || nArgs > 3 )
3239     {
3240         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3241         return;
3242     }
3243     sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3244     SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3245     SbiStream* pSbStrm = pIO->GetStream( nChannel );
3246     if ( !pSbStrm )
3247     {
3248         StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3249         return;
3250     }
3251     SvStream* pStrm = pSbStrm->GetStrm();
3252 
3253     if ( nArgs == 2 )   // Seek-Function
3254     {
3255         sal_uInt64 nPos = pStrm->Tell();
3256         if( pSbStrm->IsRandom() )
3257         {
3258             nPos = nPos / pSbStrm->GetBlockLen();
3259         }
3260         nPos++; // Basic counts from 1
3261         rPar.Get(0)->PutLong( static_cast<sal_Int32>(nPos) );
3262     }
3263     else                // Seek-Statement
3264     {
3265         sal_Int32 nPos = rPar.Get(2)->GetLong();
3266         if ( nPos < 1 )
3267         {
3268             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3269             return;
3270         }
3271         nPos--; // Basic counts from 1, SvStreams count from 0
3272         pSbStrm->SetExpandOnWriteTo( 0 );
3273         if ( pSbStrm->IsRandom() )
3274         {
3275             nPos *= pSbStrm->GetBlockLen();
3276         }
3277         pStrm->Seek( static_cast<sal_uInt64>(nPos) );
3278         pSbStrm->SetExpandOnWriteTo( nPos );
3279     }
3280 }
3281 
3282 void SbRtl_Format(StarBASIC *, SbxArray & rPar, bool)
3283 {
3284     sal_uInt16 nArgCount = rPar.Count();
3285     if ( nArgCount < 2 || nArgCount > 3 )
3286     {
3287         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3288     }
3289     else
3290     {
3291         OUString aResult;
3292         if( nArgCount == 2 )
3293         {
3294             rPar.Get(1)->Format( aResult );
3295         }
3296         else
3297         {
3298             OUString aFmt( rPar.Get(2)->GetOUString() );
3299             rPar.Get(1)->Format( aResult, &aFmt );
3300         }
3301         rPar.Get(0)->PutString( aResult );
3302     }
3303 }
3304 
3305 // https://msdn.microsoft.com/en-us/vba/language-reference-vba/articles/formatnumber-function
3306 void SbRtl_FormatNumber(StarBASIC*, SbxArray& rPar, bool)
3307 {
3308     const sal_uInt16 nArgCount = rPar.Count();
3309     if (nArgCount < 2 || nArgCount > 6)
3310     {
3311         StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3312         return;
3313     }
3314 
3315     // The UI locale never changes -> we can use static value here
3316     static const LocaleDataWrapper localeData(Application::GetSettings().GetUILanguageTag());
3317     sal_Int16 nNumDigitsAfterDecimal = -1;
3318     if (nArgCount > 2 && !rPar.Get(2)->IsEmpty())
3319     {
3320         nNumDigitsAfterDecimal = rPar.Get(2)->GetInteger();
3321         if (nNumDigitsAfterDecimal < -1)
3322         {
3323             StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3324             return;
3325         }
3326         else if (nNumDigitsAfterDecimal > 255)
3327             nNumDigitsAfterDecimal %= 256;
3328     }
3329     if (nNumDigitsAfterDecimal == -1)
3330         nNumDigitsAfterDecimal = LocaleDataWrapper::getNumDigits();
3331 
3332     bool bIncludeLeadingDigit = LocaleDataWrapper::isNumLeadingZero();
3333     if (nArgCount > 3 && !rPar.Get(3)->IsEmpty())
3334     {
3335         switch (rPar.Get(3)->GetInteger())
3336         {
3337             case ooo::vba::VbTriState::vbFalse:
3338                 bIncludeLeadingDigit = false;
3339                 break;
3340             case ooo::vba::VbTriState::vbTrue:
3341                 bIncludeLeadingDigit = true;
3342                 break;
3343             case ooo::vba::VbTriState::vbUseDefault:
3344                 // do nothing;
3345                 break;
3346             default:
3347                 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3348                 return;
3349         }
3350     }
3351 
3352     bool bUseParensForNegativeNumbers = false;
3353     if (nArgCount > 4 && !rPar.Get(4)->IsEmpty())
3354     {
3355         switch (rPar.Get(4)->GetInteger())
3356         {
3357             case ooo::vba::VbTriState::vbFalse:
3358             case ooo::vba::VbTriState::vbUseDefault:
3359                 // do nothing
3360                 break;
3361             case ooo::vba::VbTriState::vbTrue:
3362                 bUseParensForNegativeNumbers = true;
3363                 break;
3364             default:
3365                 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3366                 return;
3367         }
3368     }
3369 
3370     bool bGroupDigits = false;
3371     if (nArgCount > 5 && !rPar.Get(5)->IsEmpty())
3372     {
3373         switch (rPar.Get(5)->GetInteger())
3374         {
3375             case ooo::vba::VbTriState::vbFalse:
3376             case ooo::vba::VbTriState::vbUseDefault:
3377                 // do nothing
3378                 break;
3379             case ooo::vba::VbTriState::vbTrue:
3380                 bGroupDigits = true;
3381                 break;
3382             default:
3383                 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3384                 return;
3385         }
3386     }
3387 
3388     double fVal = rPar.Get(1)->GetDouble();
3389     const bool bNegative = fVal < 0;
3390     if (bNegative)
3391         fVal = fabs(fVal); // Always work with non-negatives, to easily handle leading zero
3392 
3393     static const sal_Unicode decSep = localeData.getNumDecimalSep().toChar();
3394     OUString aResult = rtl::math::doubleToUString(
3395         fVal, rtl_math_StringFormat_F, nNumDigitsAfterDecimal, decSep,
3396         bGroupDigits ? localeData.getDigitGrouping().getConstArray() : nullptr,
3397         localeData.getNumThousandSep().toChar());
3398 
3399     if (!bIncludeLeadingDigit && aResult.getLength() > 1 && aResult.startsWith("0"))
3400         aResult = aResult.copy(1);
3401 
3402     if (nNumDigitsAfterDecimal > 0)
3403     {
3404         sal_Int32 nActualDigits = nNumDigitsAfterDecimal;
3405         const sal_Int32 nSepPos = aResult.indexOf(decSep);
3406         if (nSepPos == -1)
3407             nActualDigits = 0;
3408         else
3409             nActualDigits = aResult.getLength() - nSepPos - 1;
3410 
3411         // VBA allows up to 255 digits; rtl::math::doubleToUString outputs up to 15 digits
3412         // for ~small numbers, so pad them as appropriate.
3413         if (nActualDigits < nNumDigitsAfterDecimal)
3414         {
3415             OUStringBuffer sBuf;
3416             comphelper::string::padToLength(sBuf, nNumDigitsAfterDecimal - nActualDigits, '0');
3417             aResult += sBuf;
3418         }
3419     }
3420 
3421     if (bNegative)
3422     {
3423         if (bUseParensForNegativeNumbers)
3424             aResult = "(" + aResult + ")";
3425         else
3426             aResult = "-" + aResult;
3427     }
3428 
3429     rPar.Get(0)->PutString(aResult);
3430 }
3431 
3432 namespace {
3433 
3434 // note: BASIC does not use comphelper::random, because
3435 // Randomize(int) must be supported and should not affect non-BASIC random use
3436 struct RandomNumberGenerator
3437 {
3438     std::mt19937 global_rng;
3439 
3440     RandomNumberGenerator()
3441     {
3442         try
3443         {
3444             std::random_device rd;
3445             // initialises the state of the global random number generator
3446             // should only be called once.
3447             // (note, a few std::variate_generator<> (like normal) have their
3448             // own state which would need a reset as well to guarantee identical
3449             // sequence of numbers, e.g. via myrand.distribution().reset())
3450             global_rng.seed(rd() ^ time(nullptr));
3451         }
3452         catch (std::runtime_error& e)
3453         {
3454             SAL_WARN("basic", "Using std::random_device failed: " << e.what());
3455             global_rng.seed(time(nullptr));
3456         }
3457     }
3458 };
3459 
3460 class theRandomNumberGenerator : public rtl::Static<RandomNumberGenerator, theRandomNumberGenerator> {};
3461 
3462 }
3463 
3464 void SbRtl_Randomize(StarBASIC *, SbxArray & rPar, bool)
3465 {
3466     if ( rPar.Count() > 2 )
3467     {
3468         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3469     }
3470     if( rPar.Count() == 2 )
3471     {
3472         int nSeed = static_cast<int>(rPar.Get(1)->GetInteger());
3473         theRandomNumberGenerator::get().global_rng.seed(nSeed);
3474     }
3475     // without parameter, no need to do anything - RNG is seeded at first use
3476 }
3477 
3478 void SbRtl_Rnd(StarBASIC *, SbxArray & rPar, bool)
3479 {
3480     if ( rPar.Count() > 2 )
3481     {
3482         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3483     }
3484     else
3485     {
3486         std::uniform_real_distribution<double> dist(0.0, 1.0);
3487         double const tmp(dist(theRandomNumberGenerator::get().global_rng));
3488         rPar.Get(0)->PutDouble(tmp);
3489     }
3490 }
3491 
3492 
3493 //  Syntax: Shell("Path",[ Window-Style,[ "Params", [ bSync = sal_False ]]])
3494 //  WindowStyles (VBA compatible):
3495 //      2 == Minimized
3496 //      3 == Maximized
3497 //     10 == Full-Screen (text mode applications OS/2, WIN95, WNT)
3498 //     HACK: The WindowStyle will be passed to
3499 //     Application::StartApp in Creator. Format: "xxxx2"
3500 
3501 
3502 void SbRtl_Shell(StarBASIC *, SbxArray & rPar, bool)
3503 {
3504     std::size_t nArgCount = rPar.Count();
3505     if ( nArgCount < 2 || nArgCount > 5 )
3506     {
3507         rPar.Get(0)->PutLong(0);
3508         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3509     }
3510     else
3511     {
3512         oslProcessOption nOptions = osl_Process_SEARCHPATH | osl_Process_DETACHED;
3513 
3514         OUString aCmdLine = rPar.Get(1)->GetOUString();
3515         // attach additional parameters - everything must be parsed anyway
3516         if( nArgCount >= 4 )
3517         {
3518             OUString tmp = rPar.Get(3)->GetOUString().trim();
3519             if (!tmp.isEmpty())
3520             {
3521                 aCmdLine += " ";
3522                 aCmdLine += tmp;
3523             }
3524         }
3525         else if( aCmdLine.isEmpty() )
3526         {
3527             // avoid special treatment (empty list)
3528             aCmdLine += " ";
3529         }
3530         sal_Int32 nLen = aCmdLine.getLength();
3531 
3532         // #55735 if there are parameters, they have to be separated
3533         // #72471 also separate the single parameters
3534         std::vector<OUString> aTokenVector;
3535         OUString aToken;
3536         sal_Int32 i = 0;
3537         sal_Unicode c;
3538         while( i < nLen )
3539         {
3540             for ( ;; ++i )
3541             {
3542                 c = aCmdLine[ i ];
3543                 if ( c != ' ' && c != '\t' )
3544                 {
3545                     break;
3546                 }
3547             }
3548 
3549             if( c == '\"' || c == '\'' )
3550             {
3551                 sal_Int32 iFoundPos = aCmdLine.indexOf( c, i + 1 );
3552 
3553                 if( iFoundPos < 0 )
3554                 {
3555                     aToken = aCmdLine.copy( i);
3556                     i = nLen;
3557                 }
3558                 else
3559                 {
3560                     aToken = aCmdLine.copy( i + 1, (iFoundPos - i - 1) );
3561                     i = iFoundPos + 1;
3562                 }
3563             }
3564             else
3565             {
3566                 sal_Int32 iFoundSpacePos = aCmdLine.indexOf( ' ', i );
3567                 sal_Int32 iFoundTabPos = aCmdLine.indexOf( '\t', i );
3568                 sal_Int32 iFoundPos = iFoundSpacePos >= 0 ? iFoundTabPos >= 0 ? std::min( iFoundSpacePos, iFoundTabPos ) : iFoundSpacePos : -1;
3569 
3570                 if( iFoundPos < 0 )
3571                 {
3572                     aToken = aCmdLine.copy( i );
3573                     i = nLen;
3574                 }
3575                 else
3576                 {
3577                     aToken = aCmdLine.copy( i, (iFoundPos - i) );
3578                     i = iFoundPos;
3579                 }
3580             }
3581 
3582             // insert into the list
3583             aTokenVector.push_back( aToken );
3584         }
3585         // #55735 / #72471 end
3586 
3587         sal_Int16 nWinStyle = 0;
3588         if( nArgCount >= 3 )
3589         {
3590             nWinStyle = rPar.Get(2)->GetInteger();
3591             switch( nWinStyle )
3592             {
3593             case 2:
3594                 nOptions |= osl_Process_MINIMIZED;
3595                 break;
3596             case 3:
3597                 nOptions |= osl_Process_MAXIMIZED;
3598                 break;
3599             case 10:
3600                 nOptions |= osl_Process_FULLSCREEN;
3601                 break;
3602             }
3603 
3604             bool bSync = false;
3605             if( nArgCount >= 5 )
3606             {
3607                 bSync = rPar.Get(4)->GetBool();
3608             }
3609             if( bSync )
3610             {
3611                 nOptions |= osl_Process_WAIT;
3612             }
3613         }
3614 
3615         // #72471 work parameter(s) up
3616         std::vector<OUString>::const_iterator iter = aTokenVector.begin();
3617         OUString aOUStrProgURL = getFullPath( *iter );
3618 
3619         ++iter;
3620 
3621         sal_uInt16 nParamCount = sal::static_int_cast< sal_uInt16 >(aTokenVector.size() - 1 );
3622         std::unique_ptr<rtl_uString*[]> pParamList;
3623         if( nParamCount )
3624         {
3625             pParamList.reset( new rtl_uString*[nParamCount]);
3626             for(int iVector = 0; iter != aTokenVector.end(); ++iVector, ++iter)
3627             {
3628                 const OUString& rParamStr = *iter;
3629                 pParamList[iVector] = nullptr;
3630                 rtl_uString_assign(&(pParamList[iVector]), rParamStr.pData);
3631             }
3632         }
3633 
3634         oslProcess pApp;
3635         bool bSucc = osl_executeProcess(
3636                     aOUStrProgURL.pData,
3637                     pParamList.get(),
3638                     nParamCount,
3639                     nOptions,
3640                     nullptr,
3641                     nullptr,
3642                     nullptr, 0,
3643                     &pApp ) == osl_Process_E_None;
3644 
3645         // 53521 only free process handle on success
3646         if (bSucc)
3647         {
3648             osl_freeProcessHandle( pApp );
3649         }
3650 
3651         for(int j = 0; j < nParamCount; ++j)
3652         {
3653             rtl_uString_release(pParamList[j]);
3654         }
3655 
3656         if( !bSucc )
3657         {
3658             StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
3659         }
3660         else
3661         {
3662             rPar.Get(0)->PutLong( 0 );
3663         }
3664     }
3665 }
3666 
3667 void SbRtl_VarType(StarBASIC *, SbxArray & rPar, bool)
3668 {
3669     if ( rPar.Count() != 2 )
3670     {
3671         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3672     }
3673     else
3674     {
3675         SbxDataType eType = rPar.Get(1)->GetType();
3676         rPar.Get(0)->PutInteger( static_cast<sal_Int16>(eType) );
3677     }
3678 }
3679 
3680 // Exported function
3681 OUString getBasicTypeName( SbxDataType eType )
3682 {
3683     static const char* pTypeNames[] =
3684     {
3685         "Empty",            // SbxEMPTY
3686         "Null",             // SbxNULL
3687         "Integer",          // SbxINTEGER
3688         "Long",             // SbxLONG
3689         "Single",           // SbxSINGLE
3690         "Double",           // SbxDOUBLE
3691         "Currency",         // SbxCURRENCY
3692         "Date",             // SbxDATE
3693         "String",           // SbxSTRING
3694         "Object",           // SbxOBJECT
3695         "Error",            // SbxERROR
3696         "Boolean",          // SbxBOOL
3697         "Variant",          // SbxVARIANT
3698         "DataObject",       // SbxDATAOBJECT
3699         "Unknown Type",
3700         "Unknown Type",
3701         "Char",             // SbxCHAR
3702         "Byte",             // SbxBYTE
3703         "UShort",           // SbxUSHORT
3704         "ULong",            // SbxULONG
3705         "Long64",           // SbxLONG64
3706         "ULong64",          // SbxULONG64
3707         "Int",              // SbxINT
3708         "UInt",             // SbxUINT
3709         "Void",             // SbxVOID
3710         "HResult",          // SbxHRESULT
3711         "Pointer",          // SbxPOINTER
3712         "DimArray",         // SbxDIMARRAY
3713         "CArray",           // SbxCARRAY
3714         "Userdef",          // SbxUSERDEF
3715         "Lpstr",            // SbxLPSTR
3716         "Lpwstr",           // SbxLPWSTR
3717         "Unknown Type",     // SbxCoreSTRING
3718         "WString",          // SbxWSTRING
3719         "WChar",            // SbxWCHAR
3720         "Int64",            // SbxSALINT64
3721         "UInt64",           // SbxSALUINT64
3722         "Decimal",          // SbxDECIMAL
3723     };
3724 
3725     size_t nPos = static_cast<size_t>(eType) & 0x0FFF;
3726     const size_t nTypeNameCount = SAL_N_ELEMENTS( pTypeNames );
3727     if ( nPos >= nTypeNameCount )
3728     {
3729         nPos = nTypeNameCount - 1;
3730     }
3731     return OUString::createFromAscii(pTypeNames[nPos]);
3732 }
3733 
3734 static OUString getObjectTypeName( SbxVariable* pVar )
3735 {
3736     OUString sRet( "Object" );
3737     if ( pVar )
3738     {
3739         SbxBase* pBaseObj = pVar->GetObject();
3740         if( !pBaseObj )
3741         {
3742            sRet = "Nothing";
3743         }
3744         else
3745         {
3746             SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pVar  );
3747             if ( !pUnoObj )
3748             {
3749                 pUnoObj = dynamic_cast<SbUnoObject*>( pBaseObj  );
3750             }
3751             if ( pUnoObj )
3752             {
3753                 Any aObj = pUnoObj->getUnoAny();
3754                 // For upstreaming unless we start to build oovbaapi by default
3755                 // we need to get detect the vba-ness of the object in some
3756                 // other way
3757                 // note: Automation objects do not support XServiceInfo
3758                 uno::Reference< XServiceInfo > xServInfo( aObj, uno::UNO_QUERY );
3759                 if ( xServInfo.is() )
3760                 {
3761                     // is this a VBA object ?
3762                     Sequence< OUString > sServices = xServInfo->getSupportedServiceNames();
3763                     if ( sServices.hasElements() )
3764                     {
3765                         sRet = sServices[ 0 ];
3766                     }
3767                 }
3768                 else
3769                 {
3770                     uno::Reference< bridge::oleautomation::XAutomationObject > xAutoMation( aObj, uno::UNO_QUERY );
3771                     if ( xAutoMation.is() )
3772                     {
3773                         uno::Reference< script::XInvocation > xInv( aObj, uno::UNO_QUERY );
3774                         if ( xInv.is() )
3775                         {
3776                             try
3777                             {
3778                                 xInv->getValue( "$GetTypeName" ) >>= sRet;
3779                             }
3780                             catch(const Exception& )
3781                             {
3782                             }
3783                         }
3784                     }
3785                 }
3786                 sal_Int32 nDot = sRet.lastIndexOf( '.' );
3787                 if ( nDot != -1 && nDot < sRet.getLength() )
3788                 {
3789                     sRet = sRet.copy( nDot + 1 );
3790                 }
3791             }
3792         }
3793     }
3794     return sRet;
3795 }
3796 
3797 void SbRtl_TypeName(StarBASIC *, SbxArray & rPar, bool)
3798 {
3799     if ( rPar.Count() != 2 )
3800     {
3801         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3802     }
3803     else
3804     {
3805         SbxDataType eType = rPar.Get(1)->GetType();
3806         bool bIsArray = ( ( eType & SbxARRAY ) != 0 );
3807 
3808         OUString aRetStr;
3809         if ( SbiRuntime::isVBAEnabled() && eType == SbxOBJECT )
3810         {
3811             aRetStr = getObjectTypeName( rPar.Get(1) );
3812         }
3813         else
3814         {
3815             aRetStr = getBasicTypeName( eType );
3816         }
3817         if( bIsArray )
3818         {
3819             aRetStr += "()";
3820         }
3821         rPar.Get(0)->PutString( aRetStr );
3822     }
3823 }
3824 
3825 void SbRtl_Len(StarBASIC *, SbxArray & rPar, bool)
3826 {
3827     if ( rPar.Count() != 2 )
3828     {
3829         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3830     }
3831     else
3832     {
3833         const OUString& rStr = rPar.Get(1)->GetOUString();
3834         rPar.Get(0)->PutLong( rStr.getLength() );
3835     }
3836 }
3837 
3838 void SbRtl_DDEInitiate(StarBASIC *, SbxArray & rPar, bool)
3839 {
3840     int nArgs = static_cast<int>(rPar.Count());
3841     if ( nArgs != 3 )
3842     {
3843         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3844         return;
3845     }
3846     const OUString& rApp = rPar.Get(1)->GetOUString();
3847     const OUString& rTopic = rPar.Get(2)->GetOUString();
3848 
3849     SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3850     size_t nChannel;
3851     ErrCode nDdeErr = pDDE->Initiate( rApp, rTopic, nChannel );
3852     if( nDdeErr )
3853     {
3854         StarBASIC::Error( nDdeErr );
3855     }
3856     else
3857     {
3858         rPar.Get(0)->PutInteger( static_cast<sal_Int16>(nChannel) );
3859     }
3860 }
3861 
3862 void SbRtl_DDETerminate(StarBASIC *, SbxArray & rPar, bool)
3863 {
3864     rPar.Get(0)->PutEmpty();
3865     int nArgs = static_cast<int>(rPar.Count());
3866     if ( nArgs != 2 )
3867     {
3868         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3869         return;
3870     }
3871     size_t nChannel = rPar.Get(1)->GetInteger();
3872     SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3873     ErrCode nDdeErr = pDDE->Terminate( nChannel );
3874     if( nDdeErr )
3875     {
3876         StarBASIC::Error( nDdeErr );
3877     }
3878 }
3879 
3880 void SbRtl_DDETerminateAll(StarBASIC *, SbxArray & rPar, bool)
3881 {
3882     rPar.Get(0)->PutEmpty();
3883     int nArgs = static_cast<int>(rPar.Count());
3884     if ( nArgs != 1 )
3885     {
3886         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3887         return;
3888     }
3889 
3890     SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3891     ErrCode nDdeErr = pDDE->TerminateAll();
3892     if( nDdeErr )
3893     {
3894         StarBASIC::Error( nDdeErr );
3895     }
3896 }
3897 
3898 void SbRtl_DDERequest(StarBASIC *, SbxArray & rPar, bool)
3899 {
3900     int nArgs = static_cast<int>(rPar.Count());
3901     if ( nArgs != 3 )
3902     {
3903         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3904         return;
3905     }
3906     size_t nChannel = rPar.Get(1)->GetInteger();
3907     const OUString& rItem = rPar.Get(2)->GetOUString();
3908     SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3909     OUString aResult;
3910     ErrCode nDdeErr = pDDE->Request( nChannel, rItem, aResult );
3911     if( nDdeErr )
3912     {
3913         StarBASIC::Error( nDdeErr );
3914     }
3915     else
3916     {
3917         rPar.Get(0)->PutString( aResult );
3918     }
3919 }
3920 
3921 void SbRtl_DDEExecute(StarBASIC *, SbxArray & rPar, bool)
3922 {
3923     rPar.Get(0)->PutEmpty();
3924     int nArgs = static_cast<int>(rPar.Count());
3925     if ( nArgs != 3 )
3926     {
3927         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3928         return;
3929     }
3930     size_t nChannel = rPar.Get(1)->GetInteger();
3931     const OUString& rCommand = rPar.Get(2)->GetOUString();
3932     SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3933     ErrCode nDdeErr = pDDE->Execute( nChannel, rCommand );
3934     if( nDdeErr )
3935     {
3936         StarBASIC::Error( nDdeErr );
3937     }
3938 }
3939 
3940 void SbRtl_DDEPoke(StarBASIC *, SbxArray & rPar, bool)
3941 {
3942     rPar.Get(0)->PutEmpty();
3943     int nArgs = static_cast<int>(rPar.Count());
3944     if ( nArgs != 4 )
3945     {
3946         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3947         return;
3948     }
3949     size_t nChannel = rPar.Get(1)->GetInteger();
3950     const OUString& rItem = rPar.Get(2)->GetOUString();
3951     const OUString& rData = rPar.Get(3)->GetOUString();
3952     SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3953     ErrCode nDdeErr = pDDE->Poke( nChannel, rItem, rData );
3954     if( nDdeErr )
3955     {
3956         StarBASIC::Error( nDdeErr );
3957     }
3958 }
3959 
3960 
3961 void SbRtl_FreeFile(StarBASIC *, SbxArray & rPar, bool)
3962 {
3963     if ( rPar.Count() != 1 )
3964     {
3965         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3966         return;
3967     }
3968     SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3969     short nChannel = 1;
3970     while( nChannel < CHANNELS )
3971     {
3972         SbiStream* pStrm = pIO->GetStream( nChannel );
3973         if( !pStrm )
3974         {
3975             rPar.Get(0)->PutInteger( nChannel );
3976             return;
3977         }
3978         nChannel++;
3979     }
3980     StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES );
3981 }
3982 
3983 void SbRtl_LBound(StarBASIC *, SbxArray & rPar, bool)
3984 {
3985     sal_uInt16 nParCount = rPar.Count();
3986     if ( nParCount != 3 && nParCount != 2 )
3987     {
3988         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3989         return;
3990     }
3991     SbxBase* pParObj = rPar.Get(1)->GetObject();
3992     SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
3993     if( pArr )
3994     {
3995         sal_Int32 nLower, nUpper;
3996         short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
3997         if( !pArr->GetDim32( nDim, nLower, nUpper ) )
3998             StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
3999         else
4000             rPar.Get(0)->PutLong( nLower );
4001     }
4002     else
4003         StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
4004 }
4005 
4006 void SbRtl_UBound(StarBASIC *, SbxArray & rPar, bool)
4007 {
4008     sal_uInt16 nParCount = rPar.Count();
4009     if ( nParCount != 3 && nParCount != 2 )
4010     {
4011         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4012         return;
4013     }
4014 
4015     SbxBase* pParObj = rPar.Get(1)->GetObject();
4016     SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
4017     if( pArr )
4018     {
4019         sal_Int32 nLower, nUpper;
4020         short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
4021         if( !pArr->GetDim32( nDim, nLower, nUpper ) )
4022             StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
4023         else
4024             rPar.Get(0)->PutLong( nUpper );
4025     }
4026     else
4027         StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
4028 }
4029 
4030 void SbRtl_RGB(StarBASIC *, SbxArray & rPar, bool)
4031 {
4032     if ( rPar.Count() != 4 )
4033     {
4034         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4035         return;
4036     }
4037 
4038     sal_Int32 nRed     = rPar.Get(1)->GetInteger() & 0xFF;
4039     sal_Int32 nGreen = rPar.Get(2)->GetInteger() & 0xFF;
4040     sal_Int32 nBlue  = rPar.Get(3)->GetInteger() & 0xFF;
4041     sal_Int32 nRGB;
4042 
4043     SbiInstance* pInst = GetSbData()->pInst;
4044     bool bCompatibility = ( pInst && pInst->IsCompatibility() );
4045     if( bCompatibility )
4046     {
4047         nRGB   = (nBlue << 16) | (nGreen << 8) | nRed;
4048     }
4049     else
4050     {
4051         nRGB   = (nRed << 16) | (nGreen << 8) | nBlue;
4052     }
4053     rPar.Get(0)->PutLong( nRGB );
4054 }
4055 
4056 void SbRtl_QBColor(StarBASIC *, SbxArray & rPar, bool)
4057 {
4058     static const sal_Int32 pRGB[] =
4059     {
4060         0x000000,
4061         0x800000,
4062         0x008000,
4063         0x808000,
4064         0x000080,
4065         0x800080,
4066         0x008080,
4067         0xC0C0C0,
4068         0x808080,
4069         0xFF0000,
4070         0x00FF00,
4071         0xFFFF00,
4072         0x0000FF,
4073         0xFF00FF,
4074         0x00FFFF,
4075         0xFFFFFF,
4076     };
4077 
4078     if ( rPar.Count() != 2 )
4079     {
4080         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4081         return;
4082     }
4083 
4084     sal_Int16 nCol = rPar.Get(1)->GetInteger();
4085     if( nCol < 0 || nCol > 15 )
4086     {
4087         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4088         return;
4089     }
4090     sal_Int32 nRGB = pRGB[ nCol ];
4091     rPar.Get(0)->PutLong( nRGB );
4092 }
4093 
4094 // StrConv(string, conversion, LCID)
4095 void SbRtl_StrConv(StarBASIC *, SbxArray & rPar, bool)
4096 {
4097     std::size_t nArgCount = rPar.Count()-1;
4098     if( nArgCount < 2 || nArgCount > 3 )
4099     {
4100         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4101         return;
4102     }
4103 
4104     OUString aOldStr = rPar.Get(1)->GetOUString();
4105     sal_Int32 nConversion = rPar.Get(2)->GetLong();
4106 
4107     LanguageType nLanguage = LANGUAGE_SYSTEM;
4108 
4109     sal_Int32 nOldLen = aOldStr.getLength();
4110     if( nOldLen == 0 )
4111     {
4112         // null string,return
4113         rPar.Get(0)->PutString(aOldStr);
4114         return;
4115     }
4116 
4117     TransliterationFlags nType = TransliterationFlags::NONE;
4118     if ( (nConversion & 0x03) == 3 ) //  vbProperCase
4119     {
4120         const CharClass& rCharClass = GetCharClass();
4121         aOldStr = rCharClass.titlecase( aOldStr.toAsciiLowerCase(), 0, nOldLen );
4122     }
4123     else if ( (nConversion & 0x01) == 1 ) // vbUpperCase
4124     {
4125         nType |= TransliterationFlags::LOWERCASE_UPPERCASE;
4126     }
4127     else if ( (nConversion & 0x02) == 2 ) // vbLowerCase
4128     {
4129         nType |= TransliterationFlags::UPPERCASE_LOWERCASE;
4130     }
4131     if ( (nConversion & 0x04) == 4 ) // vbWide
4132     {
4133         nType |= TransliterationFlags::HALFWIDTH_FULLWIDTH;
4134     }
4135     else if ( (nConversion & 0x08) == 8 ) // vbNarrow
4136     {
4137         nType |= TransliterationFlags::FULLWIDTH_HALFWIDTH;
4138     }
4139     if ( (nConversion & 0x10) == 16) // vbKatakana
4140     {
4141         nType |= TransliterationFlags::HIRAGANA_KATAKANA;
4142     }
4143     else if ( (nConversion & 0x20) == 32 ) // vbHiragana
4144     {
4145         nType |= TransliterationFlags::KATAKANA_HIRAGANA;
4146     }
4147     OUString aNewStr( aOldStr );
4148     if( nType != TransliterationFlags::NONE )
4149     {
4150         uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
4151         ::utl::TransliterationWrapper aTransliterationWrapper( xContext, nType );
4152         uno::Sequence<sal_Int32> aOffsets;
4153         aTransliterationWrapper.loadModuleIfNeeded( nLanguage );
4154         aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets );
4155     }
4156 
4157     if ( (nConversion & 0x40) == 64 ) // vbUnicode
4158     {
4159         // convert the string to byte string, preserving unicode (2 bytes per character)
4160         sal_Int32 nSize = aNewStr.getLength()*2;
4161         const sal_Unicode* pSrc = aNewStr.getStr();
4162         std::unique_ptr<sal_Char[]> pChar(new sal_Char[nSize+1]);
4163         for( sal_Int32 i=0; i < nSize; i++ )
4164         {
4165             pChar[i] = static_cast< sal_Char >( (i%2) ? ((*pSrc) >> 8) & 0xff : (*pSrc) & 0xff );
4166             if( i%2 )
4167             {
4168                 pSrc++;
4169             }
4170         }
4171         pChar[nSize] = '\0';
4172         OString aOStr(pChar.get());
4173 
4174         // there is no concept about default codepage in unix. so it is incorrectly in unix
4175         OUString aOUStr = OStringToOUString(aOStr, osl_getThreadTextEncoding());
4176         rPar.Get(0)->PutString( aOUStr );
4177         return;
4178     }
4179     else if ( (nConversion & 0x80) == 128 ) // vbFromUnicode
4180     {
4181         // there is no concept about default codepage in unix. so it is incorrectly in unix
4182         OString aOStr = OUStringToOString(aNewStr,osl_getThreadTextEncoding());
4183         const sal_Char* pChar = aOStr.getStr();
4184         sal_Int32 nArraySize = aOStr.getLength();
4185         SbxDimArray* pArray = new SbxDimArray(SbxBYTE);
4186         bool bIncIndex = (IsBaseIndexOne() && SbiRuntime::isVBAEnabled() );
4187         if(nArraySize)
4188         {
4189             if( bIncIndex )
4190             {
4191                 pArray->AddDim( 1, nArraySize );
4192             }
4193             else
4194             {
4195                 pArray->AddDim( 0, nArraySize-1 );
4196             }
4197         }
4198         else
4199         {
4200             pArray->unoAddDim( 0, -1 );
4201         }
4202 
4203         for( sal_Int32 i=0; i< nArraySize; i++)
4204         {
4205             SbxVariable* pNew = new SbxVariable( SbxBYTE );
4206             pNew->PutByte(*pChar);
4207             pChar++;
4208             pNew->SetFlag( SbxFlagBits::Write );
4209             short aIdx[1];
4210             aIdx[0] = i;
4211             if( bIncIndex )
4212             {
4213                 ++aIdx[0];
4214             }
4215             pArray->Put(pNew, aIdx);
4216         }
4217 
4218         SbxVariableRef refVar = rPar.Get(0);
4219         SbxFlagBits nFlags = refVar->GetFlags();
4220         refVar->ResetFlag( SbxFlagBits::Fixed );
4221         refVar->PutObject( pArray );
4222         refVar->SetFlags( nFlags );
4223         refVar->SetParameters( nullptr );
4224         return;
4225     }
4226     rPar.Get(0)->PutString(aNewStr);
4227 }
4228 
4229 
4230 void SbRtl_Beep(StarBASIC *, SbxArray & rPar, bool)
4231 {
4232     if ( rPar.Count() != 1 )
4233     {
4234         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4235         return;
4236     }
4237     Sound::Beep();
4238 }
4239 
4240 void SbRtl_Load(StarBASIC *, SbxArray & rPar, bool)
4241 {
4242     if( rPar.Count() != 2 )
4243     {
4244         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4245         return;
4246     }
4247 
4248 
4249     SbxBase* pObj = rPar.Get(1)->GetObject();
4250     if ( pObj )
4251     {
4252         if (SbUserFormModule* pModule = dynamic_cast<SbUserFormModule*>(pObj))
4253         {
4254             pModule->Load();
4255         }
4256         else if (SbxObject* pSbxObj = dynamic_cast<SbxObject*>(pObj))
4257         {
4258             SbxVariable* pVar = pSbxObj->Find("Load", SbxClassType::Method);
4259             if( pVar )
4260             {
4261                 pVar->GetInteger();
4262             }
4263         }
4264     }
4265 }
4266 
4267 void SbRtl_Unload(StarBASIC *, SbxArray & rPar, bool)
4268 {
4269     rPar.Get(0)->PutEmpty();
4270     if( rPar.Count() != 2 )
4271     {
4272         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4273         return;
4274     }
4275 
4276 
4277     SbxBase* pObj = rPar.Get(1)->GetObject();
4278     if ( pObj )
4279     {
4280         if (SbUserFormModule* pFormModule = dynamic_cast<SbUserFormModule*>(pObj))
4281         {
4282             pFormModule->Unload();
4283         }
4284         else if (SbxObject *pSbxObj = dynamic_cast<SbxObject*>(pObj))
4285         {
4286             SbxVariable* pVar = pSbxObj->Find("Unload", SbxClassType::Method);
4287             if( pVar )
4288             {
4289                 pVar->GetInteger();
4290             }
4291         }
4292     }
4293 }
4294 
4295 void SbRtl_LoadPicture(StarBASIC *, SbxArray & rPar, bool)
4296 {
4297     if( rPar.Count() != 2 )
4298     {
4299         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4300         return;
4301     }
4302 
4303     OUString aFileURL = getFullPath( rPar.Get(1)->GetOUString() );
4304     std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aFileURL, StreamMode::READ ));
4305     if( pStream )
4306     {
4307         Bitmap aBmp;
4308         ReadDIB(aBmp, *pStream, true);
4309         Graphic aGraphic(aBmp);
4310 
4311         SbxObjectRef xRef = new SbStdPicture;
4312         static_cast<SbStdPicture*>(xRef.get())->SetGraphic( aGraphic );
4313         rPar.Get(0)->PutObject( xRef.get() );
4314     }
4315 }
4316 
4317 void SbRtl_SavePicture(StarBASIC *, SbxArray & rPar, bool)
4318 {
4319     rPar.Get(0)->PutEmpty();
4320     if( rPar.Count() != 3 )
4321     {
4322         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4323         return;
4324     }
4325 
4326     SbxBase* pObj = rPar.Get(1)->GetObject();
4327     if (SbStdPicture *pPicture = dynamic_cast<SbStdPicture*>(pObj))
4328     {
4329         SvFileStream aOStream( rPar.Get(2)->GetOUString(), StreamMode::WRITE | StreamMode::TRUNC );
4330         const Graphic& aGraphic = pPicture->GetGraphic();
4331         WriteGraphic( aOStream, aGraphic );
4332     }
4333 }
4334 
4335 void SbRtl_MsgBox(StarBASIC *, SbxArray & rPar, bool)
4336 {
4337     sal_uInt16 nArgCount = rPar.Count();
4338     if( nArgCount < 2 || nArgCount > 6 )
4339     {
4340         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4341         return;
4342     }
4343     WinBits nType = 0; // MB_OK
4344     if( nArgCount >= 3 )
4345         nType = static_cast<WinBits>(rPar.Get(2)->GetInteger());
4346     WinBits nStyle = nType;
4347     nStyle &= 15; // delete bits 4-16
4348     if (nStyle > 5)
4349         nStyle = 0;
4350 
4351     enum BasicResponse
4352     {
4353         Ok = 1,
4354         Cancel = 2,
4355         Abort = 3,
4356         Retry = 4,
4357         Ignore = 5,
4358         Yes = 6,
4359         No = 7
4360     };
4361 
4362     OUString aMsg = rPar.Get(1)->GetOUString();
4363     OUString aTitle;
4364     if( nArgCount >= 4 )
4365     {
4366         aTitle = rPar.Get(3)->GetOUString();
4367     }
4368     else
4369     {
4370         aTitle = Application::GetDisplayName();
4371     }
4372 
4373     WinBits nDialogType = nType & (16+32+64);
4374 
4375     SolarMutexGuard aSolarGuard;
4376     vcl::Window* pParentWin = Application::GetDefDialogParent();
4377     weld::Widget* pParent = pParentWin ? pParentWin->GetFrameWeld() : nullptr;
4378 
4379     VclMessageType eType = VclMessageType::Info;
4380 
4381     switch (nDialogType)
4382     {
4383         case 16:
4384             eType = VclMessageType::Error;
4385             break;
4386         case 32:
4387             eType = VclMessageType::Question;
4388             break;
4389         case 48:
4390             eType = VclMessageType::Warning;
4391             break;
4392         case 64:
4393         default:
4394             eType = VclMessageType::Info;
4395             break;
4396     }
4397 
4398     std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
4399                 eType, VclButtonsType::NONE, aMsg));
4400 
4401     switch (nStyle)
4402     {
4403         case 0: // MB_OK
4404         default:
4405             xBox->add_button(GetStandardText(StandardButtonType::OK), BasicResponse::Ok);
4406             break;
4407         case 1: // MB_OKCANCEL
4408             xBox->add_button(GetStandardText(StandardButtonType::OK), BasicResponse::Ok);
4409             xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4410 
4411             if (nType & 256 || nType & 512)
4412                 xBox->set_default_response(BasicResponse::Cancel);
4413             else
4414                 xBox->set_default_response(BasicResponse::Ok);
4415 
4416             break;
4417         case 2: // MB_ABORTRETRYIGNORE
4418             xBox->add_button(GetStandardText(StandardButtonType::Abort), BasicResponse::Abort);
4419             xBox->add_button(GetStandardText(StandardButtonType::Retry), BasicResponse::Retry);
4420             xBox->add_button(GetStandardText(StandardButtonType::Ignore), BasicResponse::Ignore);
4421 
4422             if (nType & 256)
4423                 xBox->set_default_response(BasicResponse::Retry);
4424             else if (nType & 512)
4425                 xBox->set_default_response(BasicResponse::Ignore);
4426             else
4427                 xBox->set_default_response(BasicResponse::Cancel);
4428 
4429             break;
4430         case 3: // MB_YESNOCANCEL
4431             xBox->add_button(GetStandardText(StandardButtonType::Yes), BasicResponse::Yes);
4432             xBox->add_button(GetStandardText(StandardButtonType::No), BasicResponse::No);
4433             xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4434 
4435             if (nType & 256 || nType & 512)
4436                 xBox->set_default_response(BasicResponse::Cancel);
4437             else
4438                 xBox->set_default_response(BasicResponse::Yes);
4439 
4440             break;
4441         case 4: // MB_YESNO
4442             xBox->add_button(GetStandardText(StandardButtonType::Yes), BasicResponse::Yes);
4443             xBox->add_button(GetStandardText(StandardButtonType::No), BasicResponse::No);
4444 
4445             if (nType & 256 || nType & 512)
4446                 xBox->set_default_response(BasicResponse::No);
4447             else
4448                 xBox->set_default_response(BasicResponse::Yes);
4449 
4450             break;
4451         case 5: // MB_RETRYCANCEL
4452             xBox->add_button(GetStandardText(StandardButtonType::Retry), BasicResponse::Retry);
4453             xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4454 
4455             if (nType & 256 || nType & 512)
4456                 xBox->set_default_response(BasicResponse::Cancel);
4457             else
4458                 xBox->set_default_response(BasicResponse::Retry);
4459 
4460             break;
4461     }
4462 
4463     xBox->set_title(aTitle);
4464     sal_Int16 nRet = xBox->run();
4465     rPar.Get(0)->PutInteger(nRet);
4466 }
4467 
4468 void SbRtl_SetAttr(StarBASIC *, SbxArray & rPar, bool)
4469 {
4470     rPar.Get(0)->PutEmpty();
4471     if ( rPar.Count() == 3 )
4472     {
4473         OUString aStr = rPar.Get(1)->GetOUString();
4474         SbAttributes nFlags = static_cast<SbAttributes>( rPar.Get(2)->GetInteger() );
4475 
4476         if( hasUno() )
4477         {
4478             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
4479             if( xSFI.is() )
4480             {
4481                 try
4482                 {
4483                     bool bReadOnly = bool(nFlags & SbAttributes::READONLY);
4484                     xSFI->setReadOnly( aStr, bReadOnly );
4485                     bool bHidden   = bool(nFlags & SbAttributes::HIDDEN);
4486                     xSFI->setHidden( aStr, bHidden );
4487                 }
4488                 catch(const Exception & )
4489                 {
4490                     StarBASIC::Error( ERRCODE_IO_GENERAL );
4491                 }
4492             }
4493         }
4494     }
4495     else
4496     {
4497         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4498     }
4499 }
4500 
4501 void SbRtl_Reset(StarBASIC *, SbxArray &, bool)
4502 {
4503     SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
4504     if (pIO)
4505     {
4506         pIO->CloseAll();
4507     }
4508 }
4509 
4510 void SbRtl_DumpAllObjects(StarBASIC * pBasic, SbxArray & rPar, bool)
4511 {
4512     sal_uInt16 nArgCount = rPar.Count();
4513     if( nArgCount < 2 || nArgCount > 3 )
4514     {
4515         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4516     }
4517     else if( !pBasic )
4518     {
4519         StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
4520     }
4521     else
4522     {
4523         SbxObject* p = pBasic;
4524         while( p->GetParent() )
4525         {
4526             p = p->GetParent();
4527         }
4528         SvFileStream aStrm( rPar.Get( 1 )->GetOUString(),
4529                             StreamMode::WRITE | StreamMode::TRUNC );
4530         p->Dump( aStrm, rPar.Get( 2 )->GetBool() );
4531         aStrm.Close();
4532         if( aStrm.GetError() != ERRCODE_NONE )
4533         {
4534             StarBASIC::Error( ERRCODE_BASIC_IO_ERROR );
4535         }
4536     }
4537 }
4538 
4539 
4540 void SbRtl_FileExists(StarBASIC *, SbxArray & rPar, bool)
4541 {
4542     if ( rPar.Count() == 2 )
4543     {
4544         OUString aStr = rPar.Get(1)->GetOUString();
4545         bool bExists = false;
4546 
4547         if( hasUno() )
4548         {
4549             const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
4550             if( xSFI.is() )
4551             {
4552                 try
4553                 {
4554                     bExists = xSFI->exists( aStr );
4555                 }
4556                 catch(const Exception & )
4557                 {
4558                     StarBASIC::Error( ERRCODE_IO_GENERAL );
4559                 }
4560             }
4561         }
4562         else
4563         {
4564             DirectoryItem aItem;
4565             FileBase::RC nRet = DirectoryItem::get( getFullPath( aStr ), aItem );
4566             bExists = (nRet == FileBase::E_None);
4567         }
4568         rPar.Get(0)->PutBool( bExists );
4569     }
4570     else
4571     {
4572         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4573     }
4574 }
4575 
4576 void SbRtl_Partition(StarBASIC *, SbxArray & rPar, bool)
4577 {
4578     if ( rPar.Count() != 5 )
4579     {
4580         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4581         return;
4582     }
4583 
4584     sal_Int32 nNumber = rPar.Get(1)->GetLong();
4585     sal_Int32 nStart = rPar.Get(2)->GetLong();
4586     sal_Int32 nStop = rPar.Get(3)->GetLong();
4587     sal_Int32 nInterval = rPar.Get(4)->GetLong();
4588 
4589     if( nStart < 0 || nStop <= nStart || nInterval < 1 )
4590     {
4591         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4592         return;
4593     }
4594 
4595     // the Partition function inserts leading spaces before lowervalue and uppervalue
4596     // so that they both have the same number of characters as the string
4597     // representation of the value (Stop + 1). This ensures that if you use the output
4598     // of the Partition function with several values of Number, the resulting text
4599     // will be handled properly during any subsequent sort operation.
4600 
4601     // calculate the maximum number of characters before lowervalue and uppervalue
4602     OUString aBeforeStart = OUString::number( nStart - 1 );
4603     OUString aAfterStop = OUString::number( nStop + 1 );
4604     sal_Int32 nLen1 = aBeforeStart.getLength();
4605     sal_Int32 nLen2 = aAfterStop.getLength();
4606     sal_Int32 nLen = nLen1 >= nLen2 ? nLen1:nLen2;
4607 
4608     OUStringBuffer aRetStr( nLen * 2 + 1);
4609     OUString aLowerValue;
4610     OUString aUpperValue;
4611     if( nNumber < nStart )
4612     {
4613         aUpperValue = aBeforeStart;
4614     }
4615     else if( nNumber > nStop )
4616     {
4617         aLowerValue = aAfterStop;
4618     }
4619     else
4620     {
4621         sal_Int32 nLowerValue = nNumber;
4622         sal_Int32 nUpperValue = nLowerValue;
4623         if( nInterval > 1 )
4624         {
4625             nLowerValue = ((( nNumber - nStart ) / nInterval ) * nInterval ) + nStart;
4626             nUpperValue = nLowerValue + nInterval - 1;
4627         }
4628         aLowerValue = OUString::number( nLowerValue );
4629         aUpperValue = OUString::number( nUpperValue );
4630     }
4631 
4632     nLen1 = aLowerValue.getLength();
4633     nLen2 = aUpperValue.getLength();
4634 
4635     if( nLen > nLen1 )
4636     {
4637         // appending the leading spaces for the lowervalue
4638         for ( sal_Int32 i= nLen - nLen1; i > 0; --i )
4639         {
4640             aRetStr.append(" ");
4641         }
4642     }
4643     aRetStr.append( aLowerValue ).append(":");
4644     if( nLen > nLen2 )
4645     {
4646         // appending the leading spaces for the uppervalue
4647         for ( sal_Int32 i= nLen - nLen2; i > 0; --i )
4648         {
4649             aRetStr.append(" ");
4650         }
4651     }
4652     aRetStr.append( aUpperValue );
4653     rPar.Get(0)->PutString( aRetStr.makeStringAndClear());
4654 }
4655 
4656 #endif
4657 
4658 static long GetDayDiff( const Date& rDate )
4659 {
4660     Date aRefDate( 1,1,1900 );
4661     long nDiffDays;
4662     if ( aRefDate > rDate )
4663     {
4664         nDiffDays = aRefDate - rDate;
4665         nDiffDays *= -1;
4666     }
4667     else
4668     {
4669         nDiffDays = rDate - aRefDate;
4670     }
4671     nDiffDays += 2; // adjustment VisualBasic: 1.Jan.1900 == 2
4672     return nDiffDays;
4673 }
4674 
4675 sal_Int16 implGetDateYear( double aDate )
4676 {
4677     Date aRefDate( 1,1,1900 );
4678     long nDays = static_cast<long>(aDate);
4679     nDays -= 2; // standardize: 1.1.1900 => 0.0
4680     aRefDate.AddDays( nDays );
4681     sal_Int16 nRet = aRefDate.GetYear();
4682     return nRet;
4683 }
4684 
4685 bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
4686         bool bUseTwoDigitYear, SbDateCorrection eCorr, double& rdRet )
4687 {
4688     // XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and
4689     // 30..99 can not be input as they are 2-digit for 2000..2029 and
4690     // 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always
4691     // true). For VBA years > 9999 are invalid.
4692     // For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99
4693     // can not be input as they are 2-digit for 1900..1999, years<0 are
4694     // accepted. If bUseTwoDigitYear==false then all years are accepted, but
4695     // year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01).
4696 #if HAVE_FEATURE_SCRIPTING
4697     if ( (nYear < 0 || 9999 < nYear) && SbiRuntime::isVBAEnabled() )
4698     {
4699         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4700         return false;
4701     }
4702     else if ( nYear < 30 && SbiRuntime::isVBAEnabled() )
4703     {
4704         nYear += 2000;
4705     }
4706     else
4707 #endif
4708     {
4709         if ( 0 <= nYear && nYear < 100 && (bUseTwoDigitYear
4710 #if HAVE_FEATURE_SCRIPTING
4711                     || SbiRuntime::isVBAEnabled()
4712 #endif
4713                     ) )
4714         {
4715             nYear += 1900;
4716         }
4717     }
4718 
4719     sal_Int32 nAddMonths = 0;
4720     sal_Int32 nAddDays = 0;
4721     // Always sanitize values to set date and to use for validity detection.
4722     if (nMonth < 1 || 12 < nMonth)
4723     {
4724         sal_Int16 nM = ((nMonth < 1) ? (12 + (nMonth % 12)) : (nMonth % 12));
4725         nAddMonths = nMonth - nM;
4726         nMonth = nM;
4727     }
4728     // Day 0 would already be normalized during Date::Normalize(), include
4729     // it in negative days, also to detect non-validity. The actual day of
4730     // month is 1+(nDay-1)
4731     if (nDay < 1)
4732     {
4733         nAddDays = nDay - 1;
4734         nDay = 1;
4735     }
4736     else if (nDay > 31)
4737     {
4738         nAddDays = nDay - 31;
4739         nDay = 31;
4740     }
4741 
4742     Date aCurDate( nDay, nMonth, nYear );
4743 
4744     /* TODO: we could enable the same rollover mechanism for StarBASIC to be
4745      * compatible with VBA (just with our wider supported date range), then
4746      * documentation would need to be adapted. As is, the DateSerial() runtime
4747      * function works as dumb as documented... (except that the resulting date
4748      * is checked for validity now and not just day<=31 and month<=12).
4749      * If change wanted then simply remove overriding RollOver here and adapt
4750      * documentation.*/
4751 #if HAVE_FEATURE_SCRIPTING
4752     if (eCorr == SbDateCorrection::RollOver && !SbiRuntime::isVBAEnabled())
4753         eCorr = SbDateCorrection::None;
4754 #endif
4755 
4756     if (nYear == 0 || (eCorr == SbDateCorrection::None && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
4757     {
4758 #if HAVE_FEATURE_SCRIPTING
4759         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4760 #endif
4761         return false;
4762     }
4763 
4764     if (eCorr != SbDateCorrection::None)
4765     {
4766         aCurDate.Normalize();
4767         if (nAddMonths)
4768             aCurDate.AddMonths( nAddMonths);
4769         if (nAddDays)
4770             aCurDate.AddDays( nAddDays);
4771         if (eCorr == SbDateCorrection::TruncateToMonth && aCurDate.GetMonth() != nMonth)
4772         {
4773             if (aCurDate.GetYear() == SAL_MAX_INT16 && nMonth == 12)
4774             {
4775                 // Roll over and back not possible, hard max.
4776                 aCurDate.SetMonth(12);
4777                 aCurDate.SetDay(31);
4778             }
4779             else
4780             {
4781                 aCurDate.SetMonth(nMonth);
4782                 aCurDate.SetDay(1);
4783                 aCurDate.AddMonths(1);
4784                 aCurDate.AddDays(-1);
4785             }
4786         }
4787     }
4788 
4789     long nDiffDays = GetDayDiff( aCurDate );
4790     rdRet = static_cast<double>(nDiffDays);
4791     return true;
4792 }
4793 
4794 double implTimeSerial( sal_Int16 nHours, sal_Int16 nMinutes, sal_Int16 nSeconds )
4795 {
4796     return
4797         static_cast<double>( nHours * ::tools::Time::secondPerHour +
4798                              nMinutes * ::tools::Time::secondPerMinute +
4799                              nSeconds)
4800         /
4801         static_cast<double>( ::tools::Time::secondPerDay );
4802 }
4803 
4804 bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
4805                          sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond,
4806                          double& rdRet )
4807 {
4808     double dDate;
4809     if(!implDateSerial(nYear, nMonth, nDay, false/*bUseTwoDigitYear*/, SbDateCorrection::None, dDate))
4810         return false;
4811     rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond);
4812     return true;
4813 }
4814 
4815 sal_Int16 implGetMinute( double dDate )
4816 {
4817     double nFrac = dDate - floor( dDate );
4818     nFrac *= 86400.0;
4819     sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
4820     sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds % 3600);
4821     sal_Int16 nMin = nTemp / 60;
4822     return nMin;
4823 }
4824 
4825 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4826