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