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