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