xref: /core/basic/source/runtime/methods1.cxx (revision bc1ab88f)
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 <sal/config.h>
23 #include <config_version.h>
24 
25 #include <cstddef>
26 
27 #include <rtl/math.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/mapmod.hxx>
30 #include <vcl/outdev.hxx>
31 #include <vcl/timer.hxx>
32 #include <vcl/settings.hxx>
33 #include <basic/sbxvar.hxx>
34 #include <basic/sbx.hxx>
35 #include <svl/zforlist.hxx>
36 #include <tools/urlobj.hxx>
37 #include <tools/fract.hxx>
38 #include <o3tl/temporary.hxx>
39 #include <osl/file.hxx>
40 #include <sbobjmod.hxx>
41 #include <basic/sbuno.hxx>
42 
43 #include <date.hxx>
44 #include <sbintern.hxx>
45 #include <runtime.hxx>
46 #include <rtlproto.hxx>
47 #include "dllmgr.hxx"
48 #include <iosys.hxx>
49 #include <sbunoobj.hxx>
50 #include <propacc.hxx>
51 #include <sal/log.hxx>
52 #include <eventatt.hxx>
53 #include <rtl/math.h>
54 #include <svl/numformat.hxx>
55 
56 #include <comphelper/processfactory.hxx>
57 #include <comphelper/string.hxx>
58 
59 #include <com/sun/star/uno/Sequence.hxx>
60 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
61 #include <com/sun/star/i18n/LocaleCalendar2.hpp>
62 #include <com/sun/star/sheet/XFunctionAccess.hpp>
63 
64 #include <officecfg/Office/Scripting.hxx>
65 
66 #include <memory>
67 
68 using namespace comphelper;
69 using namespace com::sun::star::i18n;
70 using namespace com::sun::star::lang;
71 using namespace com::sun::star::sheet;
72 using namespace com::sun::star::uno;
73 
74 static Reference< XCalendar4 > const & getLocaleCalendar()
75 {
76     static Reference< XCalendar4 > xCalendar = LocaleCalendar2::create(getProcessComponentContext());
77     static css::lang::Locale aLastLocale;
78     static bool bNeedsReload = true;
79 
80     css::lang::Locale aLocale = Application::GetSettings().GetLanguageTag().getLocale();
81     bNeedsReload = bNeedsReload ||
82            ( aLocale.Language != aLastLocale.Language ||
83              aLocale.Country  != aLastLocale.Country ||
84              aLocale.Variant  != aLastLocale.Variant );
85     if( bNeedsReload )
86     {
87         bNeedsReload = false;
88         aLastLocale = aLocale;
89         xCalendar->loadDefaultCalendar( aLocale );
90     }
91     return xCalendar;
92 }
93 
94 #if HAVE_FEATURE_SCRIPTING
95 
96 void SbRtl_CallByName(StarBASIC *, SbxArray & rPar, bool)
97 {
98     const sal_Int16 vbGet       = 2;
99     const sal_Int16 vbLet       = 4;
100     const sal_Int16 vbMethod    = 1;
101     const sal_Int16 vbSet       = 8;
102 
103     // At least 3 parameter needed plus function itself -> 4
104     sal_uInt32 nParCount = rPar.Count();
105     if ( nParCount < 4 )
106     {
107         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
108         return;
109     }
110 
111     // 1. parameter is object
112     SbxBase* pObjVar = rPar.Get(1)->GetObject();
113     SbxObject* pObj = nullptr;
114     if( pObjVar )
115         pObj = dynamic_cast<SbxObject*>( pObjVar );
116     if( !pObj )
117         if (auto pSbxVar = dynamic_cast<const SbxVariable*>( pObjVar))
118             pObj = dynamic_cast<SbxObject*>( pSbxVar->GetObject() );
119     if( !pObj )
120     {
121         StarBASIC::Error( ERRCODE_BASIC_BAD_PARAMETER );
122         return;
123     }
124 
125     // 2. parameter is ProcName
126     OUString aNameStr = rPar.Get(2)->GetOUString();
127 
128     // 3. parameter is CallType
129     sal_Int16 nCallType = rPar.Get(3)->GetInteger();
130 
131     //SbxObject* pFindObj = NULL;
132     SbxVariable* pFindVar = pObj->Find( aNameStr, SbxClassType::DontCare );
133     if( pFindVar == nullptr )
134     {
135         StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED );
136         return;
137     }
138 
139     switch( nCallType )
140     {
141     case vbGet:
142         {
143             SbxValues aVals;
144             aVals.eType = SbxVARIANT;
145             pFindVar->Get( aVals );
146 
147             SbxVariableRef refVar = rPar.Get(0);
148             refVar->Put( aVals );
149         }
150         break;
151     case vbLet:
152     case vbSet:
153         {
154             if ( nParCount != 5 )
155             {
156                 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
157                 return;
158             }
159             SbxVariableRef pValVar = rPar.Get(4);
160             if( nCallType == vbLet )
161             {
162                 SbxValues aVals;
163                 aVals.eType = SbxVARIANT;
164                 pValVar->Get( aVals );
165                 pFindVar->Put( aVals );
166             }
167             else
168             {
169                 SbxVariableRef rFindVar = pFindVar;
170                 SbiInstance* pInst = GetSbData()->pInst;
171                 SbiRuntime* pRT = pInst ? pInst->pRun : nullptr;
172                 if( pRT != nullptr )
173                 {
174                     pRT->StepSET_Impl( pValVar, rFindVar );
175                 }
176             }
177         }
178         break;
179     case vbMethod:
180         {
181             SbMethod* pMeth = dynamic_cast<SbMethod*>( pFindVar );
182             if( pMeth == nullptr )
183             {
184                 StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED );
185                 return;
186             }
187 
188             // Setup parameters
189             SbxArrayRef xArray;
190             sal_uInt32 nMethParamCount = nParCount - 4;
191             if( nMethParamCount > 0 )
192             {
193                 xArray = new SbxArray;
194                 for( sal_uInt32 i = 0 ; i < nMethParamCount ; i++ )
195                 {
196                     SbxVariable* pPar = rPar.Get(i + 4);
197                     xArray->Put(pPar, i + 1);
198                 }
199             }
200 
201             // Call method
202             SbxVariableRef refVar = rPar.Get(0);
203             if( xArray.is() )
204                 pMeth->SetParameters( xArray.get() );
205             pMeth->Call( refVar.get() );
206             pMeth->SetParameters( nullptr );
207         }
208         break;
209     default:
210         StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED );
211     }
212 }
213 
214 void SbRtl_CBool(StarBASIC *, SbxArray & rPar, bool) // JSM
215 {
216     bool bVal = false;
217     if (rPar.Count() == 2)
218     {
219         SbxVariable* pSbxVariable = rPar.Get(1);
220         bVal = pSbxVariable->GetBool();
221     }
222     else
223     {
224         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
225     }
226     rPar.Get(0)->PutBool(bVal);
227 }
228 
229 void SbRtl_CByte(StarBASIC *, SbxArray & rPar, bool) // JSM
230 {
231     sal_uInt8 nByte = 0;
232     if (rPar.Count() == 2)
233     {
234         SbxVariable* pSbxVariable = rPar.Get(1);
235         nByte = pSbxVariable->GetByte();
236     }
237     else
238     {
239         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
240     }
241     rPar.Get(0)->PutByte(nByte);
242 }
243 
244 void SbRtl_CCur(StarBASIC *, SbxArray & rPar, bool)
245 {
246     sal_Int64 nCur = 0;
247     if (rPar.Count() == 2)
248     {
249         SbxVariable* pSbxVariable = rPar.Get(1);
250         nCur = pSbxVariable->GetCurrency();
251     }
252     else
253     {
254         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
255     }
256     rPar.Get(0)->PutCurrency(nCur);
257 }
258 
259 void SbRtl_CDec(StarBASIC *, SbxArray & rPar, bool)
260 {
261 #ifdef _WIN32
262     SbxDecimal* pDec = nullptr;
263     if (rPar.Count() == 2)
264     {
265         SbxVariable* pSbxVariable = rPar.Get(1);
266         pDec = pSbxVariable->GetDecimal();
267     }
268     else
269     {
270         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
271     }
272     rPar.Get(0)->PutDecimal(pDec);
273 #else
274     rPar.Get(0)->PutEmpty();
275     StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
276 #endif
277 }
278 
279 void SbRtl_CDate(StarBASIC *, SbxArray & rPar, bool) // JSM
280 {
281     double nVal = 0.0;
282     if (rPar.Count() == 2)
283     {
284         SbxVariable* pSbxVariable = rPar.Get(1);
285         nVal = pSbxVariable->GetDate();
286     }
287     else
288     {
289         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
290     }
291     rPar.Get(0)->PutDate(nVal);
292 }
293 
294 void SbRtl_CDbl(StarBASIC *, SbxArray & rPar, bool)  // JSM
295 {
296     double nVal = 0.0;
297     if (rPar.Count() == 2)
298     {
299         SbxVariable* pSbxVariable = rPar.Get(1);
300         if( pSbxVariable->GetType() == SbxSTRING )
301         {
302             // #41690
303             OUString aScanStr = pSbxVariable->GetOUString();
304             ErrCode Error = SbxValue::ScanNumIntnl( aScanStr, nVal );
305             if( Error != ERRCODE_NONE )
306             {
307                 StarBASIC::Error( Error );
308             }
309         }
310         else
311         {
312             nVal = pSbxVariable->GetDouble();
313         }
314     }
315     else
316     {
317         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
318     }
319 
320     rPar.Get(0)->PutDouble(nVal);
321 }
322 
323 void SbRtl_CInt(StarBASIC *, SbxArray & rPar, bool)  // JSM
324 {
325     sal_Int16 nVal = 0;
326     if (rPar.Count() == 2)
327     {
328         SbxVariable* pSbxVariable = rPar.Get(1);
329         nVal = pSbxVariable->GetInteger();
330     }
331     else
332     {
333         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
334     }
335     rPar.Get(0)->PutInteger(nVal);
336 }
337 
338 void SbRtl_CLng(StarBASIC *, SbxArray & rPar, bool)  // JSM
339 {
340     sal_Int32 nVal = 0;
341     if (rPar.Count() == 2)
342     {
343         SbxVariable* pSbxVariable = rPar.Get(1);
344         nVal = pSbxVariable->GetLong();
345     }
346     else
347     {
348         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
349     }
350     rPar.Get(0)->PutLong(nVal);
351 }
352 
353 void SbRtl_CSng(StarBASIC *, SbxArray & rPar, bool)  // JSM
354 {
355     float nVal = float(0.0);
356     if (rPar.Count() == 2)
357     {
358         SbxVariable* pSbxVariable = rPar.Get(1);
359         if( pSbxVariable->GetType() == SbxSTRING )
360         {
361             // #41690
362             double dVal = 0.0;
363             OUString aScanStr = pSbxVariable->GetOUString();
364             ErrCode Error = SbxValue::ScanNumIntnl( aScanStr, dVal, /*bSingle=*/true );
365             if( SbxBase::GetError() == ERRCODE_NONE && Error != ERRCODE_NONE )
366             {
367                 StarBASIC::Error( Error );
368             }
369             nVal = static_cast<float>(dVal);
370         }
371         else
372         {
373             nVal = pSbxVariable->GetSingle();
374         }
375     }
376     else
377     {
378         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
379     }
380     rPar.Get(0)->PutSingle(nVal);
381 }
382 
383 void SbRtl_CStr(StarBASIC *, SbxArray & rPar, bool)  // JSM
384 {
385     OUString aString;
386     if (rPar.Count() == 2)
387     {
388         SbxVariable* pSbxVariable = rPar.Get(1);
389         aString = pSbxVariable->GetOUString();
390     }
391     else
392     {
393         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
394     }
395     rPar.Get(0)->PutString(aString);
396 }
397 
398 void SbRtl_CVar(StarBASIC *, SbxArray & rPar, bool)  // JSM
399 {
400     SbxValues aVals( SbxVARIANT );
401     if (rPar.Count() == 2)
402     {
403         SbxVariable* pSbxVariable = rPar.Get(1);
404         pSbxVariable->Get( aVals );
405     }
406     else
407     {
408         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
409     }
410     rPar.Get(0)->Put(aVals);
411 }
412 
413 void SbRtl_CVErr(StarBASIC *, SbxArray & rPar, bool)
414 {
415     sal_Int16 nErrCode = 0;
416     if (rPar.Count() == 2)
417     {
418         SbxVariable* pSbxVariable = rPar.Get(1);
419         nErrCode = pSbxVariable->GetInteger();
420     }
421     else
422     {
423         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
424     }
425     rPar.Get(0)->PutErr(nErrCode);
426 }
427 
428 void SbRtl_Iif(StarBASIC *, SbxArray & rPar, bool) // JSM
429 {
430     if (rPar.Count() == 4)
431     {
432         if (rPar.Get(1)->GetBool())
433         {
434             *rPar.Get(0) = *rPar.Get(2);
435         }
436         else
437         {
438             *rPar.Get(0) = *rPar.Get(3);
439         }
440     }
441     else
442     {
443         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
444     }
445 }
446 
447 void SbRtl_GetSystemType(StarBASIC *, SbxArray & rPar, bool)
448 {
449     if (rPar.Count() != 1)
450     {
451         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
452     }
453     else
454     {
455         // Removed for SRC595
456         rPar.Get(0)->PutInteger(-1);
457     }
458 }
459 
460 void SbRtl_GetGUIType(StarBASIC *, SbxArray & rPar, bool)
461 {
462     if (rPar.Count() != 1)
463     {
464         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
465     }
466     else
467     {
468         // 17.7.2000 Make simple solution for testtool / fat office
469 #if   defined(_WIN32)
470         rPar.Get(0)->PutInteger(1);
471 #elif defined(UNX)
472         rPar.Get(0)->PutInteger(4);
473 #else
474         rPar.Get(0)->PutInteger(-1);
475 #endif
476     }
477 }
478 
479 void SbRtl_Red(StarBASIC *, SbxArray & rPar, bool)
480 {
481     if (rPar.Count() != 2)
482     {
483         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
484     }
485     else
486     {
487         sal_Int32 nRGB = rPar.Get(1)->GetLong();
488         nRGB &= 0x00FF0000;
489         nRGB >>= 16;
490         rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
491     }
492 }
493 
494 void SbRtl_Green(StarBASIC *, SbxArray & rPar, bool)
495 {
496     if (rPar.Count() != 2)
497     {
498         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
499     }
500     else
501     {
502         sal_Int32 nRGB = rPar.Get(1)->GetLong();
503         nRGB &= 0x0000FF00;
504         nRGB >>= 8;
505         rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
506     }
507 }
508 
509 void SbRtl_Blue(StarBASIC *, SbxArray & rPar, bool)
510 {
511     if (rPar.Count() != 2)
512     {
513         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
514     }
515     else
516     {
517         sal_Int32 nRGB = rPar.Get(1)->GetLong();
518         nRGB &= 0x000000FF;
519         rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
520     }
521 }
522 
523 
524 void SbRtl_Switch(StarBASIC *, SbxArray & rPar, bool)
525 {
526     sal_uInt32 nCount = rPar.Count();
527     if( !(nCount & 0x0001 ))
528     {
529         // number of arguments must be odd
530         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
531     }
532     sal_uInt32 nCurExpr = 1;
533     while( nCurExpr < (nCount-1) )
534     {
535         if (rPar.Get(nCurExpr)->GetBool())
536         {
537             (*rPar.Get(0)) = *(rPar.Get(nCurExpr + 1));
538             return;
539         }
540         nCurExpr += 2;
541     }
542     rPar.Get(0)->PutNull();
543 }
544 
545 //i#64882# Common wait impl for existing Wait and new WaitUntil
546 // rtl functions
547 void Wait_Impl( bool bDurationBased, SbxArray& rPar )
548 {
549     if (rPar.Count() != 2)
550     {
551         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
552         return;
553     }
554     tools::Long nWait = 0;
555     if ( bDurationBased )
556     {
557         double dWait = rPar.Get(1)->GetDouble();
558         double dNow = Now_Impl();
559         double dSecs = ( dWait - dNow ) * 24.0 * 3600.0;
560         nWait = static_cast<tools::Long>( dSecs * 1000 ); // wait in thousands of sec
561     }
562     else
563     {
564         nWait = rPar.Get(1)->GetLong();
565     }
566     if( nWait < 0 )
567     {
568         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
569         return;
570     }
571 
572     Timer aTimer("basic Wait_Impl");
573     aTimer.SetTimeout( nWait );
574     aTimer.Start();
575     while ( aTimer.IsActive() && !Application::IsQuit())
576     {
577         Application::Yield();
578     }
579 }
580 
581 //i#64882#
582 void SbRtl_Wait(StarBASIC *, SbxArray & rPar, bool)
583 {
584     Wait_Impl( false, rPar );
585 }
586 
587 //i#64882# add new WaitUntil ( for application.wait )
588 // share wait_impl with 'normal' oobasic wait
589 void SbRtl_WaitUntil(StarBASIC *, SbxArray & rPar, bool)
590 {
591     Wait_Impl( true, rPar );
592 }
593 
594 void SbRtl_DoEvents(StarBASIC *, SbxArray & rPar, bool)
595 {
596 // don't understand what upstream are up to
597 // we already process application events etc. in between
598 // basic runtime pcode ( on a timed basis )
599     // always return 0
600     rPar.Get(0)->PutInteger(0);
601     Application::Reschedule( true );
602 }
603 
604 void SbRtl_GetGUIVersion(StarBASIC *, SbxArray & rPar, bool)
605 {
606     if (rPar.Count() != 1)
607     {
608         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
609     }
610     else
611     {
612         // Removed for SRC595
613         rPar.Get(0)->PutLong(-1);
614     }
615 }
616 
617 void SbRtl_Choose(StarBASIC *, SbxArray & rPar, bool)
618 {
619     if (rPar.Count() < 2)
620     {
621         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
622     }
623     sal_Int16 nIndex = rPar.Get(1)->GetInteger();
624     sal_uInt32 nCount = rPar.Count();
625     nCount--;
626     if( nCount == 1 || nIndex > sal::static_int_cast<sal_Int16>(nCount-1) || nIndex < 1 )
627     {
628         rPar.Get(0)->PutNull();
629         return;
630     }
631     (*rPar.Get(0)) = *(rPar.Get(nIndex + 1));
632 }
633 
634 
635 void SbRtl_Trim(StarBASIC *, SbxArray & rPar, bool)
636 {
637     if (rPar.Count() < 2)
638     {
639         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
640     }
641     else
642     {
643         OUString aStr(comphelper::string::strip(rPar.Get(1)->GetOUString(), ' '));
644         rPar.Get(0)->PutString(aStr);
645     }
646 }
647 
648 void SbRtl_GetSolarVersion(StarBASIC *, SbxArray & rPar, bool)
649 {
650     rPar.Get(0)->PutLong(LIBO_VERSION_MAJOR * 10000 + LIBO_VERSION_MINOR * 100
651                          + LIBO_VERSION_MICRO * 1);
652 }
653 
654 void SbRtl_TwipsPerPixelX(StarBASIC *, SbxArray & rPar, bool)
655 {
656     sal_Int32 nResult = 0;
657     Size aSize( 100,0 );
658     MapMode aMap( MapUnit::MapTwip );
659     OutputDevice* pDevice = Application::GetDefaultDevice();
660     if( pDevice )
661     {
662         aSize = pDevice->PixelToLogic( aSize, aMap );
663         nResult = aSize.Width() / 100;
664     }
665     rPar.Get(0)->PutLong(nResult);
666 }
667 
668 void SbRtl_TwipsPerPixelY(StarBASIC *, SbxArray & rPar, bool)
669 {
670     sal_Int32 nResult = 0;
671     Size aSize( 0,100 );
672     MapMode aMap( MapUnit::MapTwip );
673     OutputDevice* pDevice = Application::GetDefaultDevice();
674     if( pDevice )
675     {
676         aSize = pDevice->PixelToLogic( aSize, aMap );
677         nResult = aSize.Height() / 100;
678     }
679     rPar.Get(0)->PutLong(nResult);
680 }
681 
682 
683 void SbRtl_FreeLibrary(StarBASIC *, SbxArray & rPar, bool)
684 {
685     if (rPar.Count() != 2)
686     {
687         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
688     }
689     GetSbData()->pInst->GetDllMgr()->FreeDll(rPar.Get(1)->GetOUString());
690 }
691 bool IsBaseIndexOne()
692 {
693     bool bResult = false;
694     if ( GetSbData()->pInst && GetSbData()->pInst->pRun )
695     {
696         sal_uInt16 res = GetSbData()->pInst->pRun->GetBase();
697         if ( res )
698         {
699             bResult = true;
700         }
701     }
702     return bResult;
703 }
704 
705 void SbRtl_Array(StarBASIC *, SbxArray & rPar, bool)
706 {
707     SbxDimArray* pArray = new SbxDimArray( SbxVARIANT );
708     sal_uInt32 nArraySize = rPar.Count() - 1;
709     bool bIncIndex = IsBaseIndexOne();
710     if( nArraySize )
711     {
712         if ( bIncIndex )
713         {
714             pArray->AddDim(1, sal::static_int_cast<sal_Int32>(nArraySize));
715         }
716         else
717         {
718             pArray->AddDim(0, sal::static_int_cast<sal_Int32>(nArraySize) - 1);
719         }
720     }
721     else
722     {
723         pArray->unoAddDim(0, -1);
724     }
725 
726     // insert parameters into the array
727     for( sal_uInt32 i = 0 ; i < nArraySize ; i++ )
728     {
729         SbxVariable* pVar = rPar.Get(i + 1);
730         SbxVariable* pNew = new SbxEnsureParentVariable(*pVar);
731         pNew->SetFlag( SbxFlagBits::Write );
732         sal_Int32 aIdx[1];
733         aIdx[0] = static_cast<sal_Int32>(i);
734         if ( bIncIndex )
735         {
736             ++aIdx[0];
737         }
738         pArray->Put(pNew, aIdx);
739     }
740 
741     // return array
742     SbxVariableRef refVar = rPar.Get(0);
743     SbxFlagBits nFlags = refVar->GetFlags();
744     refVar->ResetFlag( SbxFlagBits::Fixed );
745     refVar->PutObject( pArray );
746     refVar->SetFlags( nFlags );
747     refVar->SetParameters( nullptr );
748 }
749 
750 
751 // Featurewish #57868
752 // The function returns a variant-array; if there are no parameters passed,
753 // an empty array is created (according to dim a(); equal to a sequence of
754 // the length 0 in Uno).
755 // If there are parameters passed, there's a dimension created for each of
756 // them; DimArray( 2, 2, 4 ) is equal to DIM a( 2, 2, 4 )
757 // the array is always of the type variant
758 void SbRtl_DimArray(StarBASIC *, SbxArray & rPar, bool)
759 {
760     SbxDimArray * pArray = new SbxDimArray( SbxVARIANT );
761     sal_uInt32 nArrayDims = rPar.Count() - 1;
762     if( nArrayDims > 0 )
763     {
764         for( sal_uInt32 i = 0; i < nArrayDims ; i++ )
765         {
766             sal_Int32 ub = rPar.Get(i + 1)->GetLong();
767             if( ub < 0 )
768             {
769                 StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
770                 ub = 0;
771             }
772             pArray->AddDim(0, ub);
773         }
774     }
775     else
776     {
777         pArray->unoAddDim(0, -1);
778     }
779     SbxVariableRef refVar = rPar.Get(0);
780     SbxFlagBits nFlags = refVar->GetFlags();
781     refVar->ResetFlag( SbxFlagBits::Fixed );
782     refVar->PutObject( pArray );
783     refVar->SetFlags( nFlags );
784     refVar->SetParameters( nullptr );
785 }
786 
787 /*
788  * FindObject and FindPropertyObject make it possible to
789  * address objects and properties of the type Object with
790  * their name as string-parameters at the runtime.
791  *
792  * Example:
793  * MyObj.Prop1.Bla = 5
794  *
795  * is equal to:
796  * dim ObjVar as Object
797  * dim ObjProp as Object
798  * ObjName$ = "MyObj"
799  * ObjVar = FindObject( ObjName$ )
800  * PropName$ = "Prop1"
801  * ObjProp = FindPropertyObject( ObjVar, PropName$ )
802  * ObjProp.Bla = 5
803  *
804  * The names can be created dynamically at the runtime
805  * so that e. g. via controls "TextEdit1" to "TextEdit5"
806  * can be iterated in a dialog in a loop.
807  */
808 
809 
810 // 1st parameter = the object's name as string
811 void SbRtl_FindObject(StarBASIC *, SbxArray & rPar, bool)
812 {
813     if (rPar.Count() < 2)
814     {
815         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
816         return;
817     }
818 
819     OUString aNameStr = rPar.Get(1)->GetOUString();
820 
821     SbxBase* pFind =  StarBASIC::FindSBXInCurrentScope( aNameStr );
822     SbxObject* pFindObj = nullptr;
823     if( pFind )
824     {
825         pFindObj = dynamic_cast<SbxObject*>( pFind );
826     }
827     SbxVariableRef refVar = rPar.Get(0);
828     refVar->PutObject( pFindObj );
829 }
830 
831 // address object-property in an object
832 // 1st parameter = object
833 // 2nd parameter = the property's name as string
834 void SbRtl_FindPropertyObject(StarBASIC *, SbxArray & rPar, bool)
835 {
836     if (rPar.Count() < 3)
837     {
838         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
839         return;
840     }
841 
842     SbxBase* pObjVar = rPar.Get(1)->GetObject();
843     SbxObject* pObj = nullptr;
844     if( pObjVar )
845     {
846         pObj = dynamic_cast<SbxObject*>( pObjVar );
847     }
848     if( !pObj )
849         if (auto pSbxVar = dynamic_cast<const SbxVariable*>( pObjVar))
850             pObj = dynamic_cast<SbxObject*>( pSbxVar->GetObject() );
851 
852     OUString aNameStr = rPar.Get(2)->GetOUString();
853 
854     SbxObject* pFindObj = nullptr;
855     if( pObj )
856     {
857         SbxVariable* pFindVar = pObj->Find( aNameStr, SbxClassType::Object );
858         pFindObj = dynamic_cast<SbxObject*>( pFindVar );
859     }
860     else
861     {
862         StarBASIC::Error( ERRCODE_BASIC_BAD_PARAMETER );
863     }
864 
865     SbxVariableRef refVar = rPar.Get(0);
866     refVar->PutObject( pFindObj );
867 }
868 
869 
870 static bool lcl_WriteSbxVariable( const SbxVariable& rVar, SvStream* pStrm,
871                                       bool bBinary, short nBlockLen, bool bIsArray )
872 {
873     sal_uInt64 const nFPos = pStrm->Tell();
874 
875     bool bIsVariant = !rVar.IsFixed();
876     SbxDataType eType = rVar.GetType();
877 
878     switch( eType )
879     {
880     case SbxBOOL:
881     case SbxCHAR:
882     case SbxBYTE:
883         if( bIsVariant )
884         {
885             pStrm->WriteUInt16( SbxBYTE ); // VarType Id
886         }
887         pStrm->WriteUChar( rVar.GetByte() );
888         break;
889 
890     case SbxEMPTY:
891     case SbxNULL:
892     case SbxVOID:
893     case SbxINTEGER:
894     case SbxUSHORT:
895     case SbxINT:
896     case SbxUINT:
897         if( bIsVariant )
898         {
899             pStrm->WriteUInt16( SbxINTEGER ); // VarType Id
900         }
901         pStrm->WriteInt16( rVar.GetInteger() );
902         break;
903 
904     case SbxLONG:
905     case SbxULONG:
906         if( bIsVariant )
907         {
908             pStrm->WriteUInt16( SbxLONG ); // VarType Id
909         }
910         pStrm->WriteInt32( rVar.GetLong() );
911         break;
912     case SbxSALINT64:
913     case SbxSALUINT64:
914         if( bIsVariant )
915         {
916             pStrm->WriteUInt16( SbxSALINT64 ); // VarType Id
917         }
918         pStrm->WriteUInt64( rVar.GetInt64() );
919         break;
920     case SbxSINGLE:
921         if( bIsVariant )
922         {
923             pStrm->WriteUInt16( eType ); // VarType Id
924         }
925         pStrm->WriteFloat( rVar.GetSingle() );
926         break;
927 
928     case SbxDOUBLE:
929     case SbxCURRENCY:
930     case SbxDATE:
931         if( bIsVariant )
932         {
933             pStrm->WriteUInt16( eType ); // VarType Id
934         }
935         pStrm->WriteDouble( rVar.GetDouble() );
936         break;
937 
938     case SbxSTRING:
939     case SbxLPSTR:
940         {
941             const OUString& rStr = rVar.GetOUString();
942             if( !bBinary || bIsArray )
943             {
944                 if( bIsVariant )
945                 {
946                     pStrm->WriteUInt16( SbxSTRING );
947                 }
948                 pStrm->WriteUniOrByteString( rStr, osl_getThreadTextEncoding() );
949             }
950             else
951             {
952                 // without any length information! without end-identifier!
953                 // What does that mean for Unicode?! Choosing conversion to ByteString...
954                 OString aByteStr(OUStringToOString(rStr, osl_getThreadTextEncoding()));
955                 pStrm->WriteOString( aByteStr );
956             }
957         }
958         break;
959 
960     default:
961         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
962         return false;
963     }
964 
965     if( nBlockLen )
966     {
967         pStrm->Seek( nFPos + nBlockLen );
968     }
969     return pStrm->GetErrorCode() == ERRCODE_NONE;
970 }
971 
972 static bool lcl_ReadSbxVariable( SbxVariable& rVar, SvStream* pStrm,
973                                      bool bBinary, short nBlockLen )
974 {
975     double aDouble;
976 
977     sal_uInt64 const nFPos = pStrm->Tell();
978 
979     bool bIsVariant = !rVar.IsFixed();
980     SbxDataType eVarType = rVar.GetType();
981 
982     SbxDataType eSrcType = eVarType;
983     if( bIsVariant )
984     {
985         sal_uInt16 nTemp;
986         pStrm->ReadUInt16( nTemp );
987         eSrcType = static_cast<SbxDataType>(nTemp);
988     }
989 
990     switch( eSrcType )
991     {
992     case SbxBOOL:
993     case SbxCHAR:
994     case SbxBYTE:
995         {
996             sal_uInt8 aByte;
997             pStrm->ReadUChar( aByte );
998 
999             if( bBinary && SbiRuntime::isVBAEnabled() && aByte == 1 && pStrm->eof() )
1000             {
1001                 aByte = 0;
1002             }
1003             rVar.PutByte( aByte );
1004         }
1005         break;
1006 
1007     case SbxEMPTY:
1008     case SbxNULL:
1009     case SbxVOID:
1010     case SbxINTEGER:
1011     case SbxUSHORT:
1012     case SbxINT:
1013     case SbxUINT:
1014         {
1015             sal_Int16 aInt;
1016             pStrm->ReadInt16( aInt );
1017             rVar.PutInteger( aInt );
1018         }
1019         break;
1020 
1021     case SbxLONG:
1022     case SbxULONG:
1023         {
1024             sal_Int32 aInt;
1025             pStrm->ReadInt32( aInt );
1026             rVar.PutLong( aInt );
1027         }
1028         break;
1029     case SbxSALINT64:
1030     case SbxSALUINT64:
1031         {
1032             sal_uInt32 aInt;
1033             pStrm->ReadUInt32( aInt );
1034             rVar.PutInt64( static_cast<sal_Int64>(aInt) );
1035         }
1036         break;
1037     case SbxSINGLE:
1038         {
1039             float nS;
1040             pStrm->ReadFloat( nS );
1041             rVar.PutSingle( nS );
1042         }
1043         break;
1044 
1045     case SbxDOUBLE:
1046     case SbxCURRENCY:
1047         {
1048             pStrm->ReadDouble( aDouble );
1049             rVar.PutDouble( aDouble );
1050         }
1051         break;
1052 
1053     case SbxDATE:
1054         {
1055             pStrm->ReadDouble( aDouble );
1056             rVar.PutDate( aDouble );
1057         }
1058         break;
1059 
1060     case SbxSTRING:
1061     case SbxLPSTR:
1062         {
1063             OUString aStr = pStrm->ReadUniOrByteString(osl_getThreadTextEncoding());
1064             rVar.PutString( aStr );
1065         }
1066         break;
1067 
1068     default:
1069         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1070         return false;
1071     }
1072 
1073     if( nBlockLen )
1074     {
1075         pStrm->Seek( nFPos + nBlockLen );
1076     }
1077     return pStrm->GetErrorCode() == ERRCODE_NONE;
1078 }
1079 
1080 
1081 // nCurDim = 1...n
1082 static bool lcl_WriteReadSbxArray( SbxDimArray& rArr, SvStream* pStrm,
1083     bool bBinary, sal_Int32 nCurDim, sal_Int32* pOtherDims, bool bWrite )
1084 {
1085     SAL_WARN_IF( nCurDim <= 0,"basic", "Bad Dim");
1086     sal_Int32 nLower, nUpper;
1087     if (!rArr.GetDim(nCurDim, nLower, nUpper))
1088         return false;
1089     for(sal_Int32 nCur = nLower; nCur <= nUpper; nCur++ )
1090     {
1091         pOtherDims[ nCurDim-1 ] = nCur;
1092         if( nCurDim != 1 )
1093             lcl_WriteReadSbxArray(rArr, pStrm, bBinary, nCurDim-1, pOtherDims, bWrite);
1094         else
1095         {
1096             SbxVariable* pVar = rArr.Get(pOtherDims);
1097             bool bRet;
1098             if( bWrite )
1099                 bRet = lcl_WriteSbxVariable(*pVar, pStrm, bBinary, 0, true );
1100             else
1101                 bRet = lcl_ReadSbxVariable(*pVar, pStrm, bBinary, 0 );
1102             if( !bRet )
1103                 return false;
1104         }
1105     }
1106     return true;
1107 }
1108 
1109 static void PutGet( SbxArray& rPar, bool bPut )
1110 {
1111     if (rPar.Count() != 4)
1112     {
1113         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1114         return;
1115     }
1116     sal_Int16 nFileNo = rPar.Get(1)->GetInteger();
1117     SbxVariable* pVar2 = rPar.Get(2);
1118     SbxDataType eType2 = pVar2->GetType();
1119     bool bHasRecordNo = (eType2 != SbxEMPTY && eType2 != SbxERROR);
1120     tools::Long nRecordNo = pVar2->GetLong();
1121     if ( nFileNo < 1 || ( bHasRecordNo && nRecordNo < 1 ) )
1122     {
1123         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1124         return;
1125     }
1126     nRecordNo--;
1127     SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
1128     SbiStream* pSbStrm = pIO->GetStream( nFileNo );
1129 
1130     if ( !pSbStrm || !(pSbStrm->GetMode() & (SbiStreamFlags::Binary | SbiStreamFlags::Random)) )
1131     {
1132         StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
1133         return;
1134     }
1135 
1136     SvStream* pStrm = pSbStrm->GetStrm();
1137     bool bRandom = pSbStrm->IsRandom();
1138     short nBlockLen = bRandom ? pSbStrm->GetBlockLen() : 0;
1139 
1140     if( bPut )
1141     {
1142         pSbStrm->ExpandFile();
1143     }
1144 
1145     if( bHasRecordNo )
1146     {
1147         sal_uInt64 const nFilePos = bRandom
1148             ? static_cast<sal_uInt64>(nBlockLen * nRecordNo)
1149             : static_cast<sal_uInt64>(nRecordNo);
1150         pStrm->Seek( nFilePos );
1151     }
1152 
1153     SbxDimArray* pArr = nullptr;
1154     SbxVariable* pVar = rPar.Get(3);
1155     if( pVar->GetType() & SbxARRAY )
1156     {
1157         SbxBase* pParObj = pVar->GetObject();
1158         pArr = dynamic_cast<SbxDimArray*>( pParObj );
1159     }
1160 
1161     bool bRet;
1162 
1163     if( pArr )
1164     {
1165         sal_uInt64 const nFPos = pStrm->Tell();
1166         sal_Int32 nDims = pArr->GetDims();
1167         std::unique_ptr<sal_Int32[]> pDims(new sal_Int32[ nDims ]);
1168         bRet = lcl_WriteReadSbxArray(*pArr,pStrm,!bRandom,nDims,pDims.get(),bPut);
1169         pDims.reset();
1170         if( nBlockLen )
1171             pStrm->Seek( nFPos + nBlockLen );
1172     }
1173     else
1174     {
1175         if( bPut )
1176             bRet = lcl_WriteSbxVariable(*pVar, pStrm, !bRandom, nBlockLen, false);
1177         else
1178             bRet = lcl_ReadSbxVariable(*pVar, pStrm, !bRandom, nBlockLen);
1179     }
1180     if( !bRet || pStrm->GetErrorCode() )
1181         StarBASIC::Error( ERRCODE_BASIC_IO_ERROR );
1182 }
1183 
1184 void SbRtl_Put(StarBASIC *, SbxArray & rPar, bool)
1185 {
1186     PutGet( rPar, true );
1187 }
1188 
1189 void SbRtl_Get(StarBASIC *, SbxArray & rPar, bool)
1190 {
1191     PutGet( rPar, false );
1192 }
1193 
1194 void SbRtl_Environ(StarBASIC *, SbxArray & rPar, bool)
1195 {
1196     if (rPar.Count() != 2)
1197     {
1198         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1199         return;
1200     }
1201     OUString aResult;
1202     // should be ANSI but that's not possible under Win16 in the DLL
1203     OString aByteStr(OUStringToOString(rPar.Get(1)->GetOUString(),
1204                                                  osl_getThreadTextEncoding()));
1205     const char* pEnvStr = getenv(aByteStr.getStr());
1206     if ( pEnvStr )
1207     {
1208         aResult = OUString(pEnvStr, strlen(pEnvStr), osl_getThreadTextEncoding());
1209     }
1210     rPar.Get(0)->PutString(aResult);
1211 }
1212 
1213 static double GetDialogZoomFactor( bool bX, tools::Long nValue )
1214 {
1215     OutputDevice* pDevice = Application::GetDefaultDevice();
1216     double nResult = 0;
1217     if( pDevice )
1218     {
1219         Size aRefSize( nValue, nValue );
1220         Fraction aFracX( 1, 26 );
1221         Fraction aFracY( 1, 24 );
1222         MapMode aMap( MapUnit::MapAppFont, Point(), aFracX, aFracY );
1223         Size aScaledSize = pDevice->LogicToPixel( aRefSize, aMap );
1224         aRefSize = pDevice->LogicToPixel( aRefSize, MapMode(MapUnit::MapTwip) );
1225 
1226         double nRef, nScaled;
1227         if( bX )
1228         {
1229             nRef = aRefSize.Width();
1230             nScaled = aScaledSize.Width();
1231         }
1232         else
1233         {
1234             nRef = aRefSize.Height();
1235             nScaled = aScaledSize.Height();
1236         }
1237         nResult = nScaled / nRef;
1238     }
1239     return nResult;
1240 }
1241 
1242 
1243 void SbRtl_GetDialogZoomFactorX(StarBASIC *, SbxArray & rPar, bool)
1244 {
1245     if (rPar.Count() != 2)
1246     {
1247         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1248         return;
1249     }
1250     rPar.Get(0)->PutDouble(GetDialogZoomFactor(true, rPar.Get(1)->GetLong()));
1251 }
1252 
1253 void SbRtl_GetDialogZoomFactorY(StarBASIC *, SbxArray & rPar, bool)
1254 {
1255     if (rPar.Count() != 2)
1256     {
1257         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1258         return;
1259     }
1260     rPar.Get(0)->PutDouble(GetDialogZoomFactor(false, rPar.Get(1)->GetLong()));
1261 }
1262 
1263 
1264 void SbRtl_EnableReschedule(StarBASIC *, SbxArray & rPar, bool)
1265 {
1266     rPar.Get(0)->PutEmpty();
1267     if (rPar.Count() != 2)
1268         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1269     if( GetSbData()->pInst )
1270         GetSbData()->pInst->EnableReschedule(rPar.Get(1)->GetBool());
1271 }
1272 
1273 void SbRtl_GetSystemTicks(StarBASIC *, SbxArray & rPar, bool)
1274 {
1275     if (rPar.Count() != 1)
1276     {
1277         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1278         return;
1279     }
1280     rPar.Get(0)->PutLong(tools::Time::GetSystemTicks());
1281 }
1282 
1283 void SbRtl_GetPathSeparator(StarBASIC *, SbxArray & rPar, bool)
1284 {
1285     if (rPar.Count() != 1)
1286     {
1287         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1288         return;
1289     }
1290     rPar.Get(0)->PutString(OUString(SAL_PATHDELIMITER));
1291 }
1292 
1293 void SbRtl_ResolvePath(StarBASIC *, SbxArray & rPar, bool)
1294 {
1295     if (rPar.Count() == 2)
1296     {
1297         OUString aStr = rPar.Get(1)->GetOUString();
1298         rPar.Get(0)->PutString(aStr);
1299     }
1300     else
1301     {
1302         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1303     }
1304 }
1305 
1306 void SbRtl_TypeLen(StarBASIC *, SbxArray & rPar, bool)
1307 {
1308     if (rPar.Count() != 2)
1309     {
1310         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1311     }
1312     else
1313     {
1314         SbxDataType eType = rPar.Get(1)->GetType();
1315         sal_Int16 nLen = 0;
1316         switch( eType )
1317         {
1318         case SbxEMPTY:
1319         case SbxNULL:
1320         case SbxVECTOR:
1321         case SbxARRAY:
1322         case SbxBYREF:
1323         case SbxVOID:
1324         case SbxHRESULT:
1325         case SbxPOINTER:
1326         case SbxDIMARRAY:
1327         case SbxCARRAY:
1328         case SbxUSERDEF:
1329             nLen = 0;
1330             break;
1331 
1332         case SbxINTEGER:
1333         case SbxERROR:
1334         case SbxUSHORT:
1335         case SbxINT:
1336         case SbxUINT:
1337             nLen = 2;
1338             break;
1339 
1340         case SbxLONG:
1341         case SbxSINGLE:
1342         case SbxULONG:
1343             nLen = 4;
1344             break;
1345 
1346         case SbxDOUBLE:
1347         case SbxCURRENCY:
1348         case SbxDATE:
1349         case SbxSALINT64:
1350         case SbxSALUINT64:
1351             nLen = 8;
1352             break;
1353 
1354         case SbxOBJECT:
1355         case SbxVARIANT:
1356         case SbxDATAOBJECT:
1357             nLen = 0;
1358             break;
1359 
1360         case SbxCHAR:
1361         case SbxBYTE:
1362         case SbxBOOL:
1363             nLen = 1;
1364                 break;
1365 
1366         case SbxLPSTR:
1367         case SbxLPWSTR:
1368         case SbxCoreSTRING:
1369         case SbxSTRING:
1370             nLen = static_cast<sal_Int16>(rPar.Get(1)->GetOUString().getLength());
1371             break;
1372 
1373         default:
1374             nLen = 0;
1375             break;
1376         }
1377         rPar.Get(0)->PutInteger(nLen);
1378     }
1379 }
1380 
1381 
1382 // 1st parameter == class name, other parameters for initialisation
1383 void SbRtl_CreateUnoStruct(StarBASIC *, SbxArray & rPar, bool)
1384 {
1385     RTL_Impl_CreateUnoStruct( rPar );
1386 }
1387 
1388 
1389 // 1st parameter == service-name
1390 void SbRtl_CreateUnoService(StarBASIC *, SbxArray & rPar, bool)
1391 {
1392     RTL_Impl_CreateUnoService( rPar );
1393 }
1394 
1395 void SbRtl_CreateUnoServiceWithArguments(StarBASIC *, SbxArray & rPar, bool)
1396 {
1397     RTL_Impl_CreateUnoServiceWithArguments( rPar );
1398 }
1399 
1400 
1401 void SbRtl_CreateUnoValue(StarBASIC *, SbxArray & rPar, bool)
1402 {
1403     RTL_Impl_CreateUnoValue( rPar );
1404 }
1405 
1406 
1407 // no parameters
1408 void SbRtl_GetProcessServiceManager(StarBASIC *, SbxArray & rPar, bool)
1409 {
1410     RTL_Impl_GetProcessServiceManager( rPar );
1411 }
1412 
1413 
1414 // 1st parameter == Sequence<PropertyValue>
1415 void SbRtl_CreatePropertySet(StarBASIC *, SbxArray & rPar, bool)
1416 {
1417     RTL_Impl_CreatePropertySet( rPar );
1418 }
1419 
1420 
1421 // multiple interface-names as parameters
1422 void SbRtl_HasUnoInterfaces(StarBASIC *, SbxArray & rPar, bool)
1423 {
1424     RTL_Impl_HasInterfaces( rPar );
1425 }
1426 
1427 
1428 void SbRtl_IsUnoStruct(StarBASIC *, SbxArray & rPar, bool)
1429 {
1430     RTL_Impl_IsUnoStruct( rPar );
1431 }
1432 
1433 
1434 void SbRtl_EqualUnoObjects(StarBASIC *, SbxArray & rPar, bool)
1435 {
1436     RTL_Impl_EqualUnoObjects( rPar );
1437 }
1438 
1439 void SbRtl_CreateUnoDialog(StarBASIC *, SbxArray & rPar, bool)
1440 {
1441     RTL_Impl_CreateUnoDialog( rPar );
1442 }
1443 
1444 // Return the application standard lib as root scope
1445 void SbRtl_GlobalScope(StarBASIC * pBasic, SbxArray & rPar, bool)
1446 {
1447     SbxObject* p = pBasic;
1448     while( p->GetParent() )
1449     {
1450         p = p->GetParent();
1451     }
1452     SbxVariableRef refVar = rPar.Get(0);
1453     refVar->PutObject( p );
1454 }
1455 
1456 // Helper functions to convert Url from/to system paths
1457 void SbRtl_ConvertToUrl(StarBASIC *, SbxArray & rPar, bool)
1458 {
1459     if (rPar.Count() == 2)
1460     {
1461         OUString aStr = rPar.Get(1)->GetOUString();
1462         INetURLObject aURLObj( aStr, INetProtocol::File );
1463         OUString aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1464         if( aFileURL.isEmpty() )
1465         {
1466             osl::File::getFileURLFromSystemPath(aStr, aFileURL);
1467         }
1468         if( aFileURL.isEmpty() )
1469         {
1470             aFileURL = aStr;
1471         }
1472         rPar.Get(0)->PutString(aFileURL);
1473     }
1474     else
1475     {
1476         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1477     }
1478 }
1479 
1480 void SbRtl_ConvertFromUrl(StarBASIC *, SbxArray & rPar, bool)
1481 {
1482     if (rPar.Count() == 2)
1483     {
1484         OUString aStr = rPar.Get(1)->GetOUString();
1485         OUString aSysPath;
1486         ::osl::File::getSystemPathFromFileURL( aStr, aSysPath );
1487         if( aSysPath.isEmpty() )
1488         {
1489             aSysPath = aStr;
1490         }
1491         rPar.Get(0)->PutString(aSysPath);
1492     }
1493     else
1494     {
1495         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1496     }
1497 }
1498 
1499 
1500 // Provide DefaultContext
1501 void SbRtl_GetDefaultContext(StarBASIC *, SbxArray & rPar, bool)
1502 {
1503     RTL_Impl_GetDefaultContext( rPar );
1504 }
1505 
1506 void SbRtl_Join(StarBASIC *, SbxArray & rPar, bool)
1507 {
1508     sal_uInt32 nParCount = rPar.Count();
1509     if ( nParCount != 3 && nParCount != 2 )
1510     {
1511         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1512         return;
1513     }
1514     SbxBase* pParObj = rPar.Get(1)->GetObject();
1515     SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
1516     if( pArr )
1517     {
1518         if (pArr->GetDims() != 1)
1519         {
1520             StarBASIC::Error( ERRCODE_BASIC_WRONG_DIMS );   // Syntax Error?!
1521             return;
1522         }
1523         OUString aDelim;
1524         if( nParCount == 3 )
1525         {
1526             aDelim = rPar.Get(2)->GetOUString();
1527         }
1528         else
1529         {
1530             aDelim = " ";
1531         }
1532         OUStringBuffer aRetStr(32);
1533         sal_Int32 nLower, nUpper;
1534         pArr->GetDim(1, nLower, nUpper);
1535         sal_Int32 aIdx[1];
1536         for (aIdx[0] = nLower; aIdx[0] <= nUpper; ++aIdx[0])
1537         {
1538             OUString aStr = pArr->Get(aIdx)->GetOUString();
1539             aRetStr.append(aStr);
1540             if (aIdx[0] != nUpper)
1541             {
1542                 aRetStr.append(aDelim);
1543             }
1544         }
1545         rPar.Get(0)->PutString(aRetStr.makeStringAndClear());
1546     }
1547     else
1548     {
1549         StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
1550     }
1551 }
1552 
1553 
1554 void SbRtl_Split(StarBASIC *, SbxArray & rPar, bool)
1555 {
1556     sal_uInt32 nParCount = rPar.Count();
1557     if ( nParCount < 2 )
1558     {
1559         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1560         return;
1561     }
1562 
1563     OUString aExpression = rPar.Get(1)->GetOUString();
1564     sal_Int32 nArraySize = 0;
1565     std::vector< OUString > vRet;
1566     if( !aExpression.isEmpty() )
1567     {
1568         OUString aDelim;
1569         if( nParCount >= 3 )
1570         {
1571             aDelim = rPar.Get(2)->GetOUString();
1572         }
1573         else
1574         {
1575             aDelim = " ";
1576         }
1577 
1578         sal_Int32 nCount = -1;
1579         if( nParCount == 4 )
1580         {
1581             nCount = rPar.Get(3)->GetLong();
1582         }
1583         sal_Int32 nDelimLen = aDelim.getLength();
1584         if( nDelimLen )
1585         {
1586             sal_Int32 iSearch = -1;
1587             sal_Int32 iStart = 0;
1588             do
1589             {
1590                 bool bBreak = false;
1591                 if( nCount >= 0 && nArraySize == nCount - 1 )
1592                 {
1593                     bBreak = true;
1594                 }
1595                 iSearch = aExpression.indexOf( aDelim, iStart );
1596                 OUString aSubStr;
1597                 if( iSearch >= 0 && !bBreak )
1598                 {
1599                     aSubStr = aExpression.copy( iStart, iSearch - iStart );
1600                     iStart = iSearch + nDelimLen;
1601                 }
1602                 else
1603                 {
1604                     aSubStr = aExpression.copy( iStart );
1605                 }
1606                 vRet.push_back( aSubStr );
1607                 nArraySize++;
1608 
1609                 if( bBreak )
1610                 {
1611                     break;
1612                 }
1613             }
1614             while( iSearch >= 0 );
1615         }
1616         else
1617         {
1618             vRet.push_back( aExpression );
1619             nArraySize = 1;
1620         }
1621     }
1622 
1623     // tdf#123025 - split returns an array of substrings
1624     SbxDimArray* pArray = new SbxDimArray( SbxSTRING );
1625     pArray->unoAddDim(0, nArraySize - 1);
1626 
1627     // insert parameter(s) into the array
1628     const bool bIsVBAInterOp = SbiRuntime::isVBAEnabled();
1629     for(sal_Int32 i = 0 ; i < nArraySize ; i++ )
1630     {
1631         // tdf#123025 - split returns an array of substrings
1632         SbxVariableRef xVar = new SbxVariable( SbxSTRING );
1633         xVar->PutString( vRet[i] );
1634         // tdf#144924 - allow the assignment of different data types to the individual elements
1635         if (!bIsVBAInterOp)
1636         {
1637             xVar->ResetFlag(SbxFlagBits::Fixed);
1638         }
1639         pArray->Put(xVar.get(), &i);
1640     }
1641 
1642     // return array
1643     SbxVariableRef refVar = rPar.Get(0);
1644     SbxFlagBits nFlags = refVar->GetFlags();
1645     refVar->ResetFlag( SbxFlagBits::Fixed );
1646     refVar->PutObject( pArray );
1647     refVar->SetFlags( nFlags );
1648     refVar->SetParameters( nullptr );
1649 }
1650 
1651 // MonthName(month[, abbreviate])
1652 void SbRtl_MonthName(StarBASIC *, SbxArray & rPar, bool)
1653 {
1654     sal_uInt32 nParCount = rPar.Count();
1655     if( nParCount != 2 && nParCount != 3 )
1656     {
1657         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1658         return;
1659     }
1660 
1661     const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
1662     if( !xCalendar.is() )
1663     {
1664         StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
1665         return;
1666     }
1667     Sequence< CalendarItem2 > aMonthSeq = xCalendar->getMonths2();
1668     sal_Int32 nMonthCount = aMonthSeq.getLength();
1669 
1670     sal_Int16 nVal = rPar.Get(1)->GetInteger();
1671     if( nVal < 1 || nVal > nMonthCount )
1672     {
1673         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1674         return;
1675     }
1676 
1677     bool bAbbreviate = false;
1678     if( nParCount == 3 )
1679         bAbbreviate = rPar.Get(2)->GetBool();
1680 
1681     const CalendarItem2* pCalendarItems = aMonthSeq.getConstArray();
1682     const CalendarItem2& rItem = pCalendarItems[nVal - 1];
1683 
1684     OUString aRetStr = ( bAbbreviate ? rItem.AbbrevName : rItem.FullName );
1685     rPar.Get(0)->PutString(aRetStr);
1686 }
1687 
1688 // WeekdayName(weekday, abbreviate, firstdayofweek)
1689 void SbRtl_WeekdayName(StarBASIC *, SbxArray & rPar, bool)
1690 {
1691     sal_uInt32 nParCount = rPar.Count();
1692     if( nParCount < 2 || nParCount > 4 )
1693     {
1694         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1695         return;
1696     }
1697 
1698     const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
1699     if( !xCalendar.is() )
1700     {
1701         StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
1702         return;
1703     }
1704 
1705     Sequence< CalendarItem2 > aDaySeq = xCalendar->getDays2();
1706     sal_Int16 nDayCount = static_cast<sal_Int16>(aDaySeq.getLength());
1707     sal_Int16 nDay = rPar.Get(1)->GetInteger();
1708     sal_Int16 nFirstDay = 0;
1709     if( nParCount == 4 )
1710     {
1711         nFirstDay = rPar.Get(3)->GetInteger();
1712         if( nFirstDay < 0 || nFirstDay > 7 )
1713         {
1714             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1715             return;
1716         }
1717     }
1718     if( nFirstDay == 0 )
1719     {
1720         nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 );
1721     }
1722     nDay = 1 + (nDay + nDayCount + nFirstDay - 2) % nDayCount;
1723     if( nDay < 1 || nDay > nDayCount )
1724     {
1725         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1726         return;
1727     }
1728 
1729     bool bAbbreviate = false;
1730     if( nParCount >= 3 )
1731     {
1732         SbxVariable* pPar2 = rPar.Get(2);
1733         if( !pPar2->IsErr() )
1734         {
1735             bAbbreviate = pPar2->GetBool();
1736         }
1737     }
1738 
1739     const CalendarItem2* pCalendarItems = aDaySeq.getConstArray();
1740     const CalendarItem2& rItem = pCalendarItems[nDay - 1];
1741 
1742     OUString aRetStr = ( bAbbreviate ? rItem.AbbrevName : rItem.FullName );
1743     rPar.Get(0)->PutString(aRetStr);
1744 }
1745 
1746 void SbRtl_Weekday(StarBASIC *, SbxArray & rPar, bool)
1747 {
1748     sal_uInt32 nParCount = rPar.Count();
1749     if ( nParCount < 2 )
1750     {
1751         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1752     }
1753     else
1754     {
1755         double aDate = rPar.Get(1)->GetDate();
1756 
1757         bool bFirstDay = false;
1758         sal_Int16 nFirstDay = 0;
1759         if ( nParCount > 2 )
1760         {
1761             nFirstDay = rPar.Get(2)->GetInteger();
1762             bFirstDay = true;
1763         }
1764         sal_Int16 nDay = implGetWeekDay( aDate, bFirstDay, nFirstDay );
1765         rPar.Get(0)->PutInteger(nDay);
1766     }
1767 }
1768 
1769 namespace {
1770 
1771 enum Interval
1772 {
1773     INTERVAL_YYYY,
1774     INTERVAL_Q,
1775     INTERVAL_M,
1776     INTERVAL_Y,
1777     INTERVAL_D,
1778     INTERVAL_W,
1779     INTERVAL_WW,
1780     INTERVAL_H,
1781     INTERVAL_N,
1782     INTERVAL_S
1783 };
1784 
1785 struct IntervalInfo
1786 {
1787     Interval    meInterval;
1788     char const * mStringCode;
1789     double      mdValue;
1790     bool        mbSimple;
1791 };
1792 
1793 }
1794 
1795 static IntervalInfo const * getIntervalInfo( const OUString& rStringCode )
1796 {
1797     static IntervalInfo const aIntervalTable[] =
1798     {
1799         { INTERVAL_YYYY, "yyyy", 0.0,           false }, // Year
1800         { INTERVAL_Q,    "q",    0.0,           false }, // Quarter
1801         { INTERVAL_M,    "m",    0.0,           false }, // Month
1802         { INTERVAL_Y,    "y",    1.0,           true  }, // Day of year
1803         { INTERVAL_D,    "d",    1.0,           true  }, // Day
1804         { INTERVAL_W,    "w",    1.0,           true  }, // Weekday
1805         { INTERVAL_WW,   "ww",   7.0,           true  }, // Week
1806         { INTERVAL_H,    "h",    1.0 /    24.0, true  }, // Hour
1807         { INTERVAL_N,    "n",    1.0 /  1440.0, true  }, // Minute
1808         { INTERVAL_S,    "s",    1.0 / 86400.0, true  }  // Second
1809     };
1810     auto const pred = [&rStringCode](const IntervalInfo &aInterval) {
1811             return rStringCode.equalsIgnoreAsciiCaseAscii(aInterval.mStringCode);
1812     };
1813 
1814     auto intervalIter = std::find_if(std::begin(aIntervalTable), std::end(aIntervalTable), pred);
1815     if(intervalIter != std::end(aIntervalTable)) {
1816             return intervalIter;
1817     }
1818     return nullptr;
1819 }
1820 
1821 static void implGetDayMonthYear( sal_Int16& rnYear, sal_Int16& rnMonth, sal_Int16& rnDay, double dDate )
1822 {
1823     rnDay   = implGetDateDay( dDate );
1824     rnMonth = implGetDateMonth( dDate );
1825     rnYear  = implGetDateYear( dDate );
1826 }
1827 
1828 /** Limits a date to valid dates within tools' class Date capabilities.
1829 
1830     @return the year number, truncated if necessary and in that case also
1831             rMonth and rDay adjusted.
1832  */
1833 static sal_Int16 limitDate( sal_Int32 n32Year, sal_Int16& rMonth, sal_Int16& rDay )
1834 {
1835     if( n32Year > SAL_MAX_INT16 )
1836     {
1837         n32Year = SAL_MAX_INT16;
1838         rMonth = 12;
1839         rDay = 31;
1840     }
1841     else if( n32Year < SAL_MIN_INT16 )
1842     {
1843         n32Year = SAL_MIN_INT16;
1844         rMonth = 1;
1845         rDay = 1;
1846     }
1847     return static_cast<sal_Int16>(n32Year);
1848 }
1849 
1850 void SbRtl_DateAdd(StarBASIC *, SbxArray & rPar, bool)
1851 {
1852     sal_uInt32 nParCount = rPar.Count();
1853     if( nParCount != 4 )
1854     {
1855         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1856         return;
1857     }
1858 
1859     OUString aStringCode = rPar.Get(1)->GetOUString();
1860     IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
1861     if( !pInfo )
1862     {
1863         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1864         return;
1865     }
1866 
1867     sal_Int32 lNumber = rPar.Get(2)->GetLong();
1868     double dDate = rPar.Get(3)->GetDate();
1869     double dNewDate = 0;
1870     if( pInfo->mbSimple )
1871     {
1872         double dAdd = pInfo->mdValue * lNumber;
1873         dNewDate = dDate + dAdd;
1874     }
1875     else
1876     {
1877         // Keep hours, minutes, seconds
1878         double dHoursMinutesSeconds = dDate - floor( dDate );
1879 
1880         bool bOk = true;
1881         sal_Int16 nYear, nMonth, nDay;
1882         sal_Int16 nTargetYear16 = 0, nTargetMonth = 0;
1883         implGetDayMonthYear( nYear, nMonth, nDay, dDate );
1884         switch( pInfo->meInterval )
1885         {
1886             case INTERVAL_YYYY:
1887             {
1888                 sal_Int32 nTargetYear = lNumber + nYear;
1889                 nTargetYear16 = limitDate( nTargetYear, nMonth, nDay );
1890                 /* TODO: should the result be error if the date was limited? It never was. */
1891                 nTargetMonth = nMonth;
1892                 bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, SbDateCorrection::TruncateToMonth, dNewDate );
1893                 break;
1894             }
1895             case INTERVAL_Q:
1896             case INTERVAL_M:
1897             {
1898                 bool bNeg = (lNumber < 0);
1899                 if( bNeg )
1900                     lNumber = -lNumber;
1901                 sal_Int32 nYearsAdd;
1902                 sal_Int16 nMonthAdd;
1903                 if( pInfo->meInterval == INTERVAL_Q )
1904                 {
1905                     nYearsAdd = lNumber / 4;
1906                     nMonthAdd = static_cast<sal_Int16>( 3 * (lNumber % 4) );
1907                 }
1908                 else
1909                 {
1910                     nYearsAdd = lNumber / 12;
1911                     nMonthAdd = static_cast<sal_Int16>( lNumber % 12 );
1912                 }
1913 
1914                 sal_Int32 nTargetYear;
1915                 if( bNeg )
1916                 {
1917                     nTargetMonth = nMonth - nMonthAdd;
1918                     if( nTargetMonth <= 0 )
1919                     {
1920                         nTargetMonth += 12;
1921                         nYearsAdd++;
1922                     }
1923                     nTargetYear = static_cast<sal_Int32>(nYear) - nYearsAdd;
1924                 }
1925                 else
1926                 {
1927                     nTargetMonth = nMonth + nMonthAdd;
1928                     if( nTargetMonth > 12 )
1929                     {
1930                         nTargetMonth -= 12;
1931                         nYearsAdd++;
1932                     }
1933                     nTargetYear = static_cast<sal_Int32>(nYear) + nYearsAdd;
1934                 }
1935                 nTargetYear16 = limitDate( nTargetYear, nTargetMonth, nDay );
1936                 /* TODO: should the result be error if the date was limited? It never was. */
1937                 bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, SbDateCorrection::TruncateToMonth, dNewDate );
1938                 break;
1939             }
1940             default: break;
1941         }
1942 
1943         if( bOk )
1944             dNewDate += dHoursMinutesSeconds;
1945     }
1946 
1947     rPar.Get(0)->PutDate(dNewDate);
1948 }
1949 
1950 static double RoundImpl( double d )
1951 {
1952     return ( d >= 0 ) ? floor( d + 0.5 ) : -floor( -d + 0.5 );
1953 }
1954 
1955 void SbRtl_DateDiff(StarBASIC *, SbxArray & rPar, bool)
1956 {
1957     // DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]])
1958 
1959     sal_uInt32 nParCount = rPar.Count();
1960     if( nParCount < 4 || nParCount > 6 )
1961     {
1962         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1963         return;
1964     }
1965 
1966     OUString aStringCode = rPar.Get(1)->GetOUString();
1967     IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
1968     if( !pInfo )
1969     {
1970         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1971         return;
1972     }
1973 
1974     double dDate1 = rPar.Get(2)->GetDate();
1975     double dDate2 = rPar.Get(3)->GetDate();
1976 
1977     double dRet = 0.0;
1978     switch( pInfo->meInterval )
1979     {
1980         case INTERVAL_YYYY:
1981         {
1982             sal_Int16 nYear1 = implGetDateYear( dDate1 );
1983             sal_Int16 nYear2 = implGetDateYear( dDate2 );
1984             dRet = nYear2 - nYear1;
1985             break;
1986         }
1987         case INTERVAL_Q:
1988         {
1989             sal_Int16 nYear1 = implGetDateYear( dDate1 );
1990             sal_Int16 nYear2 = implGetDateYear( dDate2 );
1991             sal_Int16 nQ1 = 1 + (implGetDateMonth( dDate1 ) - 1) / 3;
1992             sal_Int16 nQ2 = 1 + (implGetDateMonth( dDate2 ) - 1) / 3;
1993             sal_Int16 nQGes1 = 4 * nYear1 + nQ1;
1994             sal_Int16 nQGes2 = 4 * nYear2 + nQ2;
1995             dRet = nQGes2 - nQGes1;
1996             break;
1997         }
1998         case INTERVAL_M:
1999         {
2000             sal_Int16 nYear1 = implGetDateYear( dDate1 );
2001             sal_Int16 nYear2 = implGetDateYear( dDate2 );
2002             sal_Int16 nMonth1 = implGetDateMonth( dDate1 );
2003             sal_Int16 nMonth2 = implGetDateMonth( dDate2 );
2004             sal_Int16 nMonthGes1 = 12 * nYear1 + nMonth1;
2005             sal_Int16 nMonthGes2 = 12 * nYear2 + nMonth2;
2006             dRet = nMonthGes2 - nMonthGes1;
2007             break;
2008         }
2009         case INTERVAL_Y:
2010         case INTERVAL_D:
2011         {
2012             double dDays1 = floor( dDate1 );
2013             double dDays2 = floor( dDate2 );
2014             dRet = dDays2 - dDays1;
2015             break;
2016         }
2017         case INTERVAL_W:
2018         case INTERVAL_WW:
2019         {
2020             double dDays1 = floor( dDate1 );
2021             double dDays2 = floor( dDate2 );
2022             if( pInfo->meInterval == INTERVAL_WW )
2023             {
2024                 sal_Int16 nFirstDay = 1;    // Default
2025                 if( nParCount >= 5 )
2026                 {
2027                     nFirstDay = rPar.Get(4)->GetInteger();
2028                     if( nFirstDay < 0 || nFirstDay > 7 )
2029                     {
2030                         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2031                         return;
2032                     }
2033                     if( nFirstDay == 0 )
2034                     {
2035                         const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
2036                         if( !xCalendar.is() )
2037                         {
2038                             StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
2039                             return;
2040                         }
2041                         nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 );
2042                     }
2043                 }
2044                 sal_Int16 nDay1 = implGetWeekDay( dDate1 );
2045                 sal_Int16 nDay1_Diff = nDay1 - nFirstDay;
2046                 if( nDay1_Diff < 0 )
2047                     nDay1_Diff += 7;
2048                 dDays1 -= nDay1_Diff;
2049 
2050                 sal_Int16 nDay2 = implGetWeekDay( dDate2 );
2051                 sal_Int16 nDay2_Diff = nDay2 - nFirstDay;
2052                 if( nDay2_Diff < 0 )
2053                     nDay2_Diff += 7;
2054                 dDays2 -= nDay2_Diff;
2055             }
2056 
2057             double dDiff = dDays2 - dDays1;
2058             dRet = ( dDiff >= 0 ) ? floor( dDiff / 7.0 ) : -floor( -dDiff / 7.0 );
2059             break;
2060         }
2061         case INTERVAL_H:
2062         {
2063             dRet = RoundImpl( 24.0 * (dDate2 - dDate1) );
2064             break;
2065         }
2066         case INTERVAL_N:
2067         {
2068             dRet = RoundImpl( 1440.0 * (dDate2 - dDate1) );
2069             break;
2070         }
2071         case INTERVAL_S:
2072         {
2073             dRet = RoundImpl( 86400.0 * (dDate2 - dDate1) );
2074             break;
2075         }
2076     }
2077     rPar.Get(0)->PutDouble(dRet);
2078 }
2079 
2080 static double implGetDateOfFirstDayInFirstWeek
2081     ( sal_Int16 nYear, sal_Int16& nFirstDay, sal_Int16& nFirstWeek, bool* pbError = nullptr )
2082 {
2083     ErrCode nError = ERRCODE_NONE;
2084     if( nFirstDay < 0 || nFirstDay > 7 )
2085         nError = ERRCODE_BASIC_BAD_ARGUMENT;
2086 
2087     if( nFirstWeek < 0 || nFirstWeek > 3 )
2088         nError = ERRCODE_BASIC_BAD_ARGUMENT;
2089 
2090     Reference< XCalendar4 > xCalendar;
2091     if( nFirstDay == 0 || nFirstWeek == 0 )
2092     {
2093         xCalendar = getLocaleCalendar();
2094         if( !xCalendar.is() )
2095             nError = ERRCODE_BASIC_BAD_ARGUMENT;
2096     }
2097 
2098     if( nError != ERRCODE_NONE )
2099     {
2100         StarBASIC::Error( nError );
2101         if( pbError )
2102             *pbError = true;
2103         return 0.0;
2104     }
2105 
2106     if( nFirstDay == 0 )
2107         nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 );
2108 
2109     sal_Int16 nFirstWeekMinDays = 0;    // Not used for vbFirstJan1 = default
2110     if( nFirstWeek == 0 )
2111     {
2112         nFirstWeekMinDays = xCalendar->getMinimumNumberOfDaysForFirstWeek();
2113         if( nFirstWeekMinDays == 1 )
2114         {
2115             nFirstWeekMinDays = 0;
2116             nFirstWeek = 1;
2117         }
2118         else if( nFirstWeekMinDays == 4 )
2119             nFirstWeek = 2;
2120         else if( nFirstWeekMinDays == 7 )
2121             nFirstWeek = 3;
2122     }
2123     else if( nFirstWeek == 2 )
2124         nFirstWeekMinDays = 4;      // vbFirstFourDays
2125     else if( nFirstWeek == 3 )
2126         nFirstWeekMinDays = 7;      // vbFirstFourDays
2127 
2128     double dBaseDate;
2129     implDateSerial( nYear, 1, 1, false, SbDateCorrection::None, dBaseDate );
2130 
2131     sal_Int16 nWeekDay0101 = implGetWeekDay( dBaseDate );
2132     sal_Int16 nDayDiff = nWeekDay0101 - nFirstDay;
2133     if( nDayDiff < 0 )
2134         nDayDiff += 7;
2135 
2136     if( nFirstWeekMinDays )
2137     {
2138         sal_Int16 nThisWeeksDaysInYearCount = 7 - nDayDiff;
2139         if( nThisWeeksDaysInYearCount < nFirstWeekMinDays )
2140             nDayDiff -= 7;
2141     }
2142     double dRetDate = dBaseDate - nDayDiff;
2143     return dRetDate;
2144 }
2145 
2146 void SbRtl_DatePart(StarBASIC *, SbxArray & rPar, bool)
2147 {
2148     // DatePart(interval, date[,firstdayofweek[, firstweekofyear]])
2149 
2150     sal_uInt32 nParCount = rPar.Count();
2151     if( nParCount < 3 || nParCount > 5 )
2152     {
2153         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2154         return;
2155     }
2156 
2157     OUString aStringCode = rPar.Get(1)->GetOUString();
2158     IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
2159     if( !pInfo )
2160     {
2161         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2162         return;
2163     }
2164 
2165     double dDate = rPar.Get(2)->GetDate();
2166 
2167     sal_Int32 nRet = 0;
2168     switch( pInfo->meInterval )
2169     {
2170         case INTERVAL_YYYY:
2171         {
2172             nRet = implGetDateYear( dDate );
2173             break;
2174         }
2175         case INTERVAL_Q:
2176         {
2177             nRet = 1 + (implGetDateMonth( dDate ) - 1) / 3;
2178             break;
2179         }
2180         case INTERVAL_M:
2181         {
2182             nRet = implGetDateMonth( dDate );
2183             break;
2184         }
2185         case INTERVAL_Y:
2186         {
2187             sal_Int16 nYear = implGetDateYear( dDate );
2188             double dBaseDate;
2189             implDateSerial( nYear, 1, 1, false, SbDateCorrection::None, dBaseDate );
2190             nRet = 1 + sal_Int32( dDate - dBaseDate );
2191             break;
2192         }
2193         case INTERVAL_D:
2194         {
2195             nRet = implGetDateDay( dDate );
2196             break;
2197         }
2198         case INTERVAL_W:
2199         {
2200             bool bFirstDay = false;
2201             sal_Int16 nFirstDay = 1;    // Default
2202             if( nParCount >= 4 )
2203             {
2204                 nFirstDay = rPar.Get(3)->GetInteger();
2205                 bFirstDay = true;
2206             }
2207             nRet = implGetWeekDay( dDate, bFirstDay, nFirstDay );
2208             break;
2209         }
2210         case INTERVAL_WW:
2211         {
2212             sal_Int16 nFirstDay = 1;    // Default
2213             if( nParCount >= 4 )
2214                 nFirstDay = rPar.Get(3)->GetInteger();
2215 
2216             sal_Int16 nFirstWeek = 1;   // Default
2217             if( nParCount == 5 )
2218                 nFirstWeek = rPar.Get(4)->GetInteger();
2219 
2220             sal_Int16 nYear = implGetDateYear( dDate );
2221             bool bError = false;
2222             double dYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear, nFirstDay, nFirstWeek, &bError );
2223             if( !bError )
2224             {
2225                 if( dYearFirstDay > dDate )
2226                 {
2227                     // Date belongs to last year's week
2228                     dYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear - 1, nFirstDay, nFirstWeek );
2229                 }
2230                 else if( nFirstWeek != 1 )
2231                 {
2232                     // Check if date belongs to next year
2233                     double dNextYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear + 1, nFirstDay, nFirstWeek );
2234                     if( dDate >= dNextYearFirstDay )
2235                         dYearFirstDay = dNextYearFirstDay;
2236                 }
2237 
2238                 // Calculate week
2239                 double dDiff = dDate - dYearFirstDay;
2240                 nRet = 1 + sal_Int32( dDiff / 7 );
2241             }
2242             break;
2243         }
2244         case INTERVAL_H:
2245         {
2246             nRet = implGetHour( dDate );
2247             break;
2248         }
2249         case INTERVAL_N:
2250         {
2251             nRet = implGetMinute( dDate );
2252             break;
2253         }
2254         case INTERVAL_S:
2255         {
2256             nRet = implGetSecond( dDate );
2257             break;
2258         }
2259     }
2260     rPar.Get(0)->PutLong(nRet);
2261 }
2262 
2263 // FormatDateTime(Date[,NamedFormat])
2264 void SbRtl_FormatDateTime(StarBASIC *, SbxArray & rPar, bool)
2265 {
2266     sal_uInt32 nParCount = rPar.Count();
2267     if( nParCount < 2 || nParCount > 3 )
2268     {
2269         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2270         return;
2271     }
2272 
2273     double dDate = rPar.Get(1)->GetDate();
2274     sal_Int16 nNamedFormat = 0;
2275     if( nParCount > 2 )
2276     {
2277         nNamedFormat = rPar.Get(2)->GetInteger();
2278         if( nNamedFormat < 0 || nNamedFormat > 4 )
2279         {
2280             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2281             return;
2282         }
2283     }
2284 
2285     const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
2286     if( !xCalendar.is() )
2287     {
2288         StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
2289         return;
2290     }
2291 
2292     OUString aRetStr;
2293     SbxVariableRef pSbxVar = new SbxVariable( SbxSTRING );
2294     switch( nNamedFormat )
2295     {
2296         // GeneralDate:
2297         // Display a date and/or time. If there is a date part,
2298         // display it as a short date. If there is a time part,
2299         // display it as a long time. If present, both parts are displayed.
2300 
2301         // 12/21/2004 11:24:50 AM
2302         // 21.12.2004 12:13:51
2303     case 0:
2304         pSbxVar->PutDate( dDate );
2305         aRetStr = pSbxVar->GetOUString();
2306         break;
2307 
2308         // LongDate: Display a date using the long date format specified
2309         // in your computer's regional settings.
2310         // Tuesday, December 21, 2004
2311         // Dienstag, 21. December 2004
2312     case 1:
2313         {
2314             std::shared_ptr<SvNumberFormatter> pFormatter;
2315             if( GetSbData()->pInst )
2316             {
2317                 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2318             }
2319             else
2320             {
2321                 sal_uInt32 n;   // Dummy
2322                 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
2323             }
2324 
2325             LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
2326             const sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_LONG, eLangType );
2327             const Color* pCol;
2328             pFormatter->GetOutputString( dDate, nIndex, aRetStr, &pCol );
2329             break;
2330         }
2331 
2332         // ShortDate: Display a date using the short date format specified
2333         // in your computer's regional settings.
2334         // 21.12.2004
2335     case 2:
2336         pSbxVar->PutDate( floor(dDate) );
2337         aRetStr = pSbxVar->GetOUString();
2338         break;
2339 
2340         // LongTime: Display a time using the time format specified
2341         // in your computer's regional settings.
2342         // 11:24:50 AM
2343         // 12:13:51
2344     case 3:
2345         // ShortTime: Display a time using the 24-hour format (hh:mm).
2346         // 11:24
2347     case 4:
2348         double dTime = modf( dDate, &o3tl::temporary(double()) );
2349         pSbxVar->PutDate( dTime );
2350         if( nNamedFormat == 3 )
2351         {
2352             aRetStr = pSbxVar->GetOUString();
2353         }
2354         else
2355         {
2356             aRetStr = pSbxVar->GetOUString().copy( 0, 5 );
2357         }
2358         break;
2359     }
2360 
2361     rPar.Get(0)->PutString(aRetStr);
2362 }
2363 
2364 void SbRtl_Frac(StarBASIC *, SbxArray & rPar, bool)
2365 {
2366     sal_uInt32 nParCount = rPar.Count();
2367     if( nParCount != 2)
2368     {
2369         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2370         return;
2371     }
2372 
2373     SbxVariable* pSbxVariable = rPar.Get(1);
2374     double dVal = pSbxVariable->GetDouble();
2375     if(dVal >= 0)
2376         rPar.Get(0)->PutDouble(dVal - ::rtl::math::approxFloor(dVal));
2377     else
2378         rPar.Get(0)->PutDouble(dVal - ::rtl::math::approxCeil(dVal));
2379 }
2380 
2381 void SbRtl_Round(StarBASIC *, SbxArray & rPar, bool)
2382 {
2383     sal_uInt32 nParCount = rPar.Count();
2384     if( nParCount != 2 && nParCount != 3 )
2385     {
2386         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2387         return;
2388     }
2389 
2390     SbxVariable* pSbxVariable = rPar.Get(1);
2391     double dVal = pSbxVariable->GetDouble();
2392     double dRes = 0.0;
2393     if( dVal != 0.0 )
2394     {
2395         sal_Int16 numdecimalplaces = 0;
2396         if( nParCount == 3 )
2397         {
2398             numdecimalplaces = rPar.Get(2)->GetInteger();
2399             if( numdecimalplaces < 0 || numdecimalplaces > 22 )
2400             {
2401                 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2402                 return;
2403             }
2404         }
2405 
2406         dRes = rtl_math_round(dVal, numdecimalplaces, rtl_math_RoundingMode_HalfEven);
2407     }
2408     rPar.Get(0)->PutDouble(dRes);
2409 }
2410 
2411 static void CallFunctionAccessFunction( const Sequence< Any >& aArgs, const OUString& sFuncName, SbxVariable* pRet )
2412 {
2413     static Reference< XFunctionAccess > xFunc;
2414     try
2415     {
2416         if ( !xFunc.is() )
2417         {
2418             Reference< XMultiServiceFactory > xFactory( getProcessServiceFactory() );
2419             if( xFactory.is() )
2420             {
2421                 xFunc.set( xFactory->createInstance("com.sun.star.sheet.FunctionAccess"), UNO_QUERY_THROW);
2422             }
2423         }
2424         Any aRet = xFunc->callFunction( sFuncName, aArgs );
2425 
2426         unoToSbxValue( pRet, aRet );
2427 
2428     }
2429     catch(const Exception& )
2430     {
2431         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2432     }
2433 }
2434 
2435 void SbRtl_SYD(StarBASIC *, SbxArray & rPar, bool)
2436 {
2437     sal_uInt32 nArgCount = rPar.Count() - 1;
2438 
2439     if ( nArgCount < 4 )
2440     {
2441         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2442         return;
2443     }
2444 
2445     // retrieve non-optional params
2446 
2447     Sequence< Any > aParams
2448     {
2449         Any(rPar.Get(1)->GetDouble()),
2450         Any(rPar.Get(2)->GetDouble()),
2451         Any(rPar.Get(3)->GetDouble()),
2452         Any(rPar.Get(4)->GetDouble())
2453     };
2454 
2455     CallFunctionAccessFunction(aParams, "SYD", rPar.Get(0));
2456 }
2457 
2458 void SbRtl_SLN(StarBASIC *, SbxArray & rPar, bool)
2459 {
2460     sal_uInt32 nArgCount = rPar.Count() - 1;
2461 
2462     if ( nArgCount < 3 )
2463     {
2464         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2465         return;
2466     }
2467 
2468     // retrieve non-optional params
2469 
2470     Sequence< Any > aParams
2471     {
2472         Any(rPar.Get(1)->GetDouble()),
2473         Any(rPar.Get(2)->GetDouble()),
2474         Any(rPar.Get(3)->GetDouble())
2475     };
2476 
2477     CallFunctionAccessFunction(aParams, "SLN", rPar.Get(0));
2478 }
2479 
2480 void SbRtl_Pmt(StarBASIC *, SbxArray & rPar, bool)
2481 {
2482     sal_uInt32 nArgCount = rPar.Count() - 1;
2483 
2484     if ( nArgCount < 3 || nArgCount > 5 )
2485     {
2486         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2487         return;
2488     }
2489     // retrieve non-optional params
2490 
2491     double rate = rPar.Get(1)->GetDouble();
2492     double nper = rPar.Get(2)->GetDouble();
2493     double pmt = rPar.Get(3)->GetDouble();
2494 
2495     // set default values for Optional args
2496     double fv = 0;
2497     double type = 0;
2498 
2499     // fv
2500     if ( nArgCount >= 4 )
2501     {
2502         if (rPar.Get(4)->GetType() != SbxEMPTY)
2503             fv = rPar.Get(4)->GetDouble();
2504     }
2505     // type
2506     if ( nArgCount >= 5 )
2507     {
2508         if (rPar.Get(5)->GetType() != SbxEMPTY)
2509             type = rPar.Get(5)->GetDouble();
2510     }
2511 
2512     Sequence< Any > aParams
2513     {
2514         Any(rate),
2515         Any(nper),
2516         Any(pmt),
2517         Any(fv),
2518         Any(type)
2519     };
2520 
2521     CallFunctionAccessFunction(aParams, "Pmt", rPar.Get(0));
2522 }
2523 
2524 void SbRtl_PPmt(StarBASIC *, SbxArray & rPar, bool)
2525 {
2526     sal_uInt32 nArgCount = rPar.Count() - 1;
2527 
2528     if ( nArgCount < 4 || nArgCount > 6 )
2529     {
2530         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2531         return;
2532     }
2533     // retrieve non-optional params
2534 
2535     double rate = rPar.Get(1)->GetDouble();
2536     double per = rPar.Get(2)->GetDouble();
2537     double nper = rPar.Get(3)->GetDouble();
2538     double pv = rPar.Get(4)->GetDouble();
2539 
2540     // set default values for Optional args
2541     double fv = 0;
2542     double type = 0;
2543 
2544     // fv
2545     if ( nArgCount >= 5 )
2546     {
2547         if (rPar.Get(5)->GetType() != SbxEMPTY)
2548             fv = rPar.Get(5)->GetDouble();
2549     }
2550     // type
2551     if ( nArgCount >= 6 )
2552     {
2553         if (rPar.Get(6)->GetType() != SbxEMPTY)
2554             type = rPar.Get(6)->GetDouble();
2555     }
2556 
2557     Sequence< Any > aParams
2558     {
2559         Any(rate),
2560         Any(per),
2561         Any(nper),
2562         Any(pv),
2563         Any(fv),
2564         Any(type)
2565     };
2566 
2567     CallFunctionAccessFunction(aParams, "PPmt", rPar.Get(0));
2568 }
2569 
2570 void SbRtl_PV(StarBASIC *, SbxArray & rPar, bool)
2571 {
2572     sal_uInt32 nArgCount = rPar.Count() - 1;
2573 
2574     if ( nArgCount < 3 || nArgCount > 5 )
2575     {
2576         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2577         return;
2578     }
2579     // retrieve non-optional params
2580 
2581     double rate = rPar.Get(1)->GetDouble();
2582     double nper = rPar.Get(2)->GetDouble();
2583     double pmt = rPar.Get(3)->GetDouble();
2584 
2585     // set default values for Optional args
2586     double fv = 0;
2587     double type = 0;
2588 
2589     // fv
2590     if ( nArgCount >= 4 )
2591     {
2592         if (rPar.Get(4)->GetType() != SbxEMPTY)
2593             fv = rPar.Get(4)->GetDouble();
2594     }
2595     // type
2596     if ( nArgCount >= 5 )
2597     {
2598         if (rPar.Get(5)->GetType() != SbxEMPTY)
2599             type = rPar.Get(5)->GetDouble();
2600     }
2601 
2602     Sequence< Any > aParams
2603     {
2604         Any(rate),
2605         Any(nper),
2606         Any(pmt),
2607         Any(fv),
2608         Any(type)
2609     };
2610 
2611     CallFunctionAccessFunction(aParams, "PV", rPar.Get(0));
2612 }
2613 
2614 void SbRtl_NPV(StarBASIC *, SbxArray & rPar, bool)
2615 {
2616     sal_uInt32 nArgCount = rPar.Count() - 1;
2617 
2618     if ( nArgCount < 1 || nArgCount > 2 )
2619     {
2620         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2621         return;
2622     }
2623 
2624     Any aValues = sbxToUnoValue(rPar.Get(2),
2625                 cppu::UnoType<Sequence<double>>::get() );
2626 
2627     // convert for calc functions
2628     Sequence< Sequence< double > > sValues(1);
2629     aValues >>= sValues.getArray()[ 0 ];
2630     aValues <<= sValues;
2631 
2632     Sequence< Any > aParams
2633     {
2634         Any(rPar.Get(1)->GetDouble()),
2635         aValues
2636     };
2637 
2638     CallFunctionAccessFunction(aParams, "NPV", rPar.Get(0));
2639 }
2640 
2641 void SbRtl_NPer(StarBASIC *, SbxArray & rPar, bool)
2642 {
2643     sal_uInt32 nArgCount = rPar.Count() - 1;
2644 
2645     if ( nArgCount < 3 || nArgCount > 5 )
2646     {
2647         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2648         return;
2649     }
2650     // retrieve non-optional params
2651 
2652     double rate = rPar.Get(1)->GetDouble();
2653     double pmt = rPar.Get(2)->GetDouble();
2654     double pv = rPar.Get(3)->GetDouble();
2655 
2656     // set default values for Optional args
2657     double fv = 0;
2658     double type = 0;
2659 
2660     // fv
2661     if ( nArgCount >= 4 )
2662     {
2663         if (rPar.Get(4)->GetType() != SbxEMPTY)
2664             fv = rPar.Get(4)->GetDouble();
2665     }
2666     // type
2667     if ( nArgCount >= 5 )
2668     {
2669         if (rPar.Get(5)->GetType() != SbxEMPTY)
2670             type = rPar.Get(5)->GetDouble();
2671     }
2672 
2673     Sequence< Any > aParams
2674     {
2675         Any(rate),
2676         Any(pmt),
2677         Any(pv),
2678         Any(fv),
2679         Any(type)
2680     };
2681 
2682     CallFunctionAccessFunction(aParams, "NPer", rPar.Get(0));
2683 }
2684 
2685 void SbRtl_MIRR(StarBASIC *, SbxArray & rPar, bool)
2686 {
2687     sal_uInt32 nArgCount = rPar.Count() - 1;
2688 
2689     if ( nArgCount < 3 )
2690     {
2691         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2692         return;
2693     }
2694 
2695     // retrieve non-optional params
2696 
2697     Any aValues = sbxToUnoValue(rPar.Get(1),
2698                 cppu::UnoType<Sequence<double>>::get() );
2699 
2700     // convert for calc functions
2701     Sequence< Sequence< double > > sValues(1);
2702     aValues >>= sValues.getArray()[ 0 ];
2703     aValues <<= sValues;
2704 
2705     Sequence< Any > aParams
2706     {
2707         aValues,
2708         Any(rPar.Get(2)->GetDouble()),
2709         Any(rPar.Get(3)->GetDouble())
2710     };
2711 
2712     CallFunctionAccessFunction(aParams, "MIRR", rPar.Get(0));
2713 }
2714 
2715 void SbRtl_IRR(StarBASIC *, SbxArray & rPar, bool)
2716 {
2717     sal_uInt32 nArgCount = rPar.Count() - 1;
2718 
2719     if ( nArgCount < 1 || nArgCount > 2 )
2720     {
2721         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2722         return;
2723     }
2724     // retrieve non-optional params
2725     Any aValues = sbxToUnoValue(rPar.Get(1),
2726                 cppu::UnoType<Sequence<double>>::get() );
2727 
2728     // convert for calc functions
2729     Sequence< Sequence< double > > sValues(1);
2730     aValues >>= sValues.getArray()[ 0 ];
2731     aValues <<= sValues;
2732 
2733     // set default values for Optional args
2734     double guess = 0.1;
2735     // guess
2736     if ( nArgCount >= 2 )
2737     {
2738         if (rPar.Get(2)->GetType() != SbxEMPTY)
2739             guess = rPar.Get(2)->GetDouble();
2740     }
2741 
2742     Sequence< Any > aParams
2743     {
2744         aValues,
2745         Any(guess)
2746     };
2747 
2748     CallFunctionAccessFunction(aParams, "IRR", rPar.Get(0));
2749 }
2750 
2751 void SbRtl_IPmt(StarBASIC *, SbxArray & rPar, bool)
2752 {
2753     sal_uInt32 nArgCount = rPar.Count() - 1;
2754 
2755     if ( nArgCount < 4 || nArgCount > 6 )
2756     {
2757         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2758         return;
2759     }
2760     // retrieve non-optional params
2761 
2762     double rate = rPar.Get(1)->GetDouble();
2763     double per = rPar.Get(2)->GetInteger();
2764     double nper = rPar.Get(3)->GetDouble();
2765     double pv = rPar.Get(4)->GetDouble();
2766 
2767     // set default values for Optional args
2768     double fv = 0;
2769     double type = 0;
2770 
2771     // fv
2772     if ( nArgCount >= 5 )
2773     {
2774         if (rPar.Get(5)->GetType() != SbxEMPTY)
2775             fv = rPar.Get(5)->GetDouble();
2776     }
2777     // type
2778     if ( nArgCount >= 6 )
2779     {
2780         if (rPar.Get(6)->GetType() != SbxEMPTY)
2781             type = rPar.Get(6)->GetDouble();
2782     }
2783 
2784     Sequence< Any > aParams
2785     {
2786         Any(rate),
2787         Any(per),
2788         Any(nper),
2789         Any(pv),
2790         Any(fv),
2791         Any(type)
2792     };
2793 
2794     CallFunctionAccessFunction(aParams, "IPmt", rPar.Get(0));
2795 }
2796 
2797 void SbRtl_FV(StarBASIC *, SbxArray & rPar, bool)
2798 {
2799     sal_uInt32 nArgCount = rPar.Count() - 1;
2800 
2801     if ( nArgCount < 3 || nArgCount > 5 )
2802     {
2803         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2804         return;
2805     }
2806     // retrieve non-optional params
2807 
2808     double rate = rPar.Get(1)->GetDouble();
2809     double nper = rPar.Get(2)->GetDouble();
2810     double pmt = rPar.Get(3)->GetDouble();
2811 
2812     // set default values for Optional args
2813     double pv = 0;
2814     double type = 0;
2815 
2816     // pv
2817     if ( nArgCount >= 4 )
2818     {
2819         if (rPar.Get(4)->GetType() != SbxEMPTY)
2820             pv = rPar.Get(4)->GetDouble();
2821     }
2822     // type
2823     if ( nArgCount >= 5 )
2824     {
2825         if (rPar.Get(5)->GetType() != SbxEMPTY)
2826             type = rPar.Get(5)->GetDouble();
2827     }
2828 
2829     Sequence< Any > aParams
2830     {
2831         Any(rate),
2832         Any(nper),
2833         Any(pmt),
2834         Any(pv),
2835         Any(type)
2836     };
2837 
2838     CallFunctionAccessFunction(aParams, "FV", rPar.Get(0));
2839 }
2840 
2841 void SbRtl_DDB(StarBASIC *, SbxArray & rPar, bool)
2842 {
2843     sal_uInt32 nArgCount = rPar.Count() - 1;
2844 
2845     if ( nArgCount < 4 || nArgCount > 5 )
2846     {
2847         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2848         return;
2849     }
2850     // retrieve non-optional params
2851 
2852     double cost = rPar.Get(1)->GetDouble();
2853     double salvage = rPar.Get(2)->GetDouble();
2854     double life = rPar.Get(3)->GetDouble();
2855     double period = rPar.Get(4)->GetDouble();
2856 
2857     // set default values for Optional args
2858     double factor = 2;
2859 
2860     // factor
2861     if ( nArgCount >= 5 )
2862     {
2863         if (rPar.Get(5)->GetType() != SbxEMPTY)
2864             factor = rPar.Get(5)->GetDouble();
2865     }
2866 
2867     Sequence< Any > aParams
2868     {
2869         Any(cost),
2870         Any(salvage),
2871         Any(life),
2872         Any(period),
2873         Any(factor)
2874     };
2875 
2876     CallFunctionAccessFunction(aParams, "DDB", rPar.Get(0));
2877 }
2878 
2879 void SbRtl_Rate(StarBASIC *, SbxArray & rPar, bool)
2880 {
2881     sal_uInt32 nArgCount = rPar.Count() - 1;
2882 
2883     if ( nArgCount < 3 || nArgCount > 6 )
2884     {
2885         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2886         return;
2887     }
2888     // retrieve non-optional params
2889 
2890     double nper = 0;
2891     double pmt = 0;
2892     double pv = 0;
2893 
2894     nper = rPar.Get(1)->GetDouble();
2895     pmt = rPar.Get(2)->GetDouble();
2896     pv = rPar.Get(3)->GetDouble();
2897 
2898     // set default values for Optional args
2899     double fv = 0;
2900     double type = 0;
2901     double guess = 0.1;
2902 
2903     // fv
2904     if ( nArgCount >= 4 )
2905     {
2906         if (rPar.Get(4)->GetType() != SbxEMPTY)
2907             fv = rPar.Get(4)->GetDouble();
2908     }
2909 
2910     // type
2911     if ( nArgCount >= 5 )
2912     {
2913         if (rPar.Get(5)->GetType() != SbxEMPTY)
2914             type = rPar.Get(5)->GetDouble();
2915     }
2916 
2917     // guess
2918     if ( nArgCount >= 6 )
2919     {
2920         if (rPar.Get(6)->GetType() != SbxEMPTY)
2921             guess = rPar.Get(6)->GetDouble();
2922     }
2923 
2924     Sequence< Any > aParams
2925     {
2926         Any(nper),
2927         Any(pmt),
2928         Any(pv),
2929         Any(fv),
2930         Any(type),
2931         Any(guess)
2932     };
2933 
2934     CallFunctionAccessFunction(aParams, "Rate", rPar.Get(0));
2935 }
2936 
2937 void SbRtl_StrReverse(StarBASIC *, SbxArray & rPar, bool)
2938 {
2939     if (rPar.Count() != 2)
2940     {
2941         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2942         return;
2943     }
2944 
2945     SbxVariable* pSbxVariable = rPar.Get(1);
2946     if( pSbxVariable->IsNull() )
2947     {
2948         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2949         return;
2950     }
2951 
2952     OUString aStr = comphelper::string::reverseString(pSbxVariable->GetOUString());
2953     rPar.Get(0)->PutString(aStr);
2954 }
2955 
2956 void SbRtl_CompatibilityMode(StarBASIC *, SbxArray & rPar, bool)
2957 {
2958     bool bEnabled = false;
2959     sal_uInt32 nCount = rPar.Count();
2960     if ( nCount != 1 && nCount != 2 )
2961         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2962 
2963     SbiInstance* pInst = GetSbData()->pInst;
2964     if( pInst )
2965     {
2966         if ( nCount == 2 )
2967         {
2968             pInst->EnableCompatibility(rPar.Get(1)->GetBool());
2969         }
2970         bEnabled = pInst->IsCompatibility();
2971     }
2972     rPar.Get(0)->PutBool(bEnabled);
2973 }
2974 
2975 void SbRtl_Input(StarBASIC *, SbxArray & rPar, bool)
2976 {
2977     // 2 parameters needed
2978     if (rPar.Count() < 3)
2979     {
2980         StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2981         return;
2982     }
2983 
2984     sal_uInt16 nByteCount = rPar.Get(1)->GetUShort();
2985     sal_Int16 nFileNumber = rPar.Get(2)->GetInteger();
2986 
2987     SbiIoSystem* pIosys = GetSbData()->pInst->GetIoSystem();
2988     SbiStream* pSbStrm = pIosys->GetStream( nFileNumber );
2989     if ( !pSbStrm || !(pSbStrm->GetMode() & (SbiStreamFlags::Binary | SbiStreamFlags::Input)) )
2990     {
2991         StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
2992         return;
2993     }
2994 
2995     OString aByteBuffer;
2996     ErrCode err = pSbStrm->Read( aByteBuffer, nByteCount, true );
2997     if( !err )
2998         err = pIosys->GetError();
2999 
3000     if( err )
3001     {
3002         StarBASIC::Error( err );
3003         return;
3004     }
3005     rPar.Get(0)->PutString(OStringToOUString(aByteBuffer, osl_getThreadTextEncoding()));
3006 }
3007 
3008 void SbRtl_Me(StarBASIC *, SbxArray & rPar, bool)
3009 {
3010     SbModule* pActiveModule = GetSbData()->pInst->GetActiveModule();
3011     SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pActiveModule );
3012     SbxVariableRef refVar = rPar.Get(0);
3013     if( pClassModuleObject == nullptr )
3014     {
3015         SbObjModule* pMod = dynamic_cast<SbObjModule*>( pActiveModule );
3016         if ( pMod )
3017             refVar->PutObject( pMod );
3018         else
3019             StarBASIC::Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
3020     }
3021     else
3022         refVar->PutObject( pClassModuleObject );
3023 }
3024 
3025 #endif
3026 
3027 bool LibreOffice6FloatingPointMode()
3028 {
3029     static bool bMode = std::getenv("LIBREOFFICE6FLOATINGPOINTMODE") != nullptr;
3030 
3031     return bMode || officecfg::Office::Scripting::Basic::Compatibility::UseLibreOffice6FloatingPointConversion::get();
3032 }
3033 
3034 sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam, sal_Int16 nFirstDay )
3035 {
3036     Date aRefDate(1899'12'30);
3037     sal_Int32 nDays = static_cast<sal_Int32>(aDate);
3038     aRefDate.AddDays( nDays);
3039     DayOfWeek aDay = aRefDate.GetDayOfWeek();
3040     sal_Int16 nDay;
3041     if ( aDay != SUNDAY )
3042         nDay = static_cast<sal_Int16>(aDay) + 2;
3043     else
3044         nDay = 1;   // 1 == Sunday
3045 
3046     // #117253 optional 2nd parameter "firstdayofweek"
3047     if( bFirstDayParam )
3048     {
3049         if( nFirstDay < 0 || nFirstDay > 7 )
3050         {
3051 #if HAVE_FEATURE_SCRIPTING
3052             StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3053 #endif
3054             return 0;
3055         }
3056         if( nFirstDay == 0 )
3057         {
3058             const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
3059             if( !xCalendar.is() )
3060             {
3061 #if HAVE_FEATURE_SCRIPTING
3062                 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
3063 #endif
3064                 return 0;
3065             }
3066             nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 );
3067         }
3068         nDay = 1 + (nDay + 7 - nFirstDay) % 7;
3069     }
3070     return nDay;
3071 }
3072 
3073 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3074