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