tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / basic / source / runtime / methods.cxx
blob6e5774b187d11d78923ead81fc12dfa8459c97dd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_features.h>
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>
48 #include <runtime.hxx>
49 #include <sbunoobj.hxx>
50 #include <osl/file.hxx>
51 #include <errobject.hxx>
53 #include <comphelper/string.hxx>
54 #include <comphelper/processfactory.hxx>
56 #include <com/sun/star/uno/Sequence.hxx>
57 #include <com/sun/star/util/DateTime.hpp>
58 #include <com/sun/star/lang/Locale.hpp>
59 #include <com/sun/star/lang/XServiceInfo.hpp>
60 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
61 #include <com/sun/star/script/XErrorQuery.hpp>
62 #include <ooo/vba/VbStrConv.hpp>
63 #include <ooo/vba/VbTriState.hpp>
64 #include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp>
65 #include <memory>
66 #include <random>
67 #include <string_view>
68 #include <o3tl/char16_t2wchar_t.hxx>
70 // include search util
71 #include <com/sun/star/i18n/Transliteration.hpp>
72 #include <com/sun/star/util/SearchAlgorithms2.hpp>
73 #include <i18nutil/searchopt.hxx>
74 #include <unotools/textsearch.hxx>
75 #include <svl/numformat.hxx>
77 #include <date.hxx>
78 #include <sbstdobj.hxx>
79 #include <rtlproto.hxx>
80 #include <image.hxx>
81 #include <iosys.hxx>
82 #include "ddectrl.hxx"
83 #include <sbintern.hxx>
84 #include <basic/vbahelper.hxx>
86 #include <vector>
87 #include <math.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <errno.h>
92 #include <sbobjmod.hxx>
93 #include <sbxmod.hxx>
95 #ifdef _WIN32
96 #include <prewin.h>
97 #include <direct.h>
98 #include <io.h>
99 #include <postwin.h>
100 #else
101 #include <unistd.h>
102 #endif
104 #include <vcl/TypeSerializer.hxx>
106 using namespace comphelper;
107 using namespace osl;
108 using namespace com::sun::star;
109 using namespace com::sun::star::lang;
110 using namespace com::sun::star::uno;
112 static sal_Int32 GetDayDiff(const Date& rDate) { return rDate - Date(1899'12'30); }
114 #if HAVE_FEATURE_SCRIPTING
116 static sal_Int32 nanoSecToMilliSec(sal_Int64 nNanoSeconds)
118 // Rounding nanoseconds to milliseconds precision to avoid comparison inaccuracies
119 return o3tl::convert(nNanoSeconds, 1, tools::Time::nanoPerMilli);
122 static void FilterWhiteSpace( OUString& rStr )
124 if (rStr.isEmpty())
126 return;
128 OUStringBuffer aRet;
130 for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
132 sal_Unicode cChar = rStr[i];
133 if ((cChar != ' ') && (cChar != '\t') &&
134 (cChar != '\n') && (cChar != '\r'))
136 aRet.append(cChar);
140 rStr = aRet.makeStringAndClear();
143 static const CharClass& GetCharClass()
145 static CharClass aCharClass( Application::GetSettings().GetLanguageTag() );
146 return aCharClass;
149 static bool isFolder( FileStatus::Type aType )
151 return ( aType == FileStatus::Directory || aType == FileStatus::Volume );
155 //*** UCB file access ***
157 // Converts possibly relative paths to absolute paths
158 // according to the setting done by ChDir/ChDrive
159 OUString getFullPath( const OUString& aRelPath )
161 OUString aFileURL;
163 // #80204 Try first if it already is a valid URL
164 INetURLObject aURLObj( aRelPath );
165 aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
167 if( aFileURL.isEmpty() )
169 File::getFileURLFromSystemPath( aRelPath, aFileURL );
172 return aFileURL;
175 // TODO: -> SbiGlobals
176 static uno::Reference< ucb::XSimpleFileAccess3 > const & getFileAccess()
178 static uno::Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
179 return xSFI;
183 // Properties and methods lie down the return value at the Get (bPut = sal_False) in the
184 // element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True)
186 // CreateObject( class )
188 void SbRtl_CreateObject(StarBASIC * pBasic, SbxArray & rPar, bool)
190 if( rPar.Count() < 2 )
191 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
193 OUString aClass(rPar.Get(1)->GetOUString());
194 SbxObjectRef p = SbxBase::CreateObject( aClass );
195 if( !p.is() )
196 return StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD );
198 // Convenience: enter BASIC as parent
199 p->SetParent( pBasic );
200 rPar.Get(0)->PutObject(p.get());
203 // Error( n )
205 void SbRtl_Error(StarBASIC * pBasic, SbxArray & rPar, bool)
207 if( !pBasic )
208 return StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
210 OUString aErrorMsg;
211 ErrCode nErr = ERRCODE_NONE;
212 sal_Int32 nCode = 0;
213 if (rPar.Count() == 1)
215 nErr = StarBASIC::GetErrBasic();
216 aErrorMsg = StarBASIC::GetErrorMsg();
218 else
220 nCode = rPar.Get(1)->GetLong();
221 if( nCode > 65535 )
223 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
225 else
227 nErr = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nCode) );
230 bool bVBA = SbiRuntime::isVBAEnabled();
231 OUString tmpErrMsg;
232 if( bVBA && !aErrorMsg.isEmpty())
234 tmpErrMsg = aErrorMsg;
236 else
238 StarBASIC::MakeErrorText( nErr, aErrorMsg );
239 tmpErrMsg = StarBASIC::GetErrorText();
241 // If this rtlfunc 'Error' passed an errcode the same as the active Err Objects's
242 // current err then return the description for the error message if it is set
243 // ( complicated isn't it ? )
244 if (bVBA && rPar.Count() > 1)
246 uno::Reference< ooo::vba::XErrObject > xErrObj( SbxErrObject::getUnoErrObject() );
247 if ( xErrObj.is() && xErrObj->getNumber() == nCode && !xErrObj->getDescription().isEmpty() )
249 tmpErrMsg = xErrObj->getDescription();
252 rPar.Get(0)->PutString(tmpErrMsg);
255 // Sinus
257 void SbRtl_Sin(StarBASIC *, SbxArray & rPar, bool)
259 if (rPar.Count() < 2)
260 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
262 SbxVariableRef pArg = rPar.Get(1);
263 rPar.Get(0)->PutDouble(sin(pArg->GetDouble()));
267 void SbRtl_Cos(StarBASIC *, SbxArray & rPar, bool)
269 if (rPar.Count() < 2)
270 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
272 SbxVariableRef pArg = rPar.Get(1);
273 rPar.Get(0)->PutDouble(cos(pArg->GetDouble()));
277 void SbRtl_Atn(StarBASIC *, SbxArray & rPar, bool)
279 if (rPar.Count() < 2)
280 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
282 SbxVariableRef pArg = rPar.Get(1);
283 rPar.Get(0)->PutDouble(atan(pArg->GetDouble()));
287 void SbRtl_Abs(StarBASIC *, SbxArray & rPar, bool)
289 if (rPar.Count() < 2)
290 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
292 SbxVariableRef pArg = rPar.Get(1);
293 rPar.Get(0)->PutDouble(fabs(pArg->GetDouble()));
297 void SbRtl_Asc(StarBASIC *, SbxArray & rPar, bool)
299 if (rPar.Count() < 2)
300 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
302 SbxVariableRef pArg = rPar.Get(1);
303 OUString aStr( pArg->GetOUString() );
304 if ( aStr.isEmpty())
306 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
307 rPar.Get(0)->PutEmpty();
308 return;
310 sal_Unicode aCh = aStr[0];
311 rPar.Get(0)->PutLong(aCh);
314 static void implChr( SbxArray& rPar, bool bChrW )
316 if (rPar.Count() < 2)
317 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
319 SbxVariableRef pArg = rPar.Get(1);
321 OUString aStr;
322 if( !bChrW && SbiRuntime::isVBAEnabled() )
324 char c = static_cast<char>(pArg->GetByte());
325 aStr = OUString(&c, 1, osl_getThreadTextEncoding());
327 else
329 // Map negative 16-bit values to large positive ones, so that code like Chr(&H8000)
330 // still works after the fix for tdf#62326 changed those four-digit hex notations to
331 // produce negative values:
332 sal_Int32 aCh = pArg->GetLong();
333 if (aCh < -0x8000 || aCh > 0xFFFF)
335 StarBASIC::Error(ERRCODE_BASIC_MATH_OVERFLOW);
336 aCh = 0;
338 aStr = OUString(static_cast<sal_Unicode>(aCh));
340 rPar.Get(0)->PutString(aStr);
343 void SbRtl_Chr(StarBASIC *, SbxArray & rPar, bool)
345 implChr( rPar, false/*bChrW*/ );
348 void SbRtl_ChrW(StarBASIC *, SbxArray & rPar, bool)
350 implChr( rPar, true/*bChrW*/ );
353 #if defined _WIN32
355 namespace {
357 extern "C" void invalidParameterHandler(
358 wchar_t const * expression, wchar_t const * function, wchar_t const * file, unsigned int line,
359 uintptr_t)
361 SAL_INFO(
362 "basic",
363 "invalid parameter during _wgetdcwd; \""
364 << (expression ? OUString(o3tl::toU(expression)) : OUString("???"))
365 << "\" (" << (function ? OUString(o3tl::toU(function)) : OUString("???")) << ") at "
366 << (file ? OUString(o3tl::toU(file)) : OUString("???")) << ":" << line);
371 #endif
373 void SbRtl_CurDir(StarBASIC *, SbxArray & rPar, bool)
375 // #57064 Although this function doesn't work with DirEntry, it isn't touched
376 // by the adjustment to virtual URLs, as, using the DirEntry-functionality,
377 // there's no possibility to detect the current one in a way that a virtual URL
378 // could be delivered.
380 if (rPar.Count() > 2)
381 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
383 #if defined(_WIN32)
384 int nCurDir = 0; // Current dir // JSM
385 if (rPar.Count() == 2)
387 OUString aDrive = rPar.Get(1)->GetOUString();
388 if ( aDrive.getLength() != 1 )
389 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
391 auto c = rtl::toAsciiUpperCase(aDrive[0]);
392 if ( !rtl::isAsciiUpperCase( c ) )
393 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
395 nCurDir = c - 'A' + 1;
397 wchar_t pBuffer[ _MAX_PATH ];
398 // _wgetdcwd calls the C runtime's invalid parameter handler (which by default terminates the
399 // process) if nCurDir does not correspond to an existing drive, so temporarily set a "harmless"
400 // handler:
401 auto const handler = _set_thread_local_invalid_parameter_handler(&invalidParameterHandler);
402 auto const ok = _wgetdcwd( nCurDir, pBuffer, _MAX_PATH ) != nullptr;
403 _set_thread_local_invalid_parameter_handler(handler);
404 if ( !ok )
405 return StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE );
407 rPar.Get(0)->PutString(OUString(o3tl::toU(pBuffer)));
409 #else
411 const int PATH_INCR = 250;
413 int nSize = PATH_INCR;
414 std::unique_ptr<char[]> pMem;
415 while( true )
417 pMem.reset(new char[nSize]);
418 if( !pMem )
419 return StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY );
421 if( getcwd( pMem.get(), nSize-1 ) != nullptr )
423 rPar.Get(0)->PutString(OUString::createFromAscii(pMem.get()));
424 return;
426 if( errno != ERANGE )
427 return StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
429 nSize += PATH_INCR;
432 #endif
435 void SbRtl_ChDir(StarBASIC * pBasic, SbxArray & rPar, bool)
437 rPar.Get(0)->PutEmpty();
438 if (rPar.Count() != 2)
439 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
441 // VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.)
442 if( SbiRuntime::isVBAEnabled() )
444 ::basic::vba::registerCurrentDirectory(getDocumentModel(pBasic),
445 rPar.Get(1)->GetOUString());
449 void SbRtl_ChDrive(StarBASIC *, SbxArray & rPar, bool)
451 rPar.Get(0)->PutEmpty();
452 if (rPar.Count() != 2)
453 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
457 // Implementation of StepRENAME with UCB
458 void implStepRenameUCB( const OUString& aSource, const OUString& aDest )
460 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
461 if( !xSFI.is() )
462 return;
466 OUString aSourceFullPath = getFullPath( aSource );
467 if( !xSFI->exists( aSourceFullPath ) )
469 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
470 return;
473 OUString aDestFullPath = getFullPath( aDest );
474 if( xSFI->exists( aDestFullPath ) )
476 StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS );
478 else
480 xSFI->move( aSourceFullPath, aDestFullPath );
483 catch(const Exception & )
485 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
489 // Implementation of StepRENAME with OSL
490 void implStepRenameOSL( const OUString& aSource, const OUString& aDest )
492 FileBase::RC nRet = File::move( getFullPath( aSource ), getFullPath( aDest ) );
493 if( nRet != FileBase::E_None )
495 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
499 void SbRtl_FileCopy(StarBASIC *, SbxArray & rPar, bool)
501 rPar.Get(0)->PutEmpty();
502 if (rPar.Count() != 3)
503 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
505 OUString aSource = rPar.Get(1)->GetOUString();
506 OUString aDest = rPar.Get(2)->GetOUString();
507 if( hasUno() )
509 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
510 if( xSFI.is() )
514 xSFI->copy( getFullPath( aSource ), getFullPath( aDest ) );
516 catch(const Exception & )
518 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
522 else
524 FileBase::RC nRet = File::copy( getFullPath( aSource ), getFullPath( aDest ) );
525 if( nRet != FileBase::E_None )
527 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
532 void SbRtl_Kill(StarBASIC *, SbxArray & rPar, bool)
534 rPar.Get(0)->PutEmpty();
535 if (rPar.Count() != 2)
536 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
538 OUString aFileSpec = rPar.Get(1)->GetOUString();
540 if( hasUno() )
542 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
543 if( xSFI.is() )
545 OUString aFullPath = getFullPath( aFileSpec );
546 if( !xSFI->exists( aFullPath ) || xSFI->isFolder( aFullPath ) )
548 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
549 return;
553 xSFI->kill( aFullPath );
555 catch(const Exception & )
557 StarBASIC::Error( ERRCODE_IO_GENERAL );
561 else
563 File::remove( getFullPath( aFileSpec ) );
567 void SbRtl_MkDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
569 rPar.Get(0)->PutEmpty();
570 if (rPar.Count() != 2)
571 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
573 OUString aPath = rPar.Get(1)->GetOUString();
574 if ( SbiRuntime::isVBAEnabled() )
576 // In vba if the full path is not specified then
577 // folder is created relative to the curdir
578 INetURLObject aURLObj( getFullPath( aPath ) );
579 if ( aURLObj.GetProtocol() != INetProtocol::File )
581 SbxArrayRef pPar = new SbxArray();
582 SbxVariableRef pResult = new SbxVariable();
583 SbxVariableRef pParam = new SbxVariable();
584 pPar->Insert(pResult.get(), pPar->Count());
585 pPar->Insert(pParam.get(), pPar->Count());
586 SbRtl_CurDir( pBasic, *pPar, bWrite );
588 OUString sCurPathURL;
589 File::getFileURLFromSystemPath(pPar->Get(0)->GetOUString(), sCurPathURL);
591 aURLObj.SetURL( sCurPathURL );
592 aURLObj.Append( aPath );
593 File::getSystemPathFromFileURL(aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ),aPath ) ;
597 if( hasUno() )
599 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
600 if( xSFI.is() )
604 xSFI->createFolder( getFullPath( aPath ) );
606 catch(const Exception & )
608 StarBASIC::Error( ERRCODE_IO_GENERAL );
612 else
614 Directory::create( getFullPath( aPath ) );
619 static void implRemoveDirRecursive( const OUString& aDirPath )
621 DirectoryItem aItem;
622 FileBase::RC nRet = DirectoryItem::get( aDirPath, aItem );
623 bool bExists = (nRet == FileBase::E_None);
625 FileStatus aFileStatus( osl_FileStatus_Mask_Type );
626 nRet = aItem.getFileStatus( aFileStatus );
627 bool bFolder = nRet == FileBase::E_None
628 && isFolder( aFileStatus.getFileType() );
630 if( !bExists || !bFolder )
632 return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
635 Directory aDir( aDirPath );
636 nRet = aDir.open();
637 if( nRet != FileBase::E_None )
639 return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
641 aDir.close();
643 comphelper::DirectoryHelper::deleteDirRecursively(aDirPath);
647 void SbRtl_RmDir(StarBASIC *, SbxArray & rPar, bool)
649 rPar.Get(0)->PutEmpty();
650 if (rPar.Count() == 2)
652 OUString aPath = rPar.Get(1)->GetOUString();
653 if( hasUno() )
655 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
656 if( xSFI.is() )
660 if( !xSFI->isFolder( aPath ) )
662 return StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
664 SbiInstance* pInst = GetSbData()->pInst;
665 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
666 if( bCompatibility )
668 Sequence< OUString > aContent = xSFI->getFolderContents( aPath, true );
669 if( aContent.hasElements() )
671 return StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR );
675 xSFI->kill( getFullPath( aPath ) );
677 catch(const Exception & )
679 StarBASIC::Error( ERRCODE_IO_GENERAL );
683 else
685 implRemoveDirRecursive( getFullPath( aPath ) );
688 else
690 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
694 void SbRtl_SendKeys(StarBASIC *, SbxArray & rPar, bool)
696 rPar.Get(0)->PutEmpty();
697 StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
700 void SbRtl_Exp(StarBASIC *, SbxArray & rPar, bool)
702 if (rPar.Count() < 2)
703 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
705 double aDouble = rPar.Get(1)->GetDouble();
706 aDouble = exp( aDouble );
707 checkArithmeticOverflow( aDouble );
708 rPar.Get(0)->PutDouble(aDouble);
711 void SbRtl_FileLen(StarBASIC *, SbxArray & rPar, bool)
713 if (rPar.Count() < 2)
715 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
718 SbxVariableRef pArg = rPar.Get(1);
719 OUString aStr( pArg->GetOUString() );
720 sal_Int32 nLen = 0;
721 if( hasUno() )
723 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
724 if( xSFI.is() )
728 nLen = xSFI->getSize( getFullPath( aStr ) );
730 catch(const Exception & )
732 StarBASIC::Error( ERRCODE_IO_GENERAL );
736 else
738 DirectoryItem aItem;
739 (void)DirectoryItem::get( getFullPath( aStr ), aItem );
740 FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
741 (void)aItem.getFileStatus( aFileStatus );
742 nLen = static_cast<sal_Int32>(aFileStatus.getFileSize());
744 rPar.Get(0)->PutLong(nLen);
749 void SbRtl_Hex(StarBASIC *, SbxArray & rPar, bool)
751 if (rPar.Count() < 2)
753 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
756 SbxVariableRef pArg = rPar.Get(1);
757 // converting value to unsigned and limit to 2 or 4 byte representation
758 sal_uInt32 nVal = pArg->IsInteger() ?
759 static_cast<sal_uInt16>(pArg->GetInteger()) :
760 static_cast<sal_uInt32>(pArg->GetLong());
761 rPar.Get(0)->PutString(OUString::number(nVal, 16).toAsciiUpperCase());
764 void SbRtl_FuncCaller(StarBASIC *, SbxArray & rPar, bool)
766 if ( SbiRuntime::isVBAEnabled() && GetSbData()->pInst && GetSbData()->pInst->pRun )
768 if ( GetSbData()->pInst->pRun->GetExternalCaller() )
769 *rPar.Get(0) = *GetSbData()->pInst->pRun->GetExternalCaller();
770 else
772 SbxVariableRef pVar = new SbxVariable(SbxVARIANT);
773 *rPar.Get(0) = *pVar;
776 else
778 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
782 // InStr( [start],string,string,[compare] )
784 void SbRtl_InStr(StarBASIC *, SbxArray & rPar, bool)
786 const sal_uInt32 nArgCount = rPar.Count() - 1;
787 if ( nArgCount < 2 )
788 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
789 else
791 sal_Int32 nStartPos = 1;
792 sal_Int32 nFirstStringPos = 1;
794 if ( nArgCount >= 3 )
796 nStartPos = rPar.Get(1)->GetLong();
797 if( nStartPos <= 0 )
799 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
800 nStartPos = 1;
802 nFirstStringPos++;
805 SbiInstance* pInst = GetSbData()->pInst;
806 bool bTextMode;
807 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
808 if( bCompatibility )
810 SbiRuntime* pRT = pInst->pRun;
811 bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
813 else
815 bTextMode = true;
817 if ( nArgCount == 4 )
819 bTextMode = rPar.Get(4)->GetInteger();
821 sal_Int32 nPos;
822 const OUString aToken = rPar.Get(nFirstStringPos + 1)->GetOUString();
824 // #97545 Always find empty string
825 if( aToken.isEmpty() )
827 nPos = nStartPos;
829 else
831 const OUString aStr1 = rPar.Get(nFirstStringPos)->GetOUString();
832 const sal_Int32 nrStr1Len = aStr1.getLength();
833 if (nStartPos > nrStr1Len)
835 // Start position is greater than the string being searched
836 nPos = 0;
838 else
840 if( !bTextMode )
842 nPos = aStr1.indexOf( aToken, nStartPos - 1 ) + 1;
844 else
846 // tdf#139840 - case-insensitive operation for non-ASCII characters
847 i18nutil::SearchOptions2 aSearchOptions;
848 aSearchOptions.searchString = aToken;
849 aSearchOptions.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
850 aSearchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
851 utl::TextSearch textSearch(aSearchOptions);
853 sal_Int32 nStart = nStartPos - 1;
854 sal_Int32 nEnd = nrStr1Len;
855 nPos = textSearch.SearchForward(aStr1, &nStart, &nEnd) ? nStart + 1 : 0;
859 rPar.Get(0)->PutLong(nPos);
864 // InstrRev(string1, string2[, start[, compare]])
866 void SbRtl_InStrRev(StarBASIC *, SbxArray & rPar, bool)
868 const sal_uInt32 nArgCount = rPar.Count() - 1;
869 if ( nArgCount < 2 )
871 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
874 const OUString aStr1 = rPar.Get(1)->GetOUString();
875 const OUString aToken = rPar.Get(2)->GetOUString();
877 sal_Int32 nStartPos = -1;
878 if ( nArgCount >= 3 )
880 nStartPos = rPar.Get(3)->GetLong();
881 if( nStartPos <= 0 && nStartPos != -1 )
883 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
884 nStartPos = -1;
888 SbiInstance* pInst = GetSbData()->pInst;
889 bool bTextMode;
890 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
891 if( bCompatibility )
893 SbiRuntime* pRT = pInst->pRun;
894 bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
896 else
898 bTextMode = true;
900 if ( nArgCount == 4 )
902 bTextMode = rPar.Get(4)->GetInteger();
904 const sal_Int32 nStrLen = aStr1.getLength();
905 if( nStartPos == -1 )
907 nStartPos = nStrLen;
910 sal_Int32 nPos = 0;
911 if( nStartPos <= nStrLen )
913 sal_Int32 nTokenLen = aToken.getLength();
914 if( !nTokenLen )
916 // Always find empty string
917 nPos = nStartPos;
919 else if( nStrLen > 0 )
921 if( !bTextMode )
923 nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
925 else
927 // tdf#143332 - case-insensitive operation for non-ASCII characters
928 i18nutil::SearchOptions2 aSearchOptions;
929 aSearchOptions.searchString = aToken;
930 aSearchOptions.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
931 aSearchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
932 utl::TextSearch textSearch(aSearchOptions);
934 sal_Int32 nStart = 0;
935 sal_Int32 nEnd = nStartPos;
936 nPos = textSearch.SearchBackward(aStr1, &nEnd, &nStart) ? nStart : 0;
940 rPar.Get(0)->PutLong(nPos);
945 Int( 2.8 ) = 2.0
946 Int( -2.8 ) = -3.0
947 Fix( 2.8 ) = 2.0
948 Fix( -2.8 ) = -2.0 <- !!
951 void SbRtl_Int(StarBASIC *, SbxArray & rPar, bool)
953 if (rPar.Count() < 2)
954 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
956 SbxVariableRef pArg = rPar.Get(1);
957 double aDouble= pArg->GetDouble();
959 floor( 2.8 ) = 2.0
960 floor( -2.8 ) = -3.0
962 aDouble = floor( aDouble );
963 rPar.Get(0)->PutDouble(aDouble);
967 void SbRtl_Fix(StarBASIC *, SbxArray & rPar, bool)
969 if (rPar.Count() < 2)
970 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
972 SbxVariableRef pArg = rPar.Get(1);
973 double aDouble = pArg->GetDouble();
974 if ( aDouble >= 0.0 )
975 aDouble = floor( aDouble );
976 else
977 aDouble = ceil( aDouble );
978 rPar.Get(0)->PutDouble(aDouble);
982 void SbRtl_LCase(StarBASIC *, SbxArray & rPar, bool)
984 if (rPar.Count() < 2)
985 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
987 const CharClass& rCharClass = GetCharClass();
988 OUString aStr(rPar.Get(1)->GetOUString());
989 aStr = rCharClass.lowercase(aStr);
990 rPar.Get(0)->PutString(aStr);
993 void SbRtl_Left(StarBASIC *, SbxArray & rPar, bool)
995 if (rPar.Count() < 3)
996 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
998 OUString aStr(rPar.Get(1)->GetOUString());
999 sal_Int32 nResultLen = rPar.Get(2)->GetLong();
1000 if( nResultLen < 0 )
1002 nResultLen = 0;
1003 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1005 else if(nResultLen > aStr.getLength())
1007 nResultLen = aStr.getLength();
1009 aStr = aStr.copy(0, nResultLen );
1010 rPar.Get(0)->PutString(aStr);
1013 void SbRtl_Log(StarBASIC *, SbxArray & rPar, bool)
1015 if (rPar.Count() < 2)
1016 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1018 double aArg = rPar.Get(1)->GetDouble();
1019 if ( aArg <= 0 )
1020 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1022 double d = log( aArg );
1023 checkArithmeticOverflow( d );
1024 rPar.Get(0)->PutDouble(d);
1027 void SbRtl_LTrim(StarBASIC *, SbxArray & rPar, bool)
1029 if (rPar.Count() < 2)
1030 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1032 OUString aStr(comphelper::string::stripStart(rPar.Get(1)->GetOUString(), ' '));
1033 rPar.Get(0)->PutString(aStr);
1037 // Mid( String, nStart, nLength )
1039 void SbRtl_Mid(StarBASIC *, SbxArray & rPar, bool bWrite)
1041 int nArgCount = rPar.Count() - 1;
1042 if ( nArgCount < 2 )
1044 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1046 else
1048 // #23178: replicate the functionality of Mid$ as a command
1049 // by adding a replacement-string as a fourth parameter.
1050 // In contrast to the original the third parameter (nLength)
1051 // can't be left out here. That's considered in bWrite already.
1052 if( nArgCount == 4 )
1054 bWrite = true;
1056 OUString aArgStr = rPar.Get(1)->GetOUString();
1057 sal_Int32 nStartPos = rPar.Get(2)->GetLong();
1058 if ( nStartPos < 1 )
1060 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1062 else
1064 nStartPos--;
1065 sal_Int32 nLen = -1;
1066 bool bWriteNoLenParam = false;
1067 if ( nArgCount == 3 || bWrite )
1069 sal_Int32 n = rPar.Get(3)->GetLong();
1070 if( bWrite && n == -1 )
1072 bWriteNoLenParam = true;
1074 nLen = n;
1076 if ( bWrite )
1078 sal_Int32 nArgLen = aArgStr.getLength();
1079 if( nStartPos > nArgLen )
1081 SbiInstance* pInst = GetSbData()->pInst;
1082 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1083 if( bCompatibility )
1085 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1087 nStartPos = nArgLen;
1090 OUString aReplaceStr = rPar.Get(4)->GetOUString();
1091 sal_Int32 nReplaceStrLen = aReplaceStr.getLength();
1092 sal_Int32 nReplaceLen;
1093 if( bWriteNoLenParam )
1095 nReplaceLen = nArgLen - nStartPos;
1097 else
1099 nReplaceLen = nLen;
1100 if( nReplaceLen < 0 || nReplaceLen > nArgLen - nStartPos )
1102 nReplaceLen = nArgLen - nStartPos;
1106 OUStringBuffer aResultStr(aArgStr);
1107 sal_Int32 nErase = nReplaceLen;
1108 aResultStr.remove( nStartPos, nErase );
1109 aResultStr.insert(
1110 nStartPos, aReplaceStr.getStr(), std::min(nReplaceLen, nReplaceStrLen));
1112 rPar.Get(1)->PutString(aResultStr.makeStringAndClear());
1114 else
1116 OUString aResultStr;
1117 if (nStartPos > aArgStr.getLength())
1119 // do nothing
1121 else if(nArgCount == 2)
1123 aResultStr = aArgStr.copy( nStartPos);
1125 else
1127 if (nLen < 0)
1128 nLen = 0;
1129 if(nStartPos + nLen > aArgStr.getLength())
1131 nLen = aArgStr.getLength() - nStartPos;
1133 if (nLen > 0)
1134 aResultStr = aArgStr.copy( nStartPos, nLen );
1136 rPar.Get(0)->PutString(aResultStr);
1142 void SbRtl_Oct(StarBASIC *, SbxArray & rPar, bool)
1144 if (rPar.Count() < 2)
1145 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1147 SbxVariableRef pArg = rPar.Get(1);
1148 // converting value to unsigned and limit to 2 or 4 byte representation
1149 sal_uInt32 nVal = pArg->IsInteger() ?
1150 static_cast<sal_uInt16>(pArg->GetInteger()) :
1151 static_cast<sal_uInt32>(pArg->GetLong());
1152 rPar.Get(0)->PutString(OUString::number(nVal, 8));
1155 // Replace(expression, find, replace[, start[, count[, compare]]])
1157 void SbRtl_Replace(StarBASIC *, SbxArray & rPar, bool)
1159 const sal_uInt32 nArgCount = rPar.Count() - 1;
1160 if ( nArgCount < 3 || nArgCount > 6 )
1161 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1163 sal_Int32 lStartPos = 1;
1164 if (nArgCount >= 4)
1166 if (rPar.Get(4)->GetType() != SbxEMPTY)
1168 lStartPos = rPar.Get(4)->GetLong();
1170 if (lStartPos < 1)
1172 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
1175 --lStartPos; // Make it 0-based
1177 sal_Int32 lCount = -1;
1178 if (nArgCount >= 5)
1180 if (rPar.Get(5)->GetType() != SbxEMPTY)
1182 lCount = rPar.Get(5)->GetLong();
1184 if (lCount < -1)
1186 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
1190 bool bCaseInsensitive;
1191 if (nArgCount == 6)
1193 bCaseInsensitive = rPar.Get(6)->GetInteger();
1195 else
1197 SbiInstance* pInst = GetSbData()->pInst;
1198 if (pInst && pInst->IsCompatibility())
1200 SbiRuntime* pRT = pInst->pRun;
1201 bCaseInsensitive = pRT && pRT->IsImageFlag(SbiImageFlags::COMPARETEXT);
1203 else
1205 bCaseInsensitive = true;
1209 const OUString aExpStr = rPar.Get(1)->GetOUString();
1210 OUString aFindStr = rPar.Get(2)->GetOUString();
1211 const OUString aReplaceStr = rPar.Get(3)->GetOUString();
1213 OUString aSrcStr(aExpStr);
1214 sal_Int32 nPrevPos = std::min(lStartPos, aSrcStr.getLength());
1215 css::uno::Sequence<sal_Int32> aOffset;
1216 if (bCaseInsensitive)
1218 // tdf#132389: case-insensitive operation for non-ASCII characters
1219 // tdf#142487: use css::i18n::Transliteration to correctly handle ß -> ss expansion
1220 // tdf#132388: We can't use utl::TextSearch (css::i18n::XTextSearch), because each call to
1221 // css::i18n::XTextSearch::SearchForward transliterates input string, making
1222 // performance of repeated calls unacceptable
1223 auto xTrans = css::i18n::Transliteration::create(comphelper::getProcessComponentContext());
1224 xTrans->loadModule(css::i18n::TransliterationModules_IGNORE_CASE, {});
1225 aFindStr = xTrans->transliterate(aFindStr, 0, aFindStr.getLength(), aOffset);
1226 aSrcStr = xTrans->transliterate(aSrcStr, nPrevPos, aSrcStr.getLength() - nPrevPos, aOffset);
1227 nPrevPos = std::distance(aOffset.begin(),
1228 std::lower_bound(aOffset.begin(), aOffset.end(), nPrevPos));
1231 auto getExpStrPos = [aOffset, nExpLen = aExpStr.getLength()](sal_Int32 nSrcStrPos) -> sal_Int32
1233 assert(!aOffset.hasElements() || aOffset.getLength() >= nSrcStrPos);
1234 if (!aOffset.hasElements())
1235 return nSrcStrPos;
1236 return aOffset.getLength() > nSrcStrPos ? aOffset[nSrcStrPos] : nExpLen;
1239 // Note: the result starts from lStartPos, removing everything to the left. See i#94895.
1240 OUStringBuffer sResult(aSrcStr.getLength() - nPrevPos);
1241 sal_Int32 nCounts = 0;
1242 while (lCount == -1 || lCount > nCounts)
1244 sal_Int32 nPos = aSrcStr.indexOf(aFindStr, nPrevPos);
1245 if (nPos < 0)
1246 break;
1248 lStartPos = getExpStrPos(nPrevPos);
1249 sResult.append(aExpStr.getStr() + lStartPos, getExpStrPos(nPos) - lStartPos);
1250 sResult.append(aReplaceStr);
1251 nPrevPos = nPos + aFindStr.getLength();
1252 nCounts++;
1254 lStartPos = getExpStrPos(nPrevPos);
1255 sResult.append(aExpStr.getStr() + lStartPos, aExpStr.getLength() - lStartPos);
1256 rPar.Get(0)->PutString(sResult.makeStringAndClear());
1259 void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool)
1261 if (rPar.Count() < 3)
1262 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1264 const OUString aStr = rPar.Get(1)->GetOUString();
1265 int nResultLen = rPar.Get(2)->GetLong();
1266 if( nResultLen < 0 )
1268 nResultLen = 0;
1269 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1271 int nStrLen = aStr.getLength();
1272 if ( nResultLen > nStrLen )
1274 nResultLen = nStrLen;
1276 OUString aResultStr = aStr.copy( nStrLen - nResultLen );
1277 rPar.Get(0)->PutString(aResultStr);
1280 void SbRtl_RTL(StarBASIC * pBasic, SbxArray & rPar, bool)
1282 rPar.Get(0)->PutObject(pBasic->getRTL().get());
1285 void SbRtl_RTrim(StarBASIC *, SbxArray & rPar, bool)
1287 if (rPar.Count() < 2)
1288 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1290 OUString aStr(comphelper::string::stripEnd(rPar.Get(1)->GetOUString(), ' '));
1291 rPar.Get(0)->PutString(aStr);
1294 void SbRtl_Sgn(StarBASIC *, SbxArray & rPar, bool)
1296 if (rPar.Count() < 2)
1297 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1299 double aDouble = rPar.Get(1)->GetDouble();
1300 sal_Int16 nResult = 0;
1301 if ( aDouble > 0 )
1303 nResult = 1;
1305 else if ( aDouble < 0 )
1307 nResult = -1;
1309 rPar.Get(0)->PutInteger(nResult);
1312 void SbRtl_Space(StarBASIC *, SbxArray & rPar, bool)
1314 if (rPar.Count() < 2)
1316 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1318 else
1320 const sal_Int32 nCount = rPar.Get(1)->GetLong();
1321 OUStringBuffer aBuf(nCount);
1322 string::padToLength(aBuf, nCount, ' ');
1323 rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1327 void SbRtl_Sqr(StarBASIC *, SbxArray & rPar, bool)
1329 if (rPar.Count() < 2)
1331 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1333 else
1335 double aDouble = rPar.Get(1)->GetDouble();
1336 if ( aDouble >= 0 )
1338 rPar.Get(0)->PutDouble(sqrt(aDouble));
1340 else
1342 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1347 void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
1349 if (rPar.Count() < 2)
1351 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1353 else
1355 OUString aStr;
1356 OUString aStrNew(u""_ustr);
1357 SbxVariableRef pArg = rPar.Get(1);
1358 pArg->Format( aStr );
1360 // Numbers start with a space
1361 if( pArg->IsNumericRTL() )
1363 // replace commas by points so that it's symmetric to Val!
1364 aStr = aStr.replaceFirst( ",", "." );
1366 SbiInstance* pInst = GetSbData()->pInst;
1367 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1368 if( bCompatibility )
1370 sal_Int32 nLen = aStr.getLength();
1372 const sal_Unicode* pBuf = aStr.getStr();
1374 bool bNeg = ( pBuf[0] == '-' );
1375 sal_Int32 iZeroSearch = 0;
1376 if( bNeg )
1378 aStrNew += "-";
1379 iZeroSearch++;
1381 else
1383 if( pBuf[0] != ' ' )
1385 aStrNew += " ";
1388 sal_Int32 iNext = iZeroSearch + 1;
1389 if( pBuf[iZeroSearch] == '0' && nLen > iNext && pBuf[iNext] == '.' )
1391 iZeroSearch += 1;
1393 aStrNew += aStr.subView(iZeroSearch);
1395 else
1397 aStrNew = " " + aStr;
1400 else
1402 aStrNew = aStr;
1404 rPar.Get(0)->PutString(aStrNew);
1408 void SbRtl_StrComp(StarBASIC *, SbxArray & rPar, bool)
1410 if (rPar.Count() < 3)
1412 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1413 rPar.Get(0)->PutEmpty();
1414 return;
1416 const OUString aStr1 = rPar.Get(1)->GetOUString();
1417 const OUString aStr2 = rPar.Get(2)->GetOUString();
1419 SbiInstance* pInst = GetSbData()->pInst;
1420 bool bTextCompare;
1421 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1422 if( bCompatibility )
1424 SbiRuntime* pRT = pInst->pRun;
1425 bTextCompare = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
1427 else
1429 bTextCompare = true;
1431 if (rPar.Count() == 4)
1432 bTextCompare = rPar.Get(3)->GetInteger();
1434 if( !bCompatibility )
1436 bTextCompare = !bTextCompare;
1438 sal_Int32 nRetValue = 0;
1439 if( bTextCompare )
1441 ::utl::TransliterationWrapper* pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
1442 if( !pTransliterationWrapper )
1444 const uno::Reference< uno::XComponentContext >& xContext = getProcessComponentContext();
1445 GetSbData()->pTransliterationWrapper.reset(
1446 new ::utl::TransliterationWrapper( xContext,
1447 TransliterationFlags::IGNORE_CASE |
1448 TransliterationFlags::IGNORE_KANA |
1449 TransliterationFlags::IGNORE_WIDTH ) );
1450 pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
1453 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
1454 pTransliterationWrapper->loadModuleIfNeeded( eLangType );
1455 nRetValue = pTransliterationWrapper->compareString( aStr1, aStr2 );
1457 else
1459 sal_Int32 aResult;
1460 aResult = aStr1.compareTo( aStr2 );
1461 if ( aResult < 0 )
1463 nRetValue = -1;
1465 else if ( aResult > 0)
1467 nRetValue = 1;
1470 rPar.Get(0)->PutInteger(sal::static_int_cast<sal_Int16>(nRetValue));
1473 void SbRtl_String(StarBASIC *, SbxArray & rPar, bool)
1475 if (rPar.Count() < 2)
1477 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1479 else
1481 sal_Unicode aFiller;
1482 sal_Int32 lCount = rPar.Get(1)->GetLong();
1483 if( lCount < 0 || lCount > 0xffff )
1485 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1487 if (rPar.Get(2)->GetType() == SbxINTEGER)
1489 aFiller = static_cast<sal_Unicode>(rPar.Get(2)->GetInteger());
1491 else
1493 const OUString aStr = rPar.Get(2)->GetOUString();
1494 aFiller = aStr[0];
1496 OUStringBuffer aBuf(lCount);
1497 string::padToLength(aBuf, lCount, aFiller);
1498 rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1502 void SbRtl_Tab(StarBASIC *, SbxArray & rPar, bool)
1504 if (rPar.Count() < 2)
1505 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1506 else
1508 const sal_Int32 nCount = std::max(rPar.Get(1)->GetLong(), sal_Int32(0));
1509 OUStringBuffer aStr(nCount);
1510 comphelper::string::padToLength(aStr, nCount, '\t');
1511 rPar.Get(0)->PutString(aStr.makeStringAndClear());
1515 void SbRtl_Tan(StarBASIC *, SbxArray & rPar, bool)
1517 if (rPar.Count() < 2)
1519 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1521 else
1523 SbxVariableRef pArg = rPar.Get(1);
1524 rPar.Get(0)->PutDouble(tan(pArg->GetDouble()));
1528 void SbRtl_UCase(StarBASIC *, SbxArray & rPar, bool)
1530 if (rPar.Count() < 2)
1532 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1534 else
1536 const CharClass& rCharClass = GetCharClass();
1537 OUString aStr(rPar.Get(1)->GetOUString());
1538 aStr = rCharClass.uppercase( aStr );
1539 rPar.Get(0)->PutString(aStr);
1544 void SbRtl_Val(StarBASIC *, SbxArray & rPar, bool)
1546 if (rPar.Count() < 2)
1548 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1550 else
1552 double nResult = 0.0;
1553 char* pEndPtr;
1555 OUString aStr(rPar.Get(1)->GetOUString());
1557 FilterWhiteSpace( aStr );
1558 if ( aStr.getLength() > 1 && aStr[0] == '&' )
1560 int nRadix = 10;
1561 char aChar = static_cast<char>(aStr[1]);
1562 if ( aChar == 'h' || aChar == 'H' )
1564 nRadix = 16;
1566 else if ( aChar == 'o' || aChar == 'O' )
1568 nRadix = 8;
1570 if ( nRadix != 10 )
1572 OString aByteStr(OUStringToOString(aStr, osl_getThreadTextEncoding()));
1573 sal_Int16 nlResult = static_cast<sal_Int16>(strtol( aByteStr.getStr()+2, &pEndPtr, nRadix));
1574 nResult = static_cast<double>(nlResult);
1577 else
1579 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
1580 sal_Int32 nParseEnd = 0;
1581 nResult = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd );
1582 if ( eStatus != rtl_math_ConversionStatus_Ok )
1583 StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW );
1584 /* TODO: we should check whether all characters were parsed here,
1585 * but earlier code silently ignored trailing nonsense such as "1x"
1586 * resulting in 1 with the side effect that any alpha-only-string
1587 * like "x" resulted in 0. Not changing that now (2013-03-22) as
1588 * user macros may rely on it. */
1589 #if 0
1590 else if ( nParseEnd != aStr.getLength() )
1591 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
1592 #endif
1595 rPar.Get(0)->PutDouble(nResult);
1600 // Helper functions for date conversion
1601 sal_Int16 implGetDateDay( double aDate )
1603 aDate = floor( aDate );
1604 Date aRefDate(1899'12'30);
1605 aRefDate.AddDays( aDate );
1607 sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetDay() );
1608 return nRet;
1611 sal_Int16 implGetDateMonth( double aDate )
1613 Date aRefDate(1899'12'30);
1614 sal_Int32 nDays = static_cast<sal_Int32>(aDate);
1615 aRefDate.AddDays( nDays );
1616 sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetMonth() );
1617 return nRet;
1620 css::util::Date SbxDateToUNODate( const SbxValue* const pVal )
1622 double aDate = pVal->GetDate();
1624 css::util::Date aUnoDate;
1625 aUnoDate.Day = implGetDateDay ( aDate );
1626 aUnoDate.Month = implGetDateMonth( aDate );
1627 aUnoDate.Year = implGetDateYear ( aDate );
1629 return aUnoDate;
1632 void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate)
1634 double dDate;
1635 if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, SbDateCorrection::None, dDate ) )
1637 pVal->PutDate( dDate );
1641 // Function to convert date to UNO date (com.sun.star.util.Date)
1642 void SbRtl_CDateToUnoDate(StarBASIC *, SbxArray & rPar, bool)
1644 if (rPar.Count() != 2)
1646 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1649 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODate(rPar.Get(1))));
1652 // Function to convert date from UNO date (com.sun.star.util.Date)
1653 void SbRtl_CDateFromUnoDate(StarBASIC *, SbxArray & rPar, bool)
1655 if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
1657 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1660 Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Date>::get()));
1661 css::util::Date aUnoDate;
1662 if(aAny >>= aUnoDate)
1663 SbxDateFromUNODate(rPar.Get(0), aUnoDate);
1664 else
1665 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1668 css::util::Time SbxDateToUNOTime( const SbxValue* const pVal )
1670 double aDate = pVal->GetDate();
1672 css::util::Time aUnoTime;
1673 aUnoTime.Hours = implGetHour ( aDate );
1674 aUnoTime.Minutes = implGetMinute ( aDate );
1675 aUnoTime.Seconds = implGetSecond ( aDate );
1676 aUnoTime.NanoSeconds = implGetNanoSecond( aDate );
1678 return aUnoTime;
1681 void SbxDateFromUNOTime( SbxValue *pVal, const css::util::Time& aUnoTime)
1683 pVal->PutDate(implTimeSerial(aUnoTime.Hours, aUnoTime.Minutes, aUnoTime.Seconds,
1684 nanoSecToMilliSec(aUnoTime.NanoSeconds)));
1687 // Function to convert date to UNO time (com.sun.star.util.Time)
1688 void SbRtl_CDateToUnoTime(StarBASIC *, SbxArray & rPar, bool)
1690 if (rPar.Count() != 2)
1692 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1695 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNOTime(rPar.Get(1))));
1698 // Function to convert date from UNO time (com.sun.star.util.Time)
1699 void SbRtl_CDateFromUnoTime(StarBASIC *, SbxArray & rPar, bool)
1701 if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
1703 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1706 Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Time>::get()));
1707 css::util::Time aUnoTime;
1708 if(aAny >>= aUnoTime)
1709 SbxDateFromUNOTime(rPar.Get(0), aUnoTime);
1710 else
1711 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1714 css::util::DateTime SbxDateToUNODateTime( const SbxValue* const pVal )
1716 double aDate = pVal->GetDate();
1718 css::util::DateTime aUnoDT;
1719 aUnoDT.Day = implGetDateDay ( aDate );
1720 aUnoDT.Month = implGetDateMonth( aDate );
1721 aUnoDT.Year = implGetDateYear ( aDate );
1722 aUnoDT.Hours = implGetHour ( aDate );
1723 aUnoDT.Minutes = implGetMinute ( aDate );
1724 aUnoDT.Seconds = implGetSecond ( aDate );
1725 aUnoDT.NanoSeconds = implGetNanoSecond( aDate );
1727 return aUnoDT;
1730 void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT)
1732 double dDate(0.0);
1733 if (implDateTimeSerial(aUnoDT.Year, aUnoDT.Month, aUnoDT.Day, aUnoDT.Hours, aUnoDT.Minutes,
1734 aUnoDT.Seconds, nanoSecToMilliSec(aUnoDT.NanoSeconds), dDate))
1736 pVal->PutDate( dDate );
1740 // Function to convert date to UNO date (com.sun.star.util.Date)
1741 void SbRtl_CDateToUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
1743 if (rPar.Count() != 2)
1745 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1748 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODateTime(rPar.Get(1))));
1751 // Function to convert date from UNO date (com.sun.star.util.Date)
1752 void SbRtl_CDateFromUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
1754 if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
1756 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1759 Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::DateTime>::get()));
1760 css::util::DateTime aUnoDT;
1761 if(aAny >>= aUnoDT)
1762 SbxDateFromUNODateTime(rPar.Get(0), aUnoDT);
1763 else
1764 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1767 // Function to convert date to ISO 8601 date format YYYYMMDD
1768 void SbRtl_CDateToIso(StarBASIC *, SbxArray & rPar, bool)
1770 if (rPar.Count() == 2)
1772 double aDate = rPar.Get(1)->GetDate();
1774 // Date may actually even be -YYYYYMMDD
1775 char Buffer[11];
1776 sal_Int16 nYear = implGetDateYear( aDate );
1777 snprintf( Buffer, sizeof( Buffer ), (nYear < 0 ? "%05d%02d%02d" : "%04d%02d%02d"),
1778 static_cast<int>(nYear),
1779 static_cast<int>(implGetDateMonth( aDate )),
1780 static_cast<int>(implGetDateDay( aDate )) );
1781 OUString aRetStr = OUString::createFromAscii( Buffer );
1782 rPar.Get(0)->PutString(aRetStr);
1784 else
1786 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1790 // Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD
1791 // And even YYMMDD for compatibility, sigh...
1792 void SbRtl_CDateFromIso(StarBASIC *, SbxArray & rPar, bool)
1794 if (rPar.Count() == 2)
1798 OUString aStr = rPar.Get(1)->GetOUString();
1799 if (aStr.isEmpty())
1800 break;
1802 // Valid formats are
1803 // YYYYMMDD -YYYMMDD YYYYYMMDD -YYYYYMMDD YYMMDD
1804 // YYYY-MM-DD -YYYY-MM-DD YYYYY-MM-DD -YYYYY-MM-DD
1806 sal_Int32 nSign = 1;
1807 if (aStr[0] == '-')
1809 nSign = -1;
1810 aStr = aStr.copy(1);
1812 const sal_Int32 nLen = aStr.getLength();
1814 // Signed YYMMDD two digit year is invalid.
1815 if (nLen == 6 && nSign == -1)
1816 break;
1818 // Now valid
1819 // YYYYMMDD YYYYYMMDD YYMMDD
1820 // YYYY-MM-DD YYYYY-MM-DD
1821 if (nLen != 6 && (nLen < 8 || 11 < nLen))
1822 break;
1824 bool bUseTwoDigitYear = false;
1825 std::u16string_view aYearStr, aMonthStr, aDayStr;
1826 if (nLen == 6 || nLen == 8 || nLen == 9)
1828 // ((Y)YY)YYMMDD
1829 if (!comphelper::string::isdigitAsciiString(aStr))
1830 break;
1832 const sal_Int32 nMonthPos = (nLen == 8 ? 4 : (nLen == 6 ? 2 : 5));
1833 if (nMonthPos == 2)
1834 bUseTwoDigitYear = true;
1835 aYearStr = aStr.subView( 0, nMonthPos );
1836 aMonthStr = aStr.subView( nMonthPos, 2 );
1837 aDayStr = aStr.subView( nMonthPos + 2, 2 );
1839 else
1841 // (Y)YYYY-MM-DD
1842 const sal_Int32 nMonthSep = (nLen == 11 ? 5 : 4);
1843 if (aStr.indexOf('-') != nMonthSep)
1844 break;
1845 if (aStr.indexOf('-', nMonthSep + 1) != nMonthSep + 3)
1846 break;
1848 aYearStr = aStr.subView( 0, nMonthSep );
1849 aMonthStr = aStr.subView( nMonthSep + 1, 2 );
1850 aDayStr = aStr.subView( nMonthSep + 4, 2 );
1851 if ( !comphelper::string::isdigitAsciiString(aYearStr) ||
1852 !comphelper::string::isdigitAsciiString(aMonthStr) ||
1853 !comphelper::string::isdigitAsciiString(aDayStr))
1854 break;
1857 double dDate;
1858 if (!implDateSerial( static_cast<sal_Int16>(nSign * o3tl::toInt32(aYearStr)),
1859 static_cast<sal_Int16>(o3tl::toInt32(aMonthStr)), static_cast<sal_Int16>(o3tl::toInt32(aDayStr)),
1860 bUseTwoDigitYear, SbDateCorrection::None, dDate ))
1861 break;
1863 rPar.Get(0)->PutDate(dDate);
1865 return;
1867 while (false);
1869 SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER );
1871 else
1873 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1877 void SbRtl_DateSerial(StarBASIC *, SbxArray & rPar, bool)
1879 if (rPar.Count() < 4)
1881 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1883 sal_Int16 nYear = rPar.Get(1)->GetInteger();
1884 sal_Int16 nMonth = rPar.Get(2)->GetInteger();
1885 sal_Int16 nDay = rPar.Get(3)->GetInteger();
1887 double dDate;
1888 if( implDateSerial( nYear, nMonth, nDay, true, SbDateCorrection::RollOver, dDate ) )
1890 rPar.Get(0)->PutDate(dDate);
1894 void SbRtl_TimeSerial(StarBASIC *, SbxArray & rPar, bool)
1896 if (rPar.Count() < 4)
1898 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1900 sal_Int16 nHour = rPar.Get(1)->GetInteger();
1901 if ( nHour == 24 )
1903 nHour = 0; // because of UNO DateTimes, which go till 24 o'clock
1905 sal_Int16 nMinute = rPar.Get(2)->GetInteger();
1906 sal_Int16 nSecond = rPar.Get(3)->GetInteger();
1907 if ((nHour < 0 || nHour > 23) ||
1908 (nMinute < 0 || nMinute > 59 ) ||
1909 (nSecond < 0 || nSecond > 59 ))
1911 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1914 rPar.Get(0)->PutDate(implTimeSerial(nHour, nMinute, nSecond, 0)); // JSM
1917 void SbRtl_DateValue(StarBASIC *, SbxArray & rPar, bool)
1919 if (rPar.Count() < 2)
1921 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1923 else
1925 // #39629 check GetSbData()->pInst, can be called from the URL line
1926 std::shared_ptr<SvNumberFormatter> pFormatter;
1927 if( GetSbData()->pInst )
1929 pFormatter = GetSbData()->pInst->GetNumberFormatter();
1931 else
1933 sal_uInt32 n; // Dummy
1934 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
1937 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
1938 sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
1939 double fResult;
1940 OUString aStr(rPar.Get(1)->GetOUString());
1941 bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
1942 SvNumFormatType nType = pFormatter->GetType( nIndex );
1944 // DateValue("February 12, 1969") raises error if the system locale is not en_US
1945 // It seems that both locale number formatter and English number
1946 // formatter are supported in Visual Basic.
1947 if( !bSuccess && ( eLangType != LANGUAGE_ENGLISH_US ) )
1949 // Try using LANGUAGE_ENGLISH_US to get the date value.
1950 nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US);
1951 bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
1952 nType = pFormatter->GetType( nIndex );
1955 if(bSuccess && (nType==SvNumFormatType::DATE || nType==SvNumFormatType::DATETIME))
1957 if ( nType == SvNumFormatType::DATETIME )
1959 // cut time
1960 if ( fResult > 0.0 )
1962 fResult = floor( fResult );
1964 else
1966 fResult = ceil( fResult );
1969 rPar.Get(0)->PutDate(fResult);
1971 else
1973 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
1978 void SbRtl_TimeValue(StarBASIC *, SbxArray & rPar, bool)
1980 if (rPar.Count() < 2)
1982 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1984 else
1986 std::shared_ptr<SvNumberFormatter> pFormatter;
1987 if( GetSbData()->pInst )
1988 pFormatter = GetSbData()->pInst->GetNumberFormatter();
1989 else
1991 sal_uInt32 n;
1992 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
1995 sal_uInt32 nIndex = 0;
1996 double fResult;
1997 bool bSuccess = pFormatter->IsNumberFormat(rPar.Get(1)->GetOUString(),
1998 nIndex, fResult );
1999 SvNumFormatType nType = pFormatter->GetType(nIndex);
2000 if(bSuccess && (nType==SvNumFormatType::TIME||nType==SvNumFormatType::DATETIME))
2002 if ( nType == SvNumFormatType::DATETIME )
2004 // cut days
2005 fResult = fmod( fResult, 1 );
2007 rPar.Get(0)->PutDate(fResult);
2009 else
2011 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
2016 void SbRtl_Day(StarBASIC *, SbxArray & rPar, bool)
2018 if (rPar.Count() < 2)
2020 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2022 else
2024 SbxVariableRef pArg = rPar.Get(1);
2025 double aDate = pArg->GetDate();
2027 sal_Int16 nDay = implGetDateDay( aDate );
2028 rPar.Get(0)->PutInteger(nDay);
2032 void SbRtl_Year(StarBASIC *, SbxArray & rPar, bool)
2034 if (rPar.Count() < 2)
2036 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2038 else
2040 sal_Int16 nYear = implGetDateYear(rPar.Get(1)->GetDate());
2041 rPar.Get(0)->PutInteger(nYear);
2045 sal_Int16 implGetHour( double dDate )
2047 double nFrac = (dDate - floor(dDate)) * ::tools::Time::milliSecPerDay;
2048 sal_uInt64 nMilliSeconds = static_cast<sal_uInt64>(nFrac + 0.5);
2049 return static_cast<sal_Int16>((nMilliSeconds / ::tools::Time::milliSecPerHour)
2050 % ::tools::Time::hourPerDay);
2053 void SbRtl_Hour(StarBASIC *, SbxArray & rPar, bool)
2055 if (rPar.Count() < 2)
2057 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2059 else
2061 double nArg = rPar.Get(1)->GetDate();
2062 sal_Int16 nHour = implGetHour( nArg );
2063 rPar.Get(0)->PutInteger(nHour);
2067 void SbRtl_Minute(StarBASIC *, SbxArray & rPar, bool)
2069 if (rPar.Count() < 2)
2071 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2073 else
2075 double nArg = rPar.Get(1)->GetDate();
2076 sal_Int16 nMin = implGetMinute( nArg );
2077 rPar.Get(0)->PutInteger(nMin);
2081 void SbRtl_Month(StarBASIC *, SbxArray & rPar, bool)
2083 if (rPar.Count() < 2)
2085 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2087 else
2089 sal_Int16 nMonth = implGetDateMonth(rPar.Get(1)->GetDate());
2090 rPar.Get(0)->PutInteger(nMonth);
2094 sal_Int16 implGetSecond( double dDate )
2096 double nFrac = (dDate - floor(dDate)) * ::tools::Time::milliSecPerDay;
2097 sal_uInt64 nMilliSeconds = static_cast<sal_uInt64>(nFrac + 0.5);
2098 return static_cast<sal_Int16>((nMilliSeconds / ::tools::Time::milliSecPerSec)
2099 % ::tools::Time::secondPerMinute);
2102 sal_Int32 implGetNanoSecond(double dDate)
2104 double nFrac = (dDate - floor(dDate)) * ::tools::Time::milliSecPerDay;
2105 sal_uInt64 nMilliSeconds = static_cast<sal_uInt64>(nFrac + 0.5);
2106 nMilliSeconds %= ::tools::Time::milliSecPerSec;
2108 return static_cast<sal_Int32>(nMilliSeconds * ::tools::Time ::nanoPerMilli);
2111 void SbRtl_Second(StarBASIC *, SbxArray & rPar, bool)
2113 if (rPar.Count() < 2)
2115 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2117 else
2119 double nArg = rPar.Get(1)->GetDate();
2120 sal_Int16 nSecond = implGetSecond( nArg );
2121 rPar.Get(0)->PutInteger(nSecond);
2125 double Now_Impl()
2127 // tdf#161469 - align implementation with the now function in calc, i.e., include subseconds
2128 DateTime aActTime(DateTime::SYSTEM);
2129 return static_cast<double>(GetDayDiff(aActTime))
2130 + implTimeSerial(aActTime.GetHour(), aActTime.GetMin(), aActTime.GetSec(),
2131 nanoSecToMilliSec(aActTime.GetNanoSec()));
2134 // Date Now()
2136 void SbRtl_Now(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutDate(Now_Impl()); }
2138 // Date Time()
2140 void SbRtl_Time(StarBASIC *, SbxArray & rPar, bool bWrite)
2142 if ( !bWrite )
2144 tools::Time aTime( tools::Time::SYSTEM );
2145 SbxVariable* pMeth = rPar.Get(0);
2146 OUString aRes;
2147 if( pMeth->IsFixed() )
2149 // Time$: hh:mm:ss
2150 char buf[ 20 ];
2151 snprintf( buf, sizeof(buf), "%02d:%02d:%02d",
2152 aTime.GetHour(), aTime.GetMin(), aTime.GetSec() );
2153 aRes = OUString::createFromAscii( buf );
2155 else
2157 // Time: system dependent
2158 tools::Long nSeconds=aTime.GetHour();
2159 nSeconds *= 3600;
2160 nSeconds += aTime.GetMin() * 60;
2161 nSeconds += aTime.GetSec();
2162 double nDays = static_cast<double>(nSeconds) * ( 1.0 / (24.0*3600.0) );
2163 const Color* pCol;
2165 std::shared_ptr<SvNumberFormatter> pFormatter;
2166 sal_uInt32 nIndex;
2167 if( GetSbData()->pInst )
2169 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2170 nIndex = GetSbData()->pInst->GetStdTimeIdx();
2172 else
2174 sal_uInt32 n; // Dummy
2175 pFormatter = SbiInstance::PrepareNumberFormatter( n, nIndex, n );
2178 pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
2180 pMeth->PutString( aRes );
2182 else
2184 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
2188 void SbRtl_Timer(StarBASIC *, SbxArray & rPar, bool)
2190 tools::Time aTime( tools::Time::SYSTEM );
2191 tools::Long nSeconds = aTime.GetHour();
2192 nSeconds *= 3600;
2193 nSeconds += aTime.GetMin() * 60;
2194 nSeconds += aTime.GetSec();
2195 rPar.Get(0)->PutDate(static_cast<double>(nSeconds));
2199 void SbRtl_Date(StarBASIC *, SbxArray & rPar, bool bWrite)
2201 if ( !bWrite )
2203 Date aToday( Date::SYSTEM );
2204 double nDays = static_cast<double>(GetDayDiff( aToday ));
2205 SbxVariable* pMeth = rPar.Get(0);
2206 if( pMeth->IsString() )
2208 OUString aRes;
2209 const Color* pCol;
2211 std::shared_ptr<SvNumberFormatter> pFormatter;
2212 sal_uInt32 nIndex;
2213 if( GetSbData()->pInst )
2215 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2216 nIndex = GetSbData()->pInst->GetStdDateIdx();
2218 else
2220 sal_uInt32 n;
2221 pFormatter = SbiInstance::PrepareNumberFormatter( nIndex, n, n );
2224 pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
2225 pMeth->PutString( aRes );
2227 else
2229 pMeth->PutDate( nDays );
2232 else
2234 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
2238 void SbRtl_IsArray(StarBASIC *, SbxArray & rPar, bool)
2240 if (rPar.Count() != 2)
2241 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2243 rPar.Get(0)->PutBool((rPar.Get(1)->GetType() & SbxARRAY) != 0);
2246 void SbRtl_IsObject(StarBASIC *, SbxArray & rPar, bool)
2248 if (rPar.Count() != 2)
2249 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2251 SbxVariable* pVar = rPar.Get(1);
2252 bool bObject = pVar->IsObject();
2253 SbxBase* pObj = (bObject ? pVar->GetObject() : nullptr);
2255 if( auto pUnoClass = dynamic_cast<SbUnoClass*>( pObj) )
2257 bObject = pUnoClass->getUnoClass().is();
2259 rPar.Get(0)->PutBool(bObject);
2262 void SbRtl_IsDate(StarBASIC *, SbxArray & rPar, bool)
2264 if (rPar.Count() != 2)
2265 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2267 // #46134 only string is converted, all other types result in sal_False
2268 SbxVariableRef xArg = rPar.Get(1);
2269 SbxDataType eType = xArg->GetType();
2270 bool bDate = false;
2272 if( eType == SbxDATE )
2274 bDate = true;
2276 else if( eType == SbxSTRING )
2278 ErrCode nPrevError = SbxBase::GetError();
2279 SbxBase::ResetError();
2281 // force conversion of the parameter to SbxDATE
2282 xArg->SbxValue::GetDate();
2284 bDate = !SbxBase::IsError();
2286 SbxBase::ResetError();
2287 SbxBase::SetError( nPrevError );
2289 rPar.Get(0)->PutBool(bDate);
2292 void SbRtl_IsEmpty(StarBASIC *, SbxArray & rPar, bool)
2294 if (rPar.Count() != 2)
2295 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2297 SbxVariable* pVar = nullptr;
2298 if( SbiRuntime::isVBAEnabled() )
2300 pVar = getDefaultProp(rPar.Get(1));
2302 if ( pVar )
2304 pVar->Broadcast( SfxHintId::BasicDataWanted );
2305 rPar.Get(0)->PutBool(pVar->IsEmpty());
2307 else
2309 rPar.Get(0)->PutBool(rPar.Get(1)->IsEmpty());
2313 void SbRtl_IsError(StarBASIC *, SbxArray & rPar, bool)
2315 if (rPar.Count() != 2)
2316 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2318 SbxVariable* pVar = rPar.Get(1);
2319 SbUnoObject* pObj = dynamic_cast<SbUnoObject*>( pVar );
2320 if ( !pObj )
2322 if ( SbxBase* pBaseObj = (pVar->IsObject() ? pVar->GetObject() : nullptr) )
2324 pObj = dynamic_cast<SbUnoObject*>( pBaseObj );
2327 uno::Reference< script::XErrorQuery > xError;
2328 if ( pObj )
2330 xError.set( pObj->getUnoAny(), uno::UNO_QUERY );
2332 if ( xError.is() )
2334 rPar.Get(0)->PutBool(xError->hasError());
2336 else
2338 rPar.Get(0)->PutBool(rPar.Get(1)->IsErr());
2342 void SbRtl_IsNull(StarBASIC *, SbxArray & rPar, bool)
2344 if (rPar.Count() != 2)
2345 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2347 // #51475 because of Uno-objects return true
2348 // even if the pObj value is NULL
2349 SbxVariableRef pArg = rPar.Get(1);
2350 bool bNull = rPar.Get(1)->IsNull();
2351 if( !bNull && pArg->GetType() == SbxOBJECT )
2353 SbxBase* pObj = pArg->GetObject();
2354 if( !pObj )
2356 bNull = true;
2359 rPar.Get(0)->PutBool(bNull);
2362 void SbRtl_IsNumeric(StarBASIC *, SbxArray & rPar, bool)
2364 if (rPar.Count() != 2)
2365 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2367 rPar.Get(0)->PutBool(rPar.Get(1)->IsNumericRTL());
2371 void SbRtl_IsMissing(StarBASIC *, SbxArray & rPar, bool)
2373 if (rPar.Count() != 2)
2374 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2376 // #57915 Missing is reported by an error
2377 rPar.Get(0)->PutBool(rPar.Get(1)->IsErr());
2380 // Function looks for wildcards, removes them and always returns the pure path
2381 static OUString implSetupWildcard(const OUString& rFileParam, SbiRTLData& rRTLData)
2383 static const char cDelim1 = '/';
2384 static const char cDelim2 = '\\';
2385 static const char cWild1 = '*';
2386 static const char cWild2 = '?';
2388 rRTLData.moWildCard.reset();
2389 rRTLData.sFullNameToBeChecked.clear();
2391 OUString aFileParam = rFileParam;
2392 sal_Int32 nLastWild = aFileParam.lastIndexOf( cWild1 );
2393 if( nLastWild < 0 )
2395 nLastWild = aFileParam.lastIndexOf( cWild2 );
2397 bool bHasWildcards = ( nLastWild >= 0 );
2400 sal_Int32 nLastDelim = aFileParam.lastIndexOf( cDelim1 );
2401 if( nLastDelim < 0 )
2403 nLastDelim = aFileParam.lastIndexOf( cDelim2 );
2405 if( bHasWildcards )
2407 // Wildcards in path?
2408 if( nLastDelim >= 0 && nLastDelim > nLastWild )
2410 return aFileParam;
2413 else
2415 OUString aPathStr = getFullPath( aFileParam );
2416 if( nLastDelim != aFileParam.getLength() - 1 )
2418 rRTLData.sFullNameToBeChecked = aPathStr;
2420 return aPathStr;
2423 OUString aPureFileName;
2424 if( nLastDelim < 0 )
2426 aPureFileName = aFileParam;
2427 aFileParam.clear();
2429 else
2431 aPureFileName = aFileParam.copy( nLastDelim + 1 );
2432 aFileParam = aFileParam.copy( 0, nLastDelim );
2435 // Try again to get a valid URL/UNC-path with only the path
2436 OUString aPathStr = getFullPath( aFileParam );
2438 // Is there a pure file name left? Otherwise the path is
2439 // invalid anyway because it was not accepted by OSL before
2440 if (aPureFileName != "*")
2442 rRTLData.moWildCard.emplace(aPureFileName);
2444 return aPathStr;
2447 static bool implCheckWildcard(std::u16string_view rName, SbiRTLData const& rRTLData)
2449 bool bMatch = true;
2451 if (rRTLData.moWildCard)
2453 bMatch = rRTLData.moWildCard->Matches(rName);
2455 return bMatch;
2459 static bool isRootDir( std::u16string_view aDirURLStr )
2461 INetURLObject aDirURLObj( aDirURLStr );
2462 bool bRoot = false;
2464 // Check if it's a root directory
2465 sal_Int32 nCount = aDirURLObj.getSegmentCount();
2467 // No segment means Unix root directory "file:///"
2468 if( nCount == 0 )
2470 bRoot = true;
2472 // Exactly one segment needs further checking, because it
2473 // can be Unix "file:///foo/" -> no root
2474 // or Windows "file:///c:/" -> root
2475 else if( nCount == 1 )
2477 OUString aSeg1 = aDirURLObj.getName( 0, true,
2478 INetURLObject::DecodeMechanism::WithCharset );
2479 if( aSeg1[1] == ':' )
2481 bRoot = true;
2484 // More than one segments can never be root
2485 // so bRoot remains false
2487 return bRoot;
2490 void SbRtl_Dir(StarBASIC *, SbxArray & rPar, bool)
2492 OUString aPath;
2494 const sal_uInt32 nParCount = rPar.Count();
2495 if( nParCount > 3 )
2497 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2499 else
2501 SbiRTLData& rRTLData = GetSbData()->pInst->GetRTLData();
2503 if( hasUno() )
2505 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2506 if( xSFI.is() )
2508 if ( nParCount >= 2 )
2510 OUString aFileParam = rPar.Get(1)->GetOUString();
2512 OUString aFileURLStr = implSetupWildcard(aFileParam, rRTLData);
2513 if (!rRTLData.sFullNameToBeChecked.isEmpty())
2515 bool bExists = false;
2516 try { bExists = xSFI->exists( aFileURLStr ); }
2517 catch(const Exception & ) {}
2519 OUString aNameOnlyStr;
2520 if( bExists )
2522 INetURLObject aFileURL( aFileURLStr );
2523 aNameOnlyStr = aFileURL.getName( INetURLObject::LAST_SEGMENT,
2524 true, INetURLObject::DecodeMechanism::WithCharset );
2526 rPar.Get(0)->PutString(aNameOnlyStr);
2527 return;
2532 OUString aDirURLStr;
2533 bool bFolder = xSFI->isFolder( aFileURLStr );
2535 if( bFolder )
2537 aDirURLStr = aFileURLStr;
2539 else
2541 rPar.Get(0)->PutString(u""_ustr);
2544 sal_Int16 nFlags = SbAttributes::NORMAL;
2545 if ( nParCount > 2 )
2547 rRTLData.nDirFlags = nFlags = rPar.Get(2)->GetInteger();
2549 else
2551 rRTLData.nDirFlags = SbAttributes::NORMAL;
2553 // Read directory
2554 bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
2555 rRTLData.aDirSeq = xSFI->getFolderContents(aDirURLStr, bIncludeFolders);
2556 rRTLData.nCurDirPos = 0;
2558 // #78651 Add "." and ".." directories for VB compatibility
2559 if( bIncludeFolders )
2561 bool bRoot = isRootDir( aDirURLStr );
2563 // If it's no root directory we flag the need for
2564 // the "." and ".." directories by the value -2
2565 // for the actual position. Later for -2 will be
2566 // returned "." and for -1 ".."
2567 if( !bRoot )
2569 rRTLData.nCurDirPos = -2;
2573 catch(const Exception & )
2579 if (rRTLData.aDirSeq.hasElements())
2581 bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY);
2583 SbiInstance* pInst = GetSbData()->pInst;
2584 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
2585 for( ;; )
2587 if (rRTLData.nCurDirPos < 0)
2589 if (rRTLData.nCurDirPos == -2)
2591 aPath = ".";
2593 else if (rRTLData.nCurDirPos == -1)
2595 aPath = "..";
2597 rRTLData.nCurDirPos++;
2599 else if (rRTLData.nCurDirPos >= rRTLData.aDirSeq.getLength())
2601 rRTLData.aDirSeq.realloc(0);
2602 aPath.clear();
2603 break;
2605 else
2607 OUString aFile
2608 = rRTLData.aDirSeq.getConstArray()[rRTLData.nCurDirPos++];
2610 if( bCompatibility )
2612 if( !bFolderFlag )
2614 bool bFolder = xSFI->isFolder( aFile );
2615 if( bFolder )
2617 continue;
2621 else
2623 // Only directories
2624 if( bFolderFlag )
2626 bool bFolder = xSFI->isFolder( aFile );
2627 if( !bFolder )
2629 continue;
2634 INetURLObject aURL( aFile );
2635 aPath = aURL.getName( INetURLObject::LAST_SEGMENT, true,
2636 INetURLObject::DecodeMechanism::WithCharset );
2639 bool bMatch = implCheckWildcard(aPath, rRTLData);
2640 if( !bMatch )
2642 continue;
2644 break;
2647 rPar.Get(0)->PutString(aPath);
2650 else
2652 // TODO: OSL
2653 if ( nParCount >= 2 )
2655 OUString aFileParam = rPar.Get(1)->GetOUString();
2657 OUString aDirURL = implSetupWildcard(aFileParam, rRTLData);
2659 sal_Int16 nFlags = SbAttributes::NORMAL;
2660 if ( nParCount > 2 )
2662 rRTLData.nDirFlags = nFlags = rPar.Get(2)->GetInteger();
2664 else
2666 rRTLData.nDirFlags = SbAttributes::NORMAL;
2669 // Read directory
2670 bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
2671 rRTLData.pDir = std::make_unique<Directory>(aDirURL);
2672 FileBase::RC nRet = rRTLData.pDir->open();
2673 if( nRet != FileBase::E_None )
2675 rRTLData.pDir.reset();
2676 rPar.Get(0)->PutString(OUString());
2677 return;
2680 // #86950 Add "." and ".." directories for VB compatibility
2681 rRTLData.nCurDirPos = 0;
2682 if( bIncludeFolders )
2684 bool bRoot = isRootDir( aDirURL );
2686 // If it's no root directory we flag the need for
2687 // the "." and ".." directories by the value -2
2688 // for the actual position. Later for -2 will be
2689 // returned "." and for -1 ".."
2690 if( !bRoot )
2692 rRTLData.nCurDirPos = -2;
2698 if (rRTLData.pDir)
2700 bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY);
2701 for( ;; )
2703 if (rRTLData.nCurDirPos < 0)
2705 if (rRTLData.nCurDirPos == -2)
2707 aPath = ".";
2709 else if (rRTLData.nCurDirPos == -1)
2711 aPath = "..";
2713 rRTLData.nCurDirPos++;
2715 else
2717 DirectoryItem aItem;
2718 FileBase::RC nRet = rRTLData.pDir->getNextItem(aItem);
2719 if( nRet != FileBase::E_None )
2721 rRTLData.pDir.reset();
2722 aPath.clear();
2723 break;
2726 // Handle flags
2727 FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName );
2728 nRet = aItem.getFileStatus( aFileStatus );
2729 if( nRet != FileBase::E_None )
2731 SAL_WARN("basic", "getFileStatus failed");
2732 continue;
2735 // Only directories?
2736 if( bFolderFlag )
2738 FileStatus::Type aType = aFileStatus.getFileType();
2739 bool bFolder = isFolder( aType );
2740 if( !bFolder )
2742 continue;
2746 aPath = aFileStatus.getFileName();
2749 bool bMatch = implCheckWildcard(aPath, rRTLData);
2750 if( !bMatch )
2752 continue;
2754 break;
2757 rPar.Get(0)->PutString(aPath);
2763 void SbRtl_GetAttr(StarBASIC *, SbxArray & rPar, bool)
2765 if (rPar.Count() == 2)
2767 sal_Int16 nFlags = SbAttributes::NORMAL;
2769 // In Windows, we want to use Windows API to get the file attributes
2770 // for VBA interoperability.
2771 #if defined(_WIN32)
2772 if( SbiRuntime::isVBAEnabled() )
2774 OUString aPathURL = getFullPath(rPar.Get(1)->GetOUString());
2775 OUString aPath;
2776 FileBase::getSystemPathFromFileURL( aPathURL, aPath );
2777 DWORD nRealFlags = GetFileAttributesW (o3tl::toW(aPath.getStr()));
2778 if (nRealFlags != 0xffffffff)
2780 if (nRealFlags == FILE_ATTRIBUTE_NORMAL)
2782 nRealFlags = 0;
2784 nFlags = static_cast<sal_Int16>(nRealFlags);
2786 else
2788 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
2790 rPar.Get(0)->PutInteger(nFlags);
2792 return;
2794 #endif
2796 if( hasUno() )
2798 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2799 if( xSFI.is() )
2803 OUString aPath = getFullPath(rPar.Get(1)->GetOUString());
2804 bool bExists = false;
2805 try { bExists = xSFI->exists( aPath ); }
2806 catch(const Exception & ) {}
2807 if( !bExists )
2809 return StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
2812 bool bReadOnly = xSFI->isReadOnly( aPath );
2813 bool bHidden = xSFI->isHidden( aPath );
2814 bool bDirectory = xSFI->isFolder( aPath );
2815 if( bReadOnly )
2817 nFlags |= SbAttributes::READONLY;
2819 if( bHidden )
2821 nFlags |= SbAttributes::HIDDEN;
2823 if( bDirectory )
2825 nFlags |= SbAttributes::DIRECTORY;
2828 catch(const Exception & )
2830 StarBASIC::Error( ERRCODE_IO_GENERAL );
2834 else
2836 DirectoryItem aItem;
2837 (void)DirectoryItem::get(getFullPath(rPar.Get(1)->GetOUString()), aItem);
2838 FileStatus aFileStatus( osl_FileStatus_Mask_Attributes | osl_FileStatus_Mask_Type );
2839 (void)aItem.getFileStatus( aFileStatus );
2840 sal_uInt64 nAttributes = aFileStatus.getAttributes();
2841 bool bReadOnly = (nAttributes & osl_File_Attribute_ReadOnly) != 0;
2843 FileStatus::Type aType = aFileStatus.getFileType();
2844 bool bDirectory = isFolder( aType );
2845 if( bReadOnly )
2847 nFlags |= SbAttributes::READONLY;
2849 if( bDirectory )
2851 nFlags |= SbAttributes::DIRECTORY;
2854 rPar.Get(0)->PutInteger(nFlags);
2856 else
2858 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2863 void SbRtl_FileDateTime(StarBASIC *, SbxArray & rPar, bool)
2865 if (rPar.Count() != 2)
2867 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2869 else
2871 OUString aPath = rPar.Get(1)->GetOUString();
2872 tools::Time aTime( tools::Time::EMPTY );
2873 Date aDate( Date::EMPTY );
2874 if( hasUno() )
2876 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2877 if( xSFI.is() )
2881 util::DateTime aUnoDT = xSFI->getDateTimeModified( aPath );
2882 aTime = tools::Time( aUnoDT );
2883 aDate = Date( aUnoDT );
2885 catch(const Exception & )
2887 StarBASIC::Error( ERRCODE_IO_GENERAL );
2891 else
2893 bool bSuccess = false;
2896 DirectoryItem aItem;
2897 if (DirectoryItem::get( getFullPath( aPath ), aItem ) != FileBase::E_None)
2898 break;
2900 FileStatus aFileStatus( osl_FileStatus_Mask_ModifyTime );
2901 if (aItem.getFileStatus( aFileStatus ) != FileBase::E_None)
2902 break;
2904 TimeValue aTimeVal = aFileStatus.getModifyTime();
2905 oslDateTime aDT;
2906 if (!osl_getDateTimeFromTimeValue( &aTimeVal, &aDT ))
2907 // Strictly spoken this is not an i/o error but some other failure.
2908 break;
2910 aTime = tools::Time( aDT.Hours, aDT.Minutes, aDT.Seconds, aDT.NanoSeconds );
2911 aDate = Date( aDT.Day, aDT.Month, aDT.Year );
2912 bSuccess = true;
2914 while(false);
2916 if (!bSuccess)
2917 StarBASIC::Error( ERRCODE_IO_GENERAL );
2920 // An empty date shall not result in a formatted null-date (1899-12-30
2921 // or 1900-01-01) or even worse -0001-12-03 or some such due to how
2922 // GetDayDiff() treats things. There should be an error set in this
2923 // case anyway because of a missing file or other error above, but... so
2924 // do not even bother to use the number formatter.
2925 OUString aRes;
2926 if (aDate.IsEmpty())
2928 aRes = "0000-00-00 00:00:00";
2930 else
2932 double fSerial = static_cast<double>(GetDayDiff( aDate ));
2933 tools::Long nSeconds = aTime.GetHour();
2934 nSeconds *= 3600;
2935 nSeconds += aTime.GetMin() * 60;
2936 nSeconds += aTime.GetSec();
2937 double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
2938 fSerial += nDays;
2940 const Color* pCol;
2942 std::shared_ptr<SvNumberFormatter> pFormatter;
2943 sal_uInt32 nIndex;
2944 if( GetSbData()->pInst )
2946 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2947 nIndex = GetSbData()->pInst->GetStdDateTimeIdx();
2949 else
2951 sal_uInt32 n;
2952 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, nIndex );
2955 pFormatter->GetOutputString( fSerial, nIndex, aRes, &pCol );
2957 rPar.Get(0)->PutString(aRes);
2962 void SbRtl_EOF(StarBASIC *, SbxArray & rPar, bool)
2964 // No changes for UCB
2965 if (rPar.Count() != 2)
2967 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2969 else
2971 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
2972 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
2973 SbiStream* pSbStrm = pIO->GetStream( nChannel );
2974 if ( !pSbStrm )
2976 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
2978 bool beof;
2979 SvStream* pSvStrm = pSbStrm->GetStrm();
2980 if ( pSbStrm->IsText() )
2982 char cBla;
2983 (*pSvStrm).ReadChar( cBla ); // can we read another character?
2984 beof = pSvStrm->eof();
2985 if ( !beof )
2987 pSvStrm->SeekRel( -1 );
2990 else
2992 beof = pSvStrm->eof(); // for binary data!
2994 rPar.Get(0)->PutBool(beof);
2998 void SbRtl_FileAttr(StarBASIC *, SbxArray & rPar, bool)
3000 // No changes for UCB
3001 // #57064 Although this function doesn't operate with DirEntry, it is
3002 // not touched by the adjustment to virtual URLs, as it only works on
3003 // already opened files and the name doesn't matter there.
3005 if (rPar.Count() != 3)
3007 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3009 else
3011 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3012 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3013 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3014 if ( !pSbStrm )
3016 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3018 sal_Int16 nRet;
3019 if (rPar.Get(2)->GetInteger() == 1)
3021 nRet = static_cast<sal_Int16>(pSbStrm->GetMode());
3023 else
3025 nRet = 0; // System file handle not supported
3027 rPar.Get(0)->PutInteger(nRet);
3030 void SbRtl_Loc(StarBASIC *, SbxArray & rPar, bool)
3032 // No changes for UCB
3033 if (rPar.Count() != 2)
3035 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3037 else
3039 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3040 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3041 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3042 if ( !pSbStrm )
3044 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3046 SvStream* pSvStrm = pSbStrm->GetStrm();
3047 std::size_t nPos;
3048 if( pSbStrm->IsRandom())
3050 short nBlockLen = pSbStrm->GetBlockLen();
3051 nPos = nBlockLen ? (pSvStrm->Tell() / nBlockLen) : 0;
3052 nPos++; // block positions starting at 1
3054 else if ( pSbStrm->IsText() )
3056 nPos = pSbStrm->GetLine();
3058 else if( pSbStrm->IsBinary() )
3060 nPos = pSvStrm->Tell();
3062 else if ( pSbStrm->IsSeq() )
3064 nPos = ( pSvStrm->Tell()+1 ) / 128;
3066 else
3068 nPos = pSvStrm->Tell();
3070 rPar.Get(0)->PutLong(static_cast<sal_Int32>(nPos));
3074 void SbRtl_Lof(StarBASIC *, SbxArray & rPar, bool)
3076 // No changes for UCB
3077 if (rPar.Count() != 2)
3079 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3081 else
3083 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3084 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3085 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3086 if ( !pSbStrm )
3088 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3090 SvStream* pSvStrm = pSbStrm->GetStrm();
3091 sal_uInt64 const nLen = pSvStrm->TellEnd();
3092 rPar.Get(0)->PutLong(static_cast<sal_Int32>(nLen));
3097 void SbRtl_Seek(StarBASIC *, SbxArray & rPar, bool)
3099 // No changes for UCB
3100 int nArgs = static_cast<int>(rPar.Count());
3101 if ( nArgs < 2 || nArgs > 3 )
3103 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3105 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3106 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3107 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3108 if ( !pSbStrm )
3110 return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3112 SvStream* pStrm = pSbStrm->GetStrm();
3114 if ( nArgs == 2 ) // Seek-Function
3116 sal_uInt64 nPos = pStrm->Tell();
3117 if( pSbStrm->IsRandom() )
3119 nPos = nPos / pSbStrm->GetBlockLen();
3121 nPos++; // Basic counts from 1
3122 rPar.Get(0)->PutLong(static_cast<sal_Int32>(nPos));
3124 else // Seek-Statement
3126 sal_Int32 nPos = rPar.Get(2)->GetLong();
3127 if ( nPos < 1 )
3129 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3131 nPos--; // Basic counts from 1, SvStreams count from 0
3132 pSbStrm->SetExpandOnWriteTo( 0 );
3133 if ( pSbStrm->IsRandom() )
3135 nPos *= pSbStrm->GetBlockLen();
3137 pStrm->Seek( static_cast<sal_uInt64>(nPos) );
3138 pSbStrm->SetExpandOnWriteTo( nPos );
3142 void SbRtl_Format(StarBASIC *, SbxArray & rPar, bool)
3144 const sal_uInt32 nArgCount = rPar.Count();
3145 if ( nArgCount < 2 || nArgCount > 3 )
3147 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3149 else
3151 OUString aResult;
3152 if( nArgCount == 2 )
3154 rPar.Get(1)->Format(aResult);
3156 else
3158 OUString aFmt(rPar.Get(2)->GetOUString());
3159 rPar.Get(1)->Format(aResult, &aFmt);
3161 rPar.Get(0)->PutString(aResult);
3165 static bool IsMissing(SbxArray& rPar, const sal_uInt32 i)
3167 const sal_uInt32 nArgCount = rPar.Count();
3168 if (nArgCount <= i)
3169 return true;
3171 SbxVariable* aPar = rPar.Get(i);
3172 return (aPar->GetType() == SbxERROR && SbiRuntime::IsMissing(aPar, 1));
3175 static sal_Int16 GetOptionalIntegerParamOrDefault(SbxArray& rPar, const sal_uInt32 i,
3176 const sal_Int16 defaultValue)
3178 return IsMissing(rPar, i) ? defaultValue : rPar.Get(i)->GetInteger();
3181 static OUString GetOptionalOUStringParamOrDefault(SbxArray& rPar, const sal_uInt32 i,
3182 const OUString& defaultValue)
3184 return IsMissing(rPar, i) ? defaultValue : rPar.Get(i)->GetOUString();
3187 static void lcl_FormatNumberPercent(SbxArray& rPar, bool isPercent)
3189 const sal_uInt32 nArgCount = rPar.Count();
3190 if (nArgCount < 2 || nArgCount > 6)
3192 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3195 // The UI locale never changes -> we can use static value here
3196 static const LocaleDataWrapper localeData(Application::GetSettings().GetUILanguageTag());
3197 sal_Int16 nNumDigitsAfterDecimal = -1;
3198 if (nArgCount > 2 && !rPar.Get(2)->IsEmpty())
3200 nNumDigitsAfterDecimal = rPar.Get(2)->GetInteger();
3201 if (nNumDigitsAfterDecimal < -1)
3203 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3205 else if (nNumDigitsAfterDecimal > 255)
3206 nNumDigitsAfterDecimal %= 256;
3208 if (nNumDigitsAfterDecimal == -1)
3209 nNumDigitsAfterDecimal = LocaleDataWrapper::getNumDigits();
3211 bool bIncludeLeadingDigit = LocaleDataWrapper::isNumLeadingZero();
3212 if (nArgCount > 3 && !rPar.Get(3)->IsEmpty())
3214 switch (rPar.Get(3)->GetInteger())
3216 case ooo::vba::VbTriState::vbFalse:
3217 bIncludeLeadingDigit = false;
3218 break;
3219 case ooo::vba::VbTriState::vbTrue:
3220 bIncludeLeadingDigit = true;
3221 break;
3222 case ooo::vba::VbTriState::vbUseDefault:
3223 // do nothing;
3224 break;
3225 default:
3226 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3230 bool bUseParensForNegativeNumbers = false;
3231 if (nArgCount > 4 && !rPar.Get(4)->IsEmpty())
3233 switch (rPar.Get(4)->GetInteger())
3235 case ooo::vba::VbTriState::vbFalse:
3236 case ooo::vba::VbTriState::vbUseDefault:
3237 // do nothing
3238 break;
3239 case ooo::vba::VbTriState::vbTrue:
3240 bUseParensForNegativeNumbers = true;
3241 break;
3242 default:
3243 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3247 bool bGroupDigits = false;
3248 if (nArgCount > 5 && !rPar.Get(5)->IsEmpty())
3250 switch (rPar.Get(5)->GetInteger())
3252 case ooo::vba::VbTriState::vbFalse:
3253 case ooo::vba::VbTriState::vbUseDefault:
3254 // do nothing
3255 break;
3256 case ooo::vba::VbTriState::vbTrue:
3257 bGroupDigits = true;
3258 break;
3259 default:
3260 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3264 double fVal = rPar.Get(1)->GetDouble();
3265 if (isPercent)
3266 fVal *= 100;
3267 const bool bNegative = fVal < 0;
3268 if (bNegative)
3269 fVal = fabs(fVal); // Always work with non-negatives, to easily handle leading zero
3271 static const sal_Unicode decSep = localeData.getNumDecimalSep().toChar();
3272 OUStringBuffer aResult;
3273 rtl::math::doubleToUStringBuffer(aResult,
3274 fVal, rtl_math_StringFormat_F, nNumDigitsAfterDecimal, decSep,
3275 bGroupDigits ? localeData.getDigitGrouping().getConstArray() : nullptr,
3276 localeData.getNumThousandSep().toChar());
3278 if (!bIncludeLeadingDigit && aResult.getLength() > 1)
3279 aResult.stripStart('0');
3281 if (nNumDigitsAfterDecimal > 0)
3283 const sal_Int32 nSepPos = aResult.indexOf(decSep);
3285 // VBA allows up to 255 digits; rtl::math::doubleToUString outputs up to 15 digits
3286 // for ~small numbers, so pad them as appropriate.
3287 if (nSepPos >= 0)
3288 comphelper::string::padToLength(aResult, nSepPos + nNumDigitsAfterDecimal + 1, '0');
3291 if (bNegative)
3293 if (bUseParensForNegativeNumbers)
3294 aResult.insert(0, '(').append(')');
3295 else
3296 aResult.insert(0, '-');
3298 if (isPercent)
3299 aResult.append('%');
3300 rPar.Get(0)->PutString(aResult.makeStringAndClear());
3303 // https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatnumber-function
3304 void SbRtl_FormatNumber(StarBASIC*, SbxArray& rPar, bool)
3306 return lcl_FormatNumberPercent(rPar, false);
3309 // https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatpercent-function
3310 void SbRtl_FormatPercent(StarBASIC*, SbxArray& rPar, bool)
3312 return lcl_FormatNumberPercent(rPar, true);
3315 namespace {
3317 // note: BASIC does not use comphelper::random, because
3318 // Randomize(int) must be supported and should not affect non-BASIC random use
3319 struct RandomNumberGenerator
3321 std::mt19937 global_rng;
3323 RandomNumberGenerator()
3327 std::random_device rd;
3328 // initialises the state of the global random number generator
3329 // should only be called once.
3330 // (note, a few std::variate_generator<> (like normal) have their
3331 // own state which would need a reset as well to guarantee identical
3332 // sequence of numbers, e.g. via myrand.distribution().reset())
3333 global_rng.seed(rd() ^ time(nullptr));
3335 catch (std::runtime_error& e)
3337 SAL_WARN("basic", "Using std::random_device failed: " << e.what());
3338 global_rng.seed(time(nullptr));
3343 RandomNumberGenerator& theRandomNumberGenerator()
3345 static RandomNumberGenerator theGenerator;
3346 return theGenerator;
3351 void SbRtl_Randomize(StarBASIC *, SbxArray & rPar, bool)
3353 if (rPar.Count() > 2)
3355 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3357 if (rPar.Count() == 2)
3359 int nSeed = static_cast<int>(rPar.Get(1)->GetInteger());
3360 theRandomNumberGenerator().global_rng.seed(nSeed);
3362 // without parameter, no need to do anything - RNG is seeded at first use
3365 void SbRtl_Rnd(StarBASIC *, SbxArray & rPar, bool)
3367 if (rPar.Count() > 2)
3369 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3371 else
3373 std::uniform_real_distribution<double> dist(0.0, 1.0);
3374 double const tmp(dist(theRandomNumberGenerator().global_rng));
3375 rPar.Get(0)->PutDouble(tmp);
3380 // Syntax: Shell("Path",[ Window-Style,[ "Params", [ bSync = sal_False ]]])
3381 // WindowStyles (VBA compatible):
3382 // 2 == Minimized
3383 // 3 == Maximized
3384 // 10 == Full-Screen (text mode applications OS/2, WIN95, WNT)
3385 // HACK: The WindowStyle will be passed to
3386 // Application::StartApp in Creator. Format: "xxxx2"
3389 void SbRtl_Shell(StarBASIC *, SbxArray & rPar, bool)
3391 const sal_uInt32 nArgCount = rPar.Count();
3392 if ( nArgCount < 2 || nArgCount > 5 )
3394 rPar.Get(0)->PutLong(0);
3395 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3397 else
3399 oslProcessOption nOptions = osl_Process_SEARCHPATH | osl_Process_DETACHED;
3401 OUString aCmdLine = rPar.Get(1)->GetOUString();
3402 // attach additional parameters - everything must be parsed anyway
3403 if( nArgCount >= 4 )
3405 OUString tmp = rPar.Get(3)->GetOUString().trim();
3406 if (!tmp.isEmpty())
3408 aCmdLine += " " + tmp;
3411 else if( aCmdLine.isEmpty() )
3413 // avoid special treatment (empty list)
3414 aCmdLine += " ";
3416 sal_Int32 nLen = aCmdLine.getLength();
3418 // #55735 if there are parameters, they have to be separated
3419 // #72471 also separate the single parameters
3420 std::vector<OUString> aTokenVector;
3421 OUString aToken;
3422 sal_Int32 i = 0;
3423 sal_Unicode c;
3424 while( i < nLen )
3426 for ( ;; ++i )
3428 c = aCmdLine[ i ];
3429 if ( c != ' ' && c != '\t' )
3431 break;
3435 if( c == '\"' || c == '\'' )
3437 sal_Int32 iFoundPos = aCmdLine.indexOf( c, i + 1 );
3439 if( iFoundPos < 0 )
3441 aToken = aCmdLine.copy( i);
3442 i = nLen;
3444 else
3446 aToken = aCmdLine.copy( i + 1, (iFoundPos - i - 1) );
3447 i = iFoundPos + 1;
3450 else
3452 sal_Int32 iFoundSpacePos = aCmdLine.indexOf( ' ', i );
3453 sal_Int32 iFoundTabPos = aCmdLine.indexOf( '\t', i );
3454 sal_Int32 iFoundPos = iFoundSpacePos >= 0 ? iFoundTabPos >= 0 ? std::min( iFoundSpacePos, iFoundTabPos ) : iFoundSpacePos : -1;
3456 if( iFoundPos < 0 )
3458 aToken = aCmdLine.copy( i );
3459 i = nLen;
3461 else
3463 aToken = aCmdLine.copy( i, (iFoundPos - i) );
3464 i = iFoundPos;
3468 // insert into the list
3469 aTokenVector.push_back( aToken );
3471 // #55735 / #72471 end
3473 sal_Int16 nWinStyle = 0;
3474 if( nArgCount >= 3 )
3476 nWinStyle = rPar.Get(2)->GetInteger();
3477 switch( nWinStyle )
3479 case 2:
3480 nOptions |= osl_Process_MINIMIZED;
3481 break;
3482 case 3:
3483 nOptions |= osl_Process_MAXIMIZED;
3484 break;
3485 case 10:
3486 nOptions |= osl_Process_FULLSCREEN;
3487 break;
3490 bool bSync = false;
3491 if( nArgCount >= 5 )
3493 bSync = rPar.Get(4)->GetBool();
3495 if( bSync )
3497 nOptions |= osl_Process_WAIT;
3501 // #72471 work parameter(s) up
3502 std::vector<OUString>::const_iterator iter = aTokenVector.begin();
3503 OUString aOUStrProgURL = getFullPath( *iter );
3505 ++iter;
3507 sal_uInt16 nParamCount = sal::static_int_cast< sal_uInt16 >(aTokenVector.size() - 1 );
3508 std::unique_ptr<rtl_uString*[]> pParamList;
3509 if( nParamCount )
3511 pParamList.reset( new rtl_uString*[nParamCount]);
3512 for(int iVector = 0; iter != aTokenVector.end(); ++iVector, ++iter)
3514 const OUString& rParamStr = *iter;
3515 pParamList[iVector] = nullptr;
3516 rtl_uString_assign(&(pParamList[iVector]), rParamStr.pData);
3520 oslProcess pApp;
3521 bool bSucc = osl_executeProcess(
3522 aOUStrProgURL.pData,
3523 pParamList.get(),
3524 nParamCount,
3525 nOptions,
3526 nullptr,
3527 nullptr,
3528 nullptr, 0,
3529 &pApp ) == osl_Process_E_None;
3531 // 53521 only free process handle on success
3532 if (bSucc)
3534 osl_freeProcessHandle( pApp );
3537 for(int j = 0; j < nParamCount; ++j)
3539 rtl_uString_release(pParamList[j]);
3542 if( !bSucc )
3544 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
3546 else
3548 rPar.Get(0)->PutLong(0);
3553 void SbRtl_VarType(StarBASIC *, SbxArray & rPar, bool)
3555 if (rPar.Count() != 2)
3557 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3559 else
3561 SbxDataType eType = rPar.Get(1)->GetType();
3562 rPar.Get(0)->PutInteger(static_cast<sal_Int16>(eType));
3566 // Exported function
3567 const OUString & getBasicTypeName( SbxDataType eType )
3569 static constexpr OUString pTypeNames[] =
3571 u"Empty"_ustr, // SbxEMPTY
3572 u"Null"_ustr, // SbxNULL
3573 u"Integer"_ustr, // SbxINTEGER
3574 u"Long"_ustr, // SbxLONG
3575 u"Single"_ustr, // SbxSINGLE
3576 u"Double"_ustr, // SbxDOUBLE
3577 u"Currency"_ustr, // SbxCURRENCY
3578 u"Date"_ustr, // SbxDATE
3579 u"String"_ustr, // SbxSTRING
3580 u"Object"_ustr, // SbxOBJECT
3581 u"Error"_ustr, // SbxERROR
3582 u"Boolean"_ustr, // SbxBOOL
3583 u"Variant"_ustr, // SbxVARIANT
3584 u"DataObject"_ustr, // SbxDATAOBJECT
3585 u"Unknown Type"_ustr,
3586 u"Unknown Type"_ustr,
3587 u"Char"_ustr, // SbxCHAR
3588 u"Byte"_ustr, // SbxBYTE
3589 u"UShort"_ustr, // SbxUSHORT
3590 u"ULong"_ustr, // SbxULONG
3591 u"Long64"_ustr, // SbxLONG64
3592 u"ULong64"_ustr, // SbxULONG64
3593 u"Int"_ustr, // SbxINT
3594 u"UInt"_ustr, // SbxUINT
3595 u"Void"_ustr, // SbxVOID
3596 u"HResult"_ustr, // SbxHRESULT
3597 u"Pointer"_ustr, // SbxPOINTER
3598 u"DimArray"_ustr, // SbxDIMARRAY
3599 u"CArray"_ustr, // SbxCARRAY
3600 u"Userdef"_ustr, // SbxUSERDEF
3601 u"Lpstr"_ustr, // SbxLPSTR
3602 u"Lpwstr"_ustr, // SbxLPWSTR
3603 u"Unknown Type"_ustr, // SbxCoreSTRING
3604 u"WString"_ustr, // SbxWSTRING
3605 u"WChar"_ustr, // SbxWCHAR
3606 u"Int64"_ustr, // SbxSALINT64
3607 u"UInt64"_ustr, // SbxSALUINT64
3608 u"Decimal"_ustr, // SbxDECIMAL
3611 size_t nPos = static_cast<size_t>(eType) & 0x0FFF;
3612 const size_t nTypeNameCount = std::size( pTypeNames );
3613 if ( nPos >= nTypeNameCount )
3615 nPos = nTypeNameCount - 1;
3617 return pTypeNames[nPos];
3620 static OUString getObjectTypeName( SbxVariable* pVar )
3622 OUString sRet( u"Object"_ustr );
3623 if ( pVar )
3625 SbxBase* pBaseObj = pVar->GetObject();
3626 if( !pBaseObj )
3628 sRet = "Nothing";
3630 else
3632 SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pVar );
3633 if ( !pUnoObj )
3635 pUnoObj = dynamic_cast<SbUnoObject*>( pBaseObj );
3637 if ( pUnoObj )
3639 Any aObj = pUnoObj->getUnoAny();
3640 // For upstreaming unless we start to build oovbaapi by default
3641 // we need to get detect the vba-ness of the object in some
3642 // other way
3643 // note: Automation objects do not support XServiceInfo
3644 uno::Reference< XServiceInfo > xServInfo( aObj, uno::UNO_QUERY );
3645 if ( xServInfo.is() )
3647 // is this a VBA object ?
3648 Sequence< OUString > sServices = xServInfo->getSupportedServiceNames();
3649 if ( sServices.hasElements() )
3651 sRet = sServices[ 0 ];
3654 else
3656 uno::Reference< bridge::oleautomation::XAutomationObject > xAutoMation( aObj, uno::UNO_QUERY );
3657 if ( xAutoMation.is() )
3659 uno::Reference< script::XInvocation > xInv( aObj, uno::UNO_QUERY );
3660 if ( xInv.is() )
3664 xInv->getValue( u"$GetTypeName"_ustr ) >>= sRet;
3666 catch(const Exception& )
3672 sal_Int32 nDot = sRet.lastIndexOf( '.' );
3673 if ( nDot != -1 && nDot < sRet.getLength() )
3675 sRet = sRet.copy( nDot + 1 );
3680 return sRet;
3683 void SbRtl_TypeName(StarBASIC *, SbxArray & rPar, bool)
3685 if (rPar.Count() != 2)
3687 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3689 else
3691 SbxDataType eType = rPar.Get(1)->GetType();
3692 bool bIsArray = ( ( eType & SbxARRAY ) != 0 );
3694 OUString aRetStr;
3695 if ( SbiRuntime::isVBAEnabled() && eType == SbxOBJECT )
3697 aRetStr = getObjectTypeName(rPar.Get(1));
3699 else
3701 aRetStr = getBasicTypeName( eType );
3703 if( bIsArray )
3705 aRetStr += "()";
3707 rPar.Get(0)->PutString(aRetStr);
3711 void SbRtl_Len(StarBASIC *, SbxArray & rPar, bool)
3713 if (rPar.Count() != 2)
3715 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3717 else
3719 const OUString aStr = rPar.Get(1)->GetOUString();
3720 rPar.Get(0)->PutLong(aStr.getLength());
3724 void SbRtl_DDEInitiate(StarBASIC *, SbxArray & rPar, bool)
3726 int nArgs = static_cast<int>(rPar.Count());
3727 if ( nArgs != 3 )
3729 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3731 const OUString aApp = rPar.Get(1)->GetOUString();
3732 const OUString aTopic = rPar.Get(2)->GetOUString();
3734 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3735 size_t nChannel;
3736 ErrCode nDdeErr = pDDE->Initiate( aApp, aTopic, nChannel );
3737 if( nDdeErr )
3739 StarBASIC::Error( nDdeErr );
3741 else
3743 rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nChannel));
3747 void SbRtl_DDETerminate(StarBASIC *, SbxArray & rPar, bool)
3749 rPar.Get(0)->PutEmpty();
3750 int nArgs = static_cast<int>(rPar.Count());
3751 if ( nArgs != 2 )
3753 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3754 return;
3756 size_t nChannel = rPar.Get(1)->GetInteger();
3757 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3758 ErrCode nDdeErr = pDDE->Terminate( nChannel );
3759 if( nDdeErr )
3761 StarBASIC::Error( nDdeErr );
3765 void SbRtl_DDETerminateAll(StarBASIC *, SbxArray & rPar, bool)
3767 rPar.Get(0)->PutEmpty();
3768 int nArgs = static_cast<int>(rPar.Count());
3769 if ( nArgs != 1 )
3771 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3774 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3775 ErrCode nDdeErr = pDDE->TerminateAll();
3776 if( nDdeErr )
3778 StarBASIC::Error( nDdeErr );
3782 void SbRtl_DDERequest(StarBASIC *, SbxArray & rPar, bool)
3784 int nArgs = static_cast<int>(rPar.Count());
3785 if ( nArgs != 3 )
3787 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3789 size_t nChannel = rPar.Get(1)->GetInteger();
3790 const OUString aItem = rPar.Get(2)->GetOUString();
3791 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3792 OUString aResult;
3793 ErrCode nDdeErr = pDDE->Request( nChannel, aItem, aResult );
3794 if( nDdeErr )
3796 StarBASIC::Error( nDdeErr );
3798 else
3800 rPar.Get(0)->PutString(aResult);
3804 void SbRtl_DDEExecute(StarBASIC *, SbxArray & rPar, bool)
3806 rPar.Get(0)->PutEmpty();
3807 int nArgs = static_cast<int>(rPar.Count());
3808 if ( nArgs != 3 )
3810 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3812 size_t nChannel = rPar.Get(1)->GetInteger();
3813 const OUString aCommand = rPar.Get(2)->GetOUString();
3814 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3815 ErrCode nDdeErr = pDDE->Execute( nChannel, aCommand );
3816 if( nDdeErr )
3818 StarBASIC::Error( nDdeErr );
3822 void SbRtl_DDEPoke(StarBASIC *, SbxArray & rPar, bool)
3824 rPar.Get(0)->PutEmpty();
3825 int nArgs = static_cast<int>(rPar.Count());
3826 if ( nArgs != 4 )
3828 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3830 size_t nChannel = rPar.Get(1)->GetInteger();
3831 const OUString aItem = rPar.Get(2)->GetOUString();
3832 const OUString aData = rPar.Get(3)->GetOUString();
3833 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3834 ErrCode nDdeErr = pDDE->Poke( nChannel, aItem, aData );
3835 if( nDdeErr )
3837 StarBASIC::Error( nDdeErr );
3842 void SbRtl_FreeFile(StarBASIC *, SbxArray & rPar, bool)
3844 if (rPar.Count() != 1)
3846 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3848 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3849 short nChannel = 1;
3850 while( nChannel < CHANNELS )
3852 SbiStream* pStrm = pIO->GetStream( nChannel );
3853 if( !pStrm )
3855 rPar.Get(0)->PutInteger(nChannel);
3856 return;
3858 nChannel++;
3860 StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES );
3863 void SbRtl_LBound(StarBASIC *, SbxArray & rPar, bool)
3865 const sal_uInt32 nParCount = rPar.Count();
3866 if ( nParCount != 3 && nParCount != 2 )
3867 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3869 SbxBase* pParObj = rPar.Get(1)->GetObject();
3870 SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
3871 if( !pArr )
3872 return StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
3874 sal_Int32 nLower, nUpper;
3875 short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
3876 if (!pArr->GetDim(nDim, nLower, nUpper))
3877 return StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
3878 rPar.Get(0)->PutLong(nLower);
3881 void SbRtl_UBound(StarBASIC *, SbxArray & rPar, bool)
3883 const sal_uInt32 nParCount = rPar.Count();
3884 if ( nParCount != 3 && nParCount != 2 )
3885 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3887 SbxBase* pParObj = rPar.Get(1)->GetObject();
3888 SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
3889 if( !pArr )
3890 return StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
3892 sal_Int32 nLower, nUpper;
3893 short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
3894 if (!pArr->GetDim(nDim, nLower, nUpper))
3895 return StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
3896 rPar.Get(0)->PutLong(nUpper);
3899 void SbRtl_RGB(StarBASIC *, SbxArray & rPar, bool)
3901 if (rPar.Count() != 4)
3902 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3904 sal_Int32 nRed = rPar.Get(1)->GetInteger() & 0xFF;
3905 sal_Int32 nGreen = rPar.Get(2)->GetInteger() & 0xFF;
3906 sal_Int32 nBlue = rPar.Get(3)->GetInteger() & 0xFF;
3907 sal_Int32 nRGB;
3909 SbiInstance* pInst = GetSbData()->pInst;
3910 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
3911 // See discussion in tdf#145725, here's the quotation from a link indicated in the bugtracker
3912 // which explains why we need to manage RGB differently according to VB compatibility
3913 // "In other words, the individual color components are stored in the opposite order one would expect.
3914 // VB stores the red color component in the low-order byte of the long integer's low-order word,
3915 // 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"
3916 if( bCompatibility )
3918 nRGB = (nBlue << 16) | (nGreen << 8) | nRed;
3920 else
3922 nRGB = (nRed << 16) | (nGreen << 8) | nBlue;
3924 rPar.Get(0)->PutLong(nRGB);
3927 void SbRtl_QBColor(StarBASIC *, SbxArray & rPar, bool)
3929 static const sal_Int32 pRGB[] =
3931 0x000000,
3932 0x800000,
3933 0x008000,
3934 0x808000,
3935 0x000080,
3936 0x800080,
3937 0x008080,
3938 0xC0C0C0,
3939 0x808080,
3940 0xFF0000,
3941 0x00FF00,
3942 0xFFFF00,
3943 0x0000FF,
3944 0xFF00FF,
3945 0x00FFFF,
3946 0xFFFFFF,
3949 if (rPar.Count() != 2)
3951 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3954 sal_Int16 nCol = rPar.Get(1)->GetInteger();
3955 if( nCol < 0 || nCol > 15 )
3957 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3959 sal_Int32 nRGB = pRGB[ nCol ];
3960 rPar.Get(0)->PutLong(nRGB);
3963 static std::vector<sal_uInt8> byteArray2Vec(SbxArray* pArr)
3965 std::vector<sal_uInt8> result;
3966 if (pArr)
3968 const sal_uInt32 nCount = pArr->Count();
3969 result.reserve(nCount + 1); // to avoid reallocation when padding in vbFromUnicode
3970 for (sal_uInt32 i = 0; i < nCount; i++)
3971 result.push_back(pArr->Get(i)->GetByte());
3973 return result;
3976 // Makes sure to get the byte array if passed, or the string converted to the bytes using
3977 // StringToByteArray in basic/source/sbx/sbxstr.cxx
3978 static std::vector<sal_uInt8> getByteArray(SbxValue& val)
3980 if (val.GetFullType() == SbxOBJECT)
3981 if (auto pObj = val.GetObject())
3982 if (pObj->GetType() == (SbxARRAY | SbxBYTE))
3983 if (auto pArr = dynamic_cast<SbxArray*>(pObj))
3984 return byteArray2Vec(pArr);
3986 // Convert to string
3987 tools::SvRef<SbxValue> pStringValue(new SbxValue(SbxSTRING));
3988 *pStringValue = val;
3990 // Convert string to byte array
3991 tools::SvRef<SbxValue> pValue(new SbxValue(SbxOBJECT));
3992 pValue->PutObject(new SbxArray(SbxBYTE));
3993 *pValue = *pStringValue; // Does the magic of conversion of strings to byte arrays
3994 return byteArray2Vec(dynamic_cast<SbxArray*>(pValue->GetObject()));
3997 // StrConv(string, conversion, LCID)
3998 void SbRtl_StrConv(StarBASIC *, SbxArray & rPar, bool)
4000 const sal_uInt32 nArgCount = rPar.Count() - 1;
4001 if( nArgCount < 2 || nArgCount > 3 )
4003 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4006 sal_Int32 nConversion = rPar.Get(2)->GetLong();
4007 LanguageType nLanguage = LANGUAGE_SYSTEM;
4008 if (nArgCount == 3)
4010 sal_Int32 lcid = rPar.Get(3)->GetLong();
4011 nLanguage = LanguageType(lcid);
4014 if (nConversion == ooo::vba::VbStrConv::vbUnicode) // This mode does not combine
4016 // Assume that the passed byte array is encoded in the defined encoding, convert to
4017 // UTF-16 and store as string. Passed strings are converted to byte array first.
4018 auto inArray = getByteArray(*rPar.Get(1));
4019 std::string_view s(reinterpret_cast<char*>(inArray.data()), inArray.size() / sizeof(char));
4020 const auto encoding = utl_getWinTextEncodingFromLangStr(LanguageTag(nLanguage).getBcp47());
4021 OUString aOUStr = OStringToOUString(s, encoding);
4022 rPar.Get(0)->PutString(aOUStr);
4023 return;
4026 if (nConversion == ooo::vba::VbStrConv::vbFromUnicode) // This mode does not combine
4028 // Assume that the passed byte array is UTF-16-encoded (system-endian), convert to specified
4029 // encoding and store as byte array. Passed strings are converted to byte array first.
4030 auto inArray = getByteArray(*rPar.Get(1));
4031 while (inArray.size() % sizeof(sal_Unicode))
4032 inArray.push_back('\0');
4033 std::u16string_view s(reinterpret_cast<sal_Unicode*>(inArray.data()),
4034 inArray.size() / sizeof(sal_Unicode));
4035 const auto encoding = utl_getWinTextEncodingFromLangStr(LanguageTag(nLanguage).getBcp47());
4036 OString aOStr = OUStringToOString(s, encoding);
4037 const sal_Int32 lb = IsBaseIndexOne() ? 1 : 0;
4038 const sal_Int32 ub = lb + aOStr.getLength() - 1;
4039 SbxDimArray* pArray = new SbxDimArray(SbxBYTE);
4040 pArray->unoAddDim(lb, ub);
4042 for (sal_Int32 i = 0; i < aOStr.getLength(); ++i)
4044 SbxVariable* pNew = new SbxVariable(SbxBYTE);
4045 pNew->PutByte(aOStr[i]);
4046 pArray->Put(pNew, i);
4049 SbxVariable* retVar = rPar.Get(0);
4050 SbxFlagBits nFlags = retVar->GetFlags();
4051 retVar->ResetFlag(SbxFlagBits::Fixed);
4052 retVar->PutObject(pArray);
4053 retVar->SetFlags(nFlags);
4054 retVar->SetParameters(nullptr);
4055 return;
4058 std::vector<TransliterationFlags> aTranslitSet;
4059 auto check = [&nConversion, &aTranslitSet](sal_Int32 conv, TransliterationFlags flag)
4061 if ((nConversion & conv) != conv)
4062 return false;
4064 aTranslitSet.push_back(flag);
4065 nConversion &= ~conv;
4066 return true;
4069 // Check mutually exclusive bits together
4071 if (!check(ooo::vba::VbStrConv::vbProperCase, TransliterationFlags::TITLE_CASE))
4072 if (!check(ooo::vba::VbStrConv::vbUpperCase, TransliterationFlags::LOWERCASE_UPPERCASE))
4073 check(ooo::vba::VbStrConv::vbLowerCase, TransliterationFlags::UPPERCASE_LOWERCASE);
4075 if (!check(ooo::vba::VbStrConv::vbWide, TransliterationFlags::HALFWIDTH_FULLWIDTH))
4076 check(ooo::vba::VbStrConv::vbNarrow, TransliterationFlags::FULLWIDTH_HALFWIDTH);
4078 if (!check(ooo::vba::VbStrConv::vbKatakana, TransliterationFlags::HIRAGANA_KATAKANA))
4079 check(ooo::vba::VbStrConv::vbHiragana, TransliterationFlags::KATAKANA_HIRAGANA);
4081 if (nConversion) // unknown / incorrectly combined bits
4082 return StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
4084 OUString aStr = rPar.Get(1)->GetOUString();
4085 if (!aStr.isEmpty() && !aTranslitSet.empty())
4087 const uno::Reference< uno::XComponentContext >& xContext = getProcessComponentContext();
4089 for (auto transliterationFlag : aTranslitSet)
4091 if (transliterationFlag == TransliterationFlags::TITLE_CASE)
4093 // TransliterationWrapper only handles the first character of the passed string
4094 // when handling TITLE_CASE; see Transliteration_titlecase::transliterateImpl in
4095 // i18npool/source/transliteration/transliteration_body.cxx
4096 CharClass aCharClass{ xContext, LanguageTag(nLanguage) };
4097 aStr = aCharClass.titlecase(aCharClass.lowercase(aStr));
4099 else
4101 utl::TransliterationWrapper aWrapper(xContext, transliterationFlag);
4102 aStr = aWrapper.transliterate(aStr, nLanguage, 0, aStr.getLength(), nullptr);
4107 rPar.Get(0)->PutString(aStr);
4111 void SbRtl_Beep(StarBASIC *, SbxArray & rPar, bool)
4113 if (rPar.Count() != 1)
4115 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4117 Sound::Beep();
4120 void SbRtl_Load(StarBASIC *, SbxArray & rPar, bool)
4122 if (rPar.Count() != 2)
4124 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4128 SbxBase* pObj = rPar.Get(1)->GetObject();
4129 if ( !pObj )
4130 return;
4132 if (SbUserFormModule* pModule = dynamic_cast<SbUserFormModule*>(pObj))
4134 pModule->Load();
4136 else if (SbxObject* pSbxObj = dynamic_cast<SbxObject*>(pObj))
4138 SbxVariable* pVar = pSbxObj->Find(u"Load"_ustr, SbxClassType::Method);
4139 if( pVar )
4141 pVar->GetInteger();
4146 void SbRtl_Unload(StarBASIC *, SbxArray & rPar, bool)
4148 rPar.Get(0)->PutEmpty();
4149 if (rPar.Count() != 2)
4151 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4155 SbxBase* pObj = rPar.Get(1)->GetObject();
4156 if ( !pObj )
4157 return;
4159 if (SbUserFormModule* pFormModule = dynamic_cast<SbUserFormModule*>(pObj))
4161 pFormModule->Unload();
4163 else if (SbxObject *pSbxObj = dynamic_cast<SbxObject*>(pObj))
4165 SbxVariable* pVar = pSbxObj->Find(u"Unload"_ustr, SbxClassType::Method);
4166 if( pVar )
4168 pVar->GetInteger();
4173 void SbRtl_LoadPicture(StarBASIC *, SbxArray & rPar, bool)
4175 if (rPar.Count() != 2)
4177 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4180 OUString aFileURL = getFullPath(rPar.Get(1)->GetOUString());
4181 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aFileURL, StreamMode::READ ));
4182 if( pStream )
4184 Bitmap aBmp;
4185 ReadDIB(aBmp, *pStream, true);
4186 BitmapEx aBitmapEx(aBmp);
4187 Graphic aGraphic(aBitmapEx);
4189 SbxObjectRef xRef = new SbStdPicture;
4190 static_cast<SbStdPicture*>(xRef.get())->SetGraphic( aGraphic );
4191 rPar.Get(0)->PutObject(xRef.get());
4195 void SbRtl_SavePicture(StarBASIC *, SbxArray & rPar, bool)
4197 rPar.Get(0)->PutEmpty();
4198 if (rPar.Count() != 3)
4200 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4203 SbxBase* pObj = rPar.Get(1)->GetObject();
4204 if (SbStdPicture *pPicture = dynamic_cast<SbStdPicture*>(pObj))
4206 SvFileStream aOStream(rPar.Get(2)->GetOUString(), StreamMode::WRITE | StreamMode::TRUNC);
4207 const Graphic& aGraphic = pPicture->GetGraphic();
4208 TypeSerializer aSerializer(aOStream);
4209 aSerializer.writeGraphic(aGraphic);
4213 void SbRtl_MsgBox(StarBASIC *, SbxArray & rPar, bool)
4215 const sal_uInt32 nArgCount = rPar.Count();
4216 if( nArgCount < 2 || nArgCount > 6 )
4218 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4221 // tdf#147529 - check for missing parameters
4222 if (IsMissing(rPar, 1))
4224 return StarBASIC::Error(ERRCODE_BASIC_NOT_OPTIONAL);
4227 // tdf#151012 - initialize optional parameters with their default values (number of buttons)
4228 sal_Int16 nType = GetOptionalIntegerParamOrDefault(rPar, 2, SbMB::OK);
4230 OUString aMsg = rPar.Get(1)->GetOUString();
4231 // tdf#151012 - initialize optional parameters with their default values (title of dialog box)
4232 OUString aTitle = GetOptionalOUStringParamOrDefault(rPar, 3, Application::GetDisplayName());
4234 sal_Int16 nDialogType = nType & (SbMB::ICONSTOP | SbMB::ICONQUESTION | SbMB::ICONINFORMATION);
4236 SolarMutexGuard aSolarGuard;
4237 weld::Widget* pParent = Application::GetDefDialogParent();
4239 VclMessageType eType = VclMessageType::Other;
4241 switch (nDialogType)
4243 case SbMB::ICONSTOP:
4244 eType = VclMessageType::Error;
4245 break;
4246 case SbMB::ICONQUESTION:
4247 eType = VclMessageType::Question;
4248 break;
4249 case SbMB::ICONEXCLAMATION:
4250 eType = VclMessageType::Warning;
4251 break;
4252 case SbMB::ICONINFORMATION:
4253 eType = VclMessageType::Info;
4254 break;
4257 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
4258 eType, VclButtonsType::NONE, aMsg, GetpApp()));
4260 std::vector<std::pair<StandardButtonType, sal_Int16>> buttons;
4261 switch (nType & 0x0F) // delete bits 4-16
4263 case SbMB::OK:
4264 default:
4265 buttons.emplace_back(StandardButtonType::OK, SbMB::Response::OK);
4266 break;
4267 case SbMB::OKCANCEL:
4268 buttons.emplace_back(StandardButtonType::OK, SbMB::Response::OK);
4269 buttons.emplace_back(StandardButtonType::Cancel, SbMB::Response::CANCEL);
4270 break;
4271 case SbMB::ABORTRETRYIGNORE:
4272 buttons.emplace_back(StandardButtonType::Abort, SbMB::Response::ABORT);
4273 buttons.emplace_back(StandardButtonType::Retry, SbMB::Response::RETRY);
4274 buttons.emplace_back(StandardButtonType::Ignore, SbMB::Response::IGNORE);
4275 break;
4276 case SbMB::YESNOCANCEL:
4277 buttons.emplace_back(StandardButtonType::Yes, SbMB::Response::YES);
4278 buttons.emplace_back(StandardButtonType::No, SbMB::Response::NO);
4279 buttons.emplace_back(StandardButtonType::Cancel, SbMB::Response::CANCEL);
4280 break;
4281 case SbMB::YESNO:
4282 buttons.emplace_back(StandardButtonType::Yes, SbMB::Response::YES);
4283 buttons.emplace_back(StandardButtonType::No, SbMB::Response::NO);
4284 break;
4285 case SbMB::RETRYCANCEL:
4286 buttons.emplace_back(StandardButtonType::Retry, SbMB::Response::RETRY);
4287 buttons.emplace_back(StandardButtonType::Cancel, SbMB::Response::CANCEL);
4288 break;
4291 for (auto [buttonType, buttonResponse] : buttons)
4292 xBox->add_button(GetStandardText(buttonType), buttonResponse);
4294 std::size_t default_button = 0;
4295 if (nType & SbMB::DEFBUTTON2)
4296 default_button = 1;
4297 else if (nType & SbMB::DEFBUTTON3)
4298 default_button = 2;
4299 xBox->set_default_response(buttons[std::min(default_button, buttons.size() - 1)].second);
4301 xBox->set_title(aTitle);
4302 sal_Int16 nRet = xBox->run();
4303 rPar.Get(0)->PutInteger(nRet);
4306 void SbRtl_SetAttr(StarBASIC *, SbxArray & rPar, bool)
4308 rPar.Get(0)->PutEmpty();
4309 if (rPar.Count() == 3)
4311 OUString aStr = rPar.Get(1)->GetOUString();
4312 sal_Int16 nFlags = rPar.Get(2)->GetInteger();
4314 if( hasUno() )
4316 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
4317 if( xSFI.is() )
4321 bool bReadOnly = bool(nFlags & SbAttributes::READONLY);
4322 xSFI->setReadOnly( aStr, bReadOnly );
4323 bool bHidden = bool(nFlags & SbAttributes::HIDDEN);
4324 xSFI->setHidden( aStr, bHidden );
4326 catch(const Exception & )
4328 StarBASIC::Error( ERRCODE_IO_GENERAL );
4333 else
4335 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4339 void SbRtl_Reset(StarBASIC *, SbxArray &, bool)
4341 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
4342 if (pIO)
4344 pIO->CloseAll();
4348 void SbRtl_DumpAllObjects(StarBASIC * pBasic, SbxArray & rPar, bool)
4350 const sal_uInt32 nArgCount = rPar.Count();
4351 if( nArgCount < 2 || nArgCount > 3 )
4353 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4355 else if( !pBasic )
4357 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
4359 else
4361 SbxObject* p = pBasic;
4362 while( p->GetParent() )
4364 p = p->GetParent();
4366 SvFileStream aStrm(rPar.Get(1)->GetOUString(),
4367 StreamMode::WRITE | StreamMode::TRUNC );
4368 p->Dump(aStrm, rPar.Get(2)->GetBool());
4369 aStrm.Close();
4370 if( aStrm.GetError() != ERRCODE_NONE )
4372 StarBASIC::Error( ERRCODE_BASIC_IO_ERROR );
4378 void SbRtl_FileExists(StarBASIC *, SbxArray & rPar, bool)
4380 if (rPar.Count() == 2)
4382 OUString aStr = rPar.Get(1)->GetOUString();
4383 bool bExists = false;
4385 if( hasUno() )
4387 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
4388 if( xSFI.is() )
4392 bExists = xSFI->exists( aStr );
4394 catch(const Exception & )
4396 StarBASIC::Error( ERRCODE_IO_GENERAL );
4400 else
4402 DirectoryItem aItem;
4403 FileBase::RC nRet = DirectoryItem::get( getFullPath( aStr ), aItem );
4404 bExists = (nRet == FileBase::E_None);
4406 rPar.Get(0)->PutBool(bExists);
4408 else
4410 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4414 void SbRtl_Partition(StarBASIC *, SbxArray & rPar, bool)
4416 if (rPar.Count() != 5)
4418 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4421 sal_Int32 nNumber = rPar.Get(1)->GetLong();
4422 sal_Int32 nStart = rPar.Get(2)->GetLong();
4423 sal_Int32 nStop = rPar.Get(3)->GetLong();
4424 sal_Int32 nInterval = rPar.Get(4)->GetLong();
4426 if( nStart < 0 || nStop <= nStart || nInterval < 1 )
4428 return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4431 // the Partition function inserts leading spaces before lowervalue and uppervalue
4432 // so that they both have the same number of characters as the string
4433 // representation of the value (Stop + 1). This ensures that if you use the output
4434 // of the Partition function with several values of Number, the resulting text
4435 // will be handled properly during any subsequent sort operation.
4437 // calculate the maximum number of characters before lowervalue and uppervalue
4438 OUString aBeforeStart = OUString::number( nStart - 1 );
4439 OUString aAfterStop = OUString::number( nStop + 1 );
4440 sal_Int32 nLen1 = aBeforeStart.getLength();
4441 sal_Int32 nLen2 = aAfterStop.getLength();
4442 sal_Int32 nLen = nLen1 >= nLen2 ? nLen1:nLen2;
4444 OUStringBuffer aRetStr( nLen * 2 + 1);
4445 OUString aLowerValue;
4446 OUString aUpperValue;
4447 if( nNumber < nStart )
4449 aUpperValue = aBeforeStart;
4451 else if( nNumber > nStop )
4453 aLowerValue = aAfterStop;
4455 else
4457 sal_Int32 nLowerValue = nNumber;
4458 sal_Int32 nUpperValue = nLowerValue;
4459 if( nInterval > 1 )
4461 nLowerValue = ((( nNumber - nStart ) / nInterval ) * nInterval ) + nStart;
4462 nUpperValue = nLowerValue + nInterval - 1;
4464 aLowerValue = OUString::number( nLowerValue );
4465 aUpperValue = OUString::number( nUpperValue );
4468 nLen1 = aLowerValue.getLength();
4469 nLen2 = aUpperValue.getLength();
4471 if( nLen > nLen1 )
4473 // appending the leading spaces for the lowervalue
4474 for ( sal_Int32 i= nLen - nLen1; i > 0; --i )
4476 aRetStr.append(" ");
4479 aRetStr.append( aLowerValue + ":");
4480 if( nLen > nLen2 )
4482 // appending the leading spaces for the uppervalue
4483 for ( sal_Int32 i= nLen - nLen2; i > 0; --i )
4485 aRetStr.append(" ");
4488 aRetStr.append( aUpperValue );
4489 rPar.Get(0)->PutString(aRetStr.makeStringAndClear());
4492 #endif
4494 sal_Int16 implGetDateYear( double aDate )
4496 Date aRefDate(1899'12'30);
4497 sal_Int32 nDays = static_cast<sal_Int32>(aDate);
4498 aRefDate.AddDays( nDays );
4499 sal_Int16 nRet = aRefDate.GetYear();
4500 return nRet;
4503 bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
4504 bool bUseTwoDigitYear, SbDateCorrection eCorr, double& rdRet )
4506 // XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and
4507 // 30..99 can not be input as they are 2-digit for 2000..2029 and
4508 // 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always
4509 // true). For VBA years > 9999 are invalid.
4510 // For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99
4511 // can not be input as they are 2-digit for 1900..1999, years<0 are
4512 // accepted. If bUseTwoDigitYear==false then all years are accepted, but
4513 // year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01).
4514 #if HAVE_FEATURE_SCRIPTING
4515 if ( (nYear < 0 || 9999 < nYear) && SbiRuntime::isVBAEnabled() )
4517 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4518 return false;
4520 else if ( nYear < 30 && SbiRuntime::isVBAEnabled() )
4522 nYear += 2000;
4524 else
4525 #endif
4527 if ( 0 <= nYear && nYear < 100 &&
4528 #if HAVE_FEATURE_SCRIPTING
4529 (bUseTwoDigitYear || SbiRuntime::isVBAEnabled())
4530 #else
4531 bUseTwoDigitYear
4532 #endif
4535 nYear += 1900;
4539 sal_Int32 nAddMonths = 0;
4540 sal_Int32 nAddDays = 0;
4541 // Always sanitize values to set date and to use for validity detection.
4542 if (nMonth < 1 || 12 < nMonth)
4544 sal_Int16 nM = ((nMonth < 1) ? (12 + (nMonth % 12)) : (nMonth % 12));
4545 nAddMonths = nMonth - nM;
4546 nMonth = nM;
4548 // Day 0 would already be normalized during Date::Normalize(), include
4549 // it in negative days, also to detect non-validity. The actual day of
4550 // month is 1+(nDay-1)
4551 if (nDay < 1)
4553 nAddDays = nDay - 1;
4554 nDay = 1;
4556 else if (nDay > 31)
4558 nAddDays = nDay - 31;
4559 nDay = 31;
4562 Date aCurDate( nDay, nMonth, nYear );
4564 /* TODO: we could enable the same rollover mechanism for StarBASIC to be
4565 * compatible with VBA (just with our wider supported date range), then
4566 * documentation would need to be adapted. As is, the DateSerial() runtime
4567 * function works as dumb as documented... (except that the resulting date
4568 * is checked for validity now and not just day<=31 and month<=12).
4569 * If change wanted then simply remove overriding RollOver here and adapt
4570 * documentation.*/
4571 #if HAVE_FEATURE_SCRIPTING
4572 if (eCorr == SbDateCorrection::RollOver && !SbiRuntime::isVBAEnabled())
4573 eCorr = SbDateCorrection::None;
4574 #endif
4576 if (nYear == 0 || (eCorr == SbDateCorrection::None && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
4578 #if HAVE_FEATURE_SCRIPTING
4579 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4580 #endif
4581 return false;
4584 if (eCorr != SbDateCorrection::None)
4586 aCurDate.Normalize();
4587 if (nAddMonths)
4588 aCurDate.AddMonths( nAddMonths);
4589 if (nAddDays)
4590 aCurDate.AddDays( nAddDays);
4591 if (eCorr == SbDateCorrection::TruncateToMonth && aCurDate.GetMonth() != nMonth)
4593 if (aCurDate.GetYear() == SAL_MAX_INT16 && nMonth == 12)
4595 // Roll over and back not possible, hard max.
4596 aCurDate.SetMonth(12);
4597 aCurDate.SetDay(31);
4599 else
4601 aCurDate.SetMonth(nMonth);
4602 aCurDate.SetDay(1);
4603 aCurDate.AddMonths(1);
4604 aCurDate.AddDays(-1);
4609 rdRet = GetDayDiff(aCurDate);
4610 return true;
4613 double implTimeSerial(sal_Int16 nHours, sal_Int16 nMinutes, sal_Int16 nSeconds,
4614 sal_Int32 nMilliSeconds)
4616 return (nHours * ::tools::Time::milliSecPerHour + nMinutes * ::tools::Time::milliSecPerMinute
4617 + nSeconds * ::tools::Time::milliSecPerSec + nMilliSeconds)
4618 / static_cast<double>(::tools::Time::milliSecPerDay);
4621 bool implDateTimeSerial(sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, sal_Int16 nHour,
4622 sal_Int16 nMinute, sal_Int16 nSecond, sal_Int32 nMilliSecond, double& rdRet)
4624 double dDate;
4625 if(!implDateSerial(nYear, nMonth, nDay, false/*bUseTwoDigitYear*/, SbDateCorrection::None, dDate))
4626 return false;
4627 rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond, nMilliSecond);
4628 return true;
4631 sal_Int16 implGetMinute( double dDate )
4633 double nFrac = (dDate - floor(dDate)) * ::tools::Time::milliSecPerDay;
4634 sal_uInt64 nMilliSeconds = static_cast<sal_uInt64>(nFrac + 0.5);
4635 return static_cast<sal_Int16>((nMilliSeconds / ::tools::Time::milliSecPerMinute)
4636 % ::tools::Time::minutePerHour);
4639 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */