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