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