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