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