LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / basic / source / runtime / methods.cxx
blob851584b6db793cebe13db022a2c8db105d21b68d
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/window.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/sound.hxx>
31 #include <tools/wintypes.hxx>
32 #include <vcl/stdtext.hxx>
33 #include <vcl/weld.hxx>
34 #include <basic/sbx.hxx>
35 #include <svl/zforlist.hxx>
36 #include <rtl/character.hxx>
37 #include <rtl/math.hxx>
38 #include <tools/urlobj.hxx>
39 #include <osl/time.h>
40 #include <unotools/charclass.hxx>
41 #include <unotools/ucbstreamhelper.hxx>
42 #include <unotools/wincodepage.hxx>
43 #include <tools/wldcrd.hxx>
44 #include <i18nlangtag/lang.h>
45 #include <rtl/string.hxx>
46 #include <sal/log.hxx>
47 #include <comphelper/DirectoryHelper.hxx>
49 #include <runtime.hxx>
50 #include <sbunoobj.hxx>
51 #include <osl/file.hxx>
52 #include <errobject.hxx>
54 #include <comphelper/string.hxx>
55 #include <comphelper/processfactory.hxx>
57 #include <com/sun/star/uno/Sequence.hxx>
58 #include <com/sun/star/util/DateTime.hpp>
59 #include <com/sun/star/lang/Locale.hpp>
60 #include <com/sun/star/lang/XServiceInfo.hpp>
61 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
62 #include <com/sun/star/script/XErrorQuery.hpp>
63 #include <ooo/vba/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>
79 using namespace comphelper;
80 using namespace osl;
81 using namespace com::sun::star;
82 using namespace com::sun::star::lang;
83 using namespace com::sun::star::uno;
85 #include <date.hxx>
86 #include <sbstdobj.hxx>
87 #include <rtlproto.hxx>
88 #include <image.hxx>
89 #include <iosys.hxx>
90 #include "ddectrl.hxx"
91 #include <sbintern.hxx>
92 #include <basic/vbahelper.hxx>
94 #include <vector>
95 #include <math.h>
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include <errno.h>
100 #include <sbobjmod.hxx>
101 #include <sbxmod.hxx>
103 #ifdef _WIN32
104 #include <prewin.h>
105 #include <direct.h>
106 #include <io.h>
107 #include <postwin.h>
108 #else
109 #include <unistd.h>
110 #endif
112 #include <com/sun/star/i18n/XCharacterClassification.hpp>
113 #include <vcl/unohelp.hxx>
114 #include <vcl/TypeSerializer.hxx>
116 #if HAVE_FEATURE_SCRIPTING
118 static void FilterWhiteSpace( OUString& rStr )
120 if (rStr.isEmpty())
122 return;
124 OUStringBuffer aRet;
126 for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
128 sal_Unicode cChar = rStr[i];
129 if ((cChar != ' ') && (cChar != '\t') &&
130 (cChar != '\n') && (cChar != '\r'))
132 aRet.append(cChar);
136 rStr = aRet.makeStringAndClear();
139 static tools::Long GetDayDiff( const Date& rDate );
141 static const CharClass& GetCharClass()
143 static CharClass aCharClass( Application::GetSettings().GetLanguageTag() );
144 return aCharClass;
147 static bool isFolder( FileStatus::Type aType )
149 return ( aType == FileStatus::Directory || aType == FileStatus::Volume );
153 //*** UCB file access ***
155 // Converts possibly relative paths to absolute paths
156 // according to the setting done by ChDir/ChDrive
157 OUString getFullPath( const OUString& aRelPath )
159 OUString aFileURL;
161 // #80204 Try first if it already is a valid URL
162 INetURLObject aURLObj( aRelPath );
163 aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
165 if( aFileURL.isEmpty() )
167 File::getFileURLFromSystemPath( aRelPath, aFileURL );
170 return aFileURL;
173 // TODO: -> SbiGlobals
174 static uno::Reference< ucb::XSimpleFileAccess3 > const & getFileAccess()
176 static uno::Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
177 return xSFI;
181 // Properties and methods lie down the return value at the Get (bPut = sal_False) in the
182 // element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True)
184 // CreateObject( class )
186 void SbRtl_CreateObject(StarBASIC * pBasic, SbxArray & rPar, bool)
188 OUString aClass(rPar.Get(1)->GetOUString());
189 SbxObjectRef p = SbxBase::CreateObject( aClass );
190 if( !p.is() )
191 StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD );
192 else
194 // Convenience: enter BASIC as parent
195 p->SetParent( pBasic );
196 rPar.Get(0)->PutObject(p.get());
200 // Error( n )
202 void SbRtl_Error(StarBASIC * pBasic, SbxArray & rPar, bool)
204 if( !pBasic )
205 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
206 else
208 OUString aErrorMsg;
209 ErrCode nErr = ERRCODE_NONE;
210 sal_Int32 nCode = 0;
211 if (rPar.Count() == 1)
213 nErr = StarBASIC::GetErrBasic();
214 aErrorMsg = StarBASIC::GetErrorMsg();
216 else
218 nCode = rPar.Get(1)->GetLong();
219 if( nCode > 65535 )
221 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
223 else
225 nErr = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nCode) );
229 bool bVBA = SbiRuntime::isVBAEnabled();
230 OUString tmpErrMsg;
231 if( bVBA && !aErrorMsg.isEmpty())
233 tmpErrMsg = aErrorMsg;
235 else
237 StarBASIC::MakeErrorText( nErr, aErrorMsg );
238 tmpErrMsg = StarBASIC::GetErrorText();
240 // If this rtlfunc 'Error' passed an errcode the same as the active Err Objects's
241 // current err then return the description for the error message if it is set
242 // ( complicated isn't it ? )
243 if (bVBA && rPar.Count() > 1)
245 uno::Reference< ooo::vba::XErrObject > xErrObj( SbxErrObject::getUnoErrObject() );
246 if ( xErrObj.is() && xErrObj->getNumber() == nCode && !xErrObj->getDescription().isEmpty() )
248 tmpErrMsg = xErrObj->getDescription();
251 rPar.Get(0)->PutString(tmpErrMsg);
255 // Sinus
257 void SbRtl_Sin(StarBASIC *, SbxArray & rPar, bool)
259 if (rPar.Count() < 2)
260 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
261 else
263 SbxVariableRef pArg = rPar.Get(1);
264 rPar.Get(0)->PutDouble(sin(pArg->GetDouble()));
269 void SbRtl_Cos(StarBASIC *, SbxArray & rPar, bool)
271 if (rPar.Count() < 2)
272 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
273 else
275 SbxVariableRef pArg = rPar.Get(1);
276 rPar.Get(0)->PutDouble(cos(pArg->GetDouble()));
281 void SbRtl_Atn(StarBASIC *, SbxArray & rPar, bool)
283 if (rPar.Count() < 2)
284 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
285 else
287 SbxVariableRef pArg = rPar.Get(1);
288 rPar.Get(0)->PutDouble(atan(pArg->GetDouble()));
293 void SbRtl_Abs(StarBASIC *, SbxArray & rPar, bool)
295 if (rPar.Count() < 2)
297 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
299 else
301 SbxVariableRef pArg = rPar.Get(1);
302 rPar.Get(0)->PutDouble(fabs(pArg->GetDouble()));
307 void SbRtl_Asc(StarBASIC *, SbxArray & rPar, bool)
309 if (rPar.Count() < 2)
311 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
313 else
315 SbxVariableRef pArg = rPar.Get(1);
316 OUString aStr( pArg->GetOUString() );
317 if ( aStr.isEmpty())
319 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
320 rPar.Get(0)->PutEmpty();
322 else
324 sal_Unicode aCh = aStr[0];
325 rPar.Get(0)->PutLong(aCh);
330 static void implChr( SbxArray& rPar, bool bChrW )
332 if (rPar.Count() < 2)
334 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
336 else
338 SbxVariableRef pArg = rPar.Get(1);
340 OUString aStr;
341 if( !bChrW && SbiRuntime::isVBAEnabled() )
343 char c = static_cast<char>(pArg->GetByte());
344 aStr = OUString(&c, 1, osl_getThreadTextEncoding());
346 else
348 // Map negative 16-bit values to large positive ones, so that code like Chr(&H8000)
349 // still works after the fix for tdf#62326 changed those four-digit hex notations to
350 // produce negative values:
351 sal_Int32 aCh = pArg->GetLong();
352 if (aCh < -0x8000 || aCh > 0xFFFF) {
353 StarBASIC::Error(ERRCODE_BASIC_MATH_OVERFLOW);
354 aCh = 0;
356 aStr = OUString(static_cast<sal_Unicode>(aCh));
358 rPar.Get(0)->PutString(aStr);
362 void SbRtl_Chr(StarBASIC *, SbxArray & rPar, bool)
364 implChr( rPar, false/*bChrW*/ );
367 void SbRtl_ChrW(StarBASIC *, SbxArray & rPar, bool)
369 implChr( rPar, true/*bChrW*/ );
372 #if defined _WIN32
374 namespace {
376 extern "C" void invalidParameterHandler(
377 wchar_t const * expression, wchar_t const * function, wchar_t const * file, unsigned int line,
378 uintptr_t)
380 SAL_INFO(
381 "basic",
382 "invalid parameter during _wgetdcwd; \""
383 << (expression ? OUString(o3tl::toU(expression)) : OUString("???"))
384 << "\" (" << (function ? OUString(o3tl::toU(function)) : OUString("???")) << ") at "
385 << (file ? OUString(o3tl::toU(file)) : OUString("???")) << ":" << line);
390 #endif
392 void SbRtl_CurDir(StarBASIC *, SbxArray & rPar, bool)
394 // #57064 Although this function doesn't work with DirEntry, it isn't touched
395 // by the adjustment to virtual URLs, as, using the DirEntry-functionality,
396 // there's no possibility to detect the current one in a way that a virtual URL
397 // could be delivered.
399 #if defined(_WIN32)
400 int nCurDir = 0; // Current dir // JSM
401 if (rPar.Count() == 2)
403 OUString aDrive = rPar.Get(1)->GetOUString();
404 if ( aDrive.getLength() != 1 )
406 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
407 return;
409 auto c = rtl::toAsciiUpperCase(aDrive[0]);
410 if ( !rtl::isAsciiUpperCase( c ) )
412 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
413 return;
415 nCurDir = c - 'A' + 1;
417 wchar_t pBuffer[ _MAX_PATH ];
418 // _wgetdcwd calls the C runtime's invalid parameter handler (which by default terminates the
419 // process) if nCurDir does not correspond to an existing drive, so temporarily set a "harmless"
420 // handler:
421 auto const handler = _set_thread_local_invalid_parameter_handler(&invalidParameterHandler);
422 auto const ok = _wgetdcwd( nCurDir, pBuffer, _MAX_PATH ) != nullptr;
423 _set_thread_local_invalid_parameter_handler(handler);
424 if ( ok )
426 rPar.Get(0)->PutString(OUString(o3tl::toU(pBuffer)));
428 else
430 StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE );
433 #else
435 const int PATH_INCR = 250;
437 int nSize = PATH_INCR;
438 std::unique_ptr<char[]> pMem;
439 while( true )
441 pMem.reset(new char[nSize]);
442 if( !pMem )
444 StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY );
445 return;
447 if( getcwd( pMem.get(), nSize-1 ) != nullptr )
449 rPar.Get(0)->PutString(OUString::createFromAscii(pMem.get()));
450 return;
452 if( errno != ERANGE )
454 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
455 return;
457 nSize += PATH_INCR;
460 #endif
463 void SbRtl_ChDir(StarBASIC * pBasic, SbxArray & rPar, bool)
465 rPar.Get(0)->PutEmpty();
466 if (rPar.Count() == 2)
468 // VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.)
469 if( SbiRuntime::isVBAEnabled() )
471 ::basic::vba::registerCurrentDirectory(getDocumentModel(pBasic),
472 rPar.Get(1)->GetOUString());
475 else
477 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
481 void SbRtl_ChDrive(StarBASIC *, SbxArray & rPar, bool)
483 rPar.Get(0)->PutEmpty();
484 if (rPar.Count() != 2)
486 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
491 // Implementation of StepRENAME with UCB
492 void implStepRenameUCB( const OUString& aSource, const OUString& aDest )
494 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
495 if( !xSFI.is() )
496 return;
500 OUString aSourceFullPath = getFullPath( aSource );
501 if( !xSFI->exists( aSourceFullPath ) )
503 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
504 return;
507 OUString aDestFullPath = getFullPath( aDest );
508 if( xSFI->exists( aDestFullPath ) )
510 StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS );
512 else
514 xSFI->move( aSourceFullPath, aDestFullPath );
517 catch(const Exception & )
519 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
523 // Implementation of StepRENAME with OSL
524 void implStepRenameOSL( const OUString& aSource, const OUString& aDest )
526 FileBase::RC nRet = File::move( getFullPath( aSource ), getFullPath( aDest ) );
527 if( nRet != FileBase::E_None )
529 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
533 void SbRtl_FileCopy(StarBASIC *, SbxArray & rPar, bool)
535 rPar.Get(0)->PutEmpty();
536 if (rPar.Count() == 3)
538 OUString aSource = rPar.Get(1)->GetOUString();
539 OUString aDest = rPar.Get(2)->GetOUString();
540 if( hasUno() )
542 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
543 if( xSFI.is() )
547 xSFI->copy( getFullPath( aSource ), getFullPath( aDest ) );
549 catch(const Exception & )
551 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
555 else
557 FileBase::RC nRet = File::copy( getFullPath( aSource ), getFullPath( aDest ) );
558 if( nRet != FileBase::E_None )
560 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
564 else
565 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
568 void SbRtl_Kill(StarBASIC *, SbxArray & rPar, bool)
570 rPar.Get(0)->PutEmpty();
571 if (rPar.Count() == 2)
573 OUString aFileSpec = rPar.Get(1)->GetOUString();
575 if( hasUno() )
577 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
578 if( xSFI.is() )
580 OUString aFullPath = getFullPath( aFileSpec );
581 if( !xSFI->exists( aFullPath ) || xSFI->isFolder( aFullPath ) )
583 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
584 return;
588 xSFI->kill( aFullPath );
590 catch(const Exception & )
592 StarBASIC::Error( ERRCODE_IO_GENERAL );
596 else
598 File::remove( getFullPath( aFileSpec ) );
601 else
603 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
607 void SbRtl_MkDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
609 rPar.Get(0)->PutEmpty();
610 if (rPar.Count() == 2)
612 OUString aPath = rPar.Get(1)->GetOUString();
613 if ( SbiRuntime::isVBAEnabled() )
615 // In vba if the full path is not specified then
616 // folder is created relative to the curdir
617 INetURLObject aURLObj( getFullPath( aPath ) );
618 if ( aURLObj.GetProtocol() != INetProtocol::File )
620 SbxArrayRef pPar = new SbxArray();
621 SbxVariableRef pResult = new SbxVariable();
622 SbxVariableRef pParam = new SbxVariable();
623 pPar->Insert(pResult.get(), pPar->Count());
624 pPar->Insert(pParam.get(), pPar->Count());
625 SbRtl_CurDir( pBasic, *pPar, bWrite );
627 OUString sCurPathURL;
628 File::getFileURLFromSystemPath(pPar->Get(0)->GetOUString(), sCurPathURL);
630 aURLObj.SetURL( sCurPathURL );
631 aURLObj.Append( aPath );
632 File::getSystemPathFromFileURL(aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ),aPath ) ;
636 if( hasUno() )
638 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
639 if( xSFI.is() )
643 xSFI->createFolder( getFullPath( aPath ) );
645 catch(const Exception & )
647 StarBASIC::Error( ERRCODE_IO_GENERAL );
651 else
653 Directory::create( getFullPath( aPath ) );
656 else
658 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
663 static void implRemoveDirRecursive( const OUString& aDirPath )
665 DirectoryItem aItem;
666 FileBase::RC nRet = DirectoryItem::get( aDirPath, aItem );
667 bool bExists = (nRet == FileBase::E_None);
669 FileStatus aFileStatus( osl_FileStatus_Mask_Type );
670 nRet = aItem.getFileStatus( aFileStatus );
671 bool bFolder = nRet == FileBase::E_None
672 && isFolder( aFileStatus.getFileType() );
674 if( !bExists || !bFolder )
676 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
677 return;
680 Directory aDir( aDirPath );
681 nRet = aDir.open();
682 if( nRet != FileBase::E_None )
684 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
685 return;
687 aDir.close();
689 comphelper::DirectoryHelper::deleteDirRecursively(aDirPath);
693 void SbRtl_RmDir(StarBASIC *, SbxArray & rPar, bool)
695 rPar.Get(0)->PutEmpty();
696 if (rPar.Count() == 2)
698 OUString aPath = rPar.Get(1)->GetOUString();
699 if( hasUno() )
701 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
702 if( xSFI.is() )
706 if( !xSFI->isFolder( aPath ) )
708 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
709 return;
711 SbiInstance* pInst = GetSbData()->pInst;
712 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
713 if( bCompatibility )
715 Sequence< OUString > aContent = xSFI->getFolderContents( aPath, true );
716 if( aContent.hasElements() )
718 StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR );
719 return;
723 xSFI->kill( getFullPath( aPath ) );
725 catch(const Exception & )
727 StarBASIC::Error( ERRCODE_IO_GENERAL );
731 else
733 implRemoveDirRecursive( getFullPath( aPath ) );
736 else
738 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
742 void SbRtl_SendKeys(StarBASIC *, SbxArray & rPar, bool)
744 rPar.Get(0)->PutEmpty();
745 StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
748 void SbRtl_Exp(StarBASIC *, SbxArray & rPar, bool)
750 if (rPar.Count() < 2)
751 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
752 else
754 double aDouble = rPar.Get(1)->GetDouble();
755 aDouble = exp( aDouble );
756 checkArithmeticOverflow( aDouble );
757 rPar.Get(0)->PutDouble(aDouble);
761 void SbRtl_FileLen(StarBASIC *, SbxArray & rPar, bool)
763 if (rPar.Count() < 2)
765 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
767 else
769 SbxVariableRef pArg = rPar.Get(1);
770 OUString aStr( pArg->GetOUString() );
771 sal_Int32 nLen = 0;
772 if( hasUno() )
774 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
775 if( xSFI.is() )
779 nLen = xSFI->getSize( getFullPath( aStr ) );
781 catch(const Exception & )
783 StarBASIC::Error( ERRCODE_IO_GENERAL );
787 else
789 DirectoryItem aItem;
790 (void)DirectoryItem::get( getFullPath( aStr ), aItem );
791 FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
792 (void)aItem.getFileStatus( aFileStatus );
793 nLen = static_cast<sal_Int32>(aFileStatus.getFileSize());
795 rPar.Get(0)->PutLong(static_cast<tools::Long>(nLen));
800 void SbRtl_Hex(StarBASIC *, SbxArray & rPar, bool)
802 if (rPar.Count() < 2)
804 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
806 else
808 SbxVariableRef pArg = rPar.Get(1);
809 // converting value to unsigned and limit to 2 or 4 byte representation
810 sal_uInt32 nVal = pArg->IsInteger() ?
811 static_cast<sal_uInt16>(pArg->GetInteger()) :
812 static_cast<sal_uInt32>(pArg->GetLong());
813 OUString aStr(OUString::number( nVal, 16 ));
814 aStr = aStr.toAsciiUpperCase();
815 rPar.Get(0)->PutString(aStr);
819 void SbRtl_FuncCaller(StarBASIC *, SbxArray & rPar, bool)
821 if ( SbiRuntime::isVBAEnabled() && GetSbData()->pInst && GetSbData()->pInst->pRun )
823 if ( GetSbData()->pInst->pRun->GetExternalCaller() )
824 *rPar.Get(0) = *GetSbData()->pInst->pRun->GetExternalCaller();
825 else
827 SbxVariableRef pVar = new SbxVariable(SbxVARIANT);
828 *rPar.Get(0) = *pVar;
831 else
833 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
837 // InStr( [start],string,string,[compare] )
839 void SbRtl_InStr(StarBASIC *, SbxArray & rPar, bool)
841 const sal_uInt32 nArgCount = rPar.Count() - 1;
842 if ( nArgCount < 2 )
843 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
844 else
846 sal_Int32 nStartPos = 1;
847 sal_Int32 nFirstStringPos = 1;
849 if ( nArgCount >= 3 )
851 nStartPos = rPar.Get(1)->GetLong();
852 if( nStartPos <= 0 )
854 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
855 nStartPos = 1;
857 nFirstStringPos++;
860 SbiInstance* pInst = GetSbData()->pInst;
861 bool bTextMode;
862 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
863 if( bCompatibility )
865 SbiRuntime* pRT = pInst->pRun;
866 bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
868 else
870 bTextMode = true;
872 if ( nArgCount == 4 )
874 bTextMode = rPar.Get(4)->GetInteger();
876 sal_Int32 nPos;
877 const OUString& rToken = rPar.Get(nFirstStringPos + 1)->GetOUString();
879 // #97545 Always find empty string
880 if( rToken.isEmpty() )
882 nPos = nStartPos;
884 else
886 const OUString& rStr1 = rPar.Get(nFirstStringPos)->GetOUString();
887 const sal_Int32 nrStr1Len = rStr1.getLength();
888 if (nStartPos > nrStr1Len)
890 // Start position is greater than the string being searched
891 nPos = 0;
893 else
895 if( !bTextMode )
897 nPos = rStr1.indexOf( rToken, nStartPos - 1 ) + 1;
899 else
901 // tdf#139840 - case-insensitive operation for non-ASCII characters
902 i18nutil::SearchOptions2 aSearchOptions;
903 aSearchOptions.searchString = rToken;
904 aSearchOptions.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
905 aSearchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
906 utl::TextSearch textSearch(aSearchOptions);
908 sal_Int32 nStart = nStartPos - 1;
909 sal_Int32 nEnd = nrStr1Len;
910 nPos = textSearch.SearchForward(rStr1, &nStart, &nEnd) ? nStart + 1 : 0;
914 rPar.Get(0)->PutLong(nPos);
919 // InstrRev(string1, string2[, start[, compare]])
921 void SbRtl_InStrRev(StarBASIC *, SbxArray & rPar, bool)
923 const sal_uInt32 nArgCount = rPar.Count() - 1;
924 if ( nArgCount < 2 )
926 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
928 else
930 const OUString aStr1 = rPar.Get(1)->GetOUString();
931 const OUString aToken = rPar.Get(2)->GetOUString();
933 sal_Int32 nStartPos = -1;
934 if ( nArgCount >= 3 )
936 nStartPos = rPar.Get(3)->GetLong();
937 if( nStartPos <= 0 && nStartPos != -1 )
939 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
940 nStartPos = -1;
944 SbiInstance* pInst = GetSbData()->pInst;
945 bool bTextMode;
946 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
947 if( bCompatibility )
949 SbiRuntime* pRT = pInst->pRun;
950 bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
952 else
954 bTextMode = true;
956 if ( nArgCount == 4 )
958 bTextMode = rPar.Get(4)->GetInteger();
960 const sal_Int32 nStrLen = aStr1.getLength();
961 if( nStartPos == -1 )
963 nStartPos = nStrLen;
966 sal_Int32 nPos = 0;
967 if( nStartPos <= nStrLen )
969 sal_Int32 nTokenLen = aToken.getLength();
970 if( !nTokenLen )
972 // Always find empty string
973 nPos = nStartPos;
975 else if( nStrLen > 0 )
977 if( !bTextMode )
979 nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
981 else
983 // tdf#143332 - case-insensitive operation for non-ASCII characters
984 i18nutil::SearchOptions2 aSearchOptions;
985 aSearchOptions.searchString = aToken;
986 aSearchOptions.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
987 aSearchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
988 utl::TextSearch textSearch(aSearchOptions);
990 sal_Int32 nStart = 0;
991 sal_Int32 nEnd = nStartPos;
992 nPos = textSearch.SearchBackward(aStr1, &nEnd, &nStart) ? nStart : 0;
996 rPar.Get(0)->PutLong(nPos);
1002 Int( 2.8 ) = 2.0
1003 Int( -2.8 ) = -3.0
1004 Fix( 2.8 ) = 2.0
1005 Fix( -2.8 ) = -2.0 <- !!
1008 void SbRtl_Int(StarBASIC *, SbxArray & rPar, bool)
1010 if (rPar.Count() < 2)
1011 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1012 else
1014 SbxVariableRef pArg = rPar.Get(1);
1015 double aDouble= pArg->GetDouble();
1017 floor( 2.8 ) = 2.0
1018 floor( -2.8 ) = -3.0
1020 aDouble = floor( aDouble );
1021 rPar.Get(0)->PutDouble(aDouble);
1026 void SbRtl_Fix(StarBASIC *, SbxArray & rPar, bool)
1028 if (rPar.Count() < 2)
1029 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1030 else
1032 SbxVariableRef pArg = rPar.Get(1);
1033 double aDouble = pArg->GetDouble();
1034 if ( aDouble >= 0.0 )
1035 aDouble = floor( aDouble );
1036 else
1037 aDouble = ceil( aDouble );
1038 rPar.Get(0)->PutDouble(aDouble);
1043 void SbRtl_LCase(StarBASIC *, SbxArray & rPar, bool)
1045 if (rPar.Count() < 2)
1047 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1049 else
1051 const CharClass& rCharClass = GetCharClass();
1052 OUString aStr(rPar.Get(1)->GetOUString());
1053 aStr = rCharClass.lowercase(aStr);
1054 rPar.Get(0)->PutString(aStr);
1058 void SbRtl_Left(StarBASIC *, SbxArray & rPar, bool)
1060 if (rPar.Count() < 3)
1062 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1064 else
1066 OUString aStr(rPar.Get(1)->GetOUString());
1067 sal_Int32 nResultLen = rPar.Get(2)->GetLong();
1068 if( nResultLen < 0 )
1070 nResultLen = 0;
1071 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1073 else if(nResultLen > aStr.getLength())
1075 nResultLen = aStr.getLength();
1077 aStr = aStr.copy(0, nResultLen );
1078 rPar.Get(0)->PutString(aStr);
1082 void SbRtl_Log(StarBASIC *, SbxArray & rPar, bool)
1084 if (rPar.Count() < 2)
1086 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1088 else
1090 double aArg = rPar.Get(1)->GetDouble();
1091 if ( aArg > 0 )
1093 double d = log( aArg );
1094 checkArithmeticOverflow( d );
1095 rPar.Get(0)->PutDouble(d);
1097 else
1099 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1104 void SbRtl_LTrim(StarBASIC *, SbxArray & rPar, bool)
1106 if (rPar.Count() < 2)
1108 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1110 else
1112 OUString aStr(comphelper::string::stripStart(rPar.Get(1)->GetOUString(), ' '));
1113 rPar.Get(0)->PutString(aStr);
1118 // Mid( String, nStart, nLength )
1120 void SbRtl_Mid(StarBASIC *, SbxArray & rPar, bool bWrite)
1122 int nArgCount = rPar.Count() - 1;
1123 if ( nArgCount < 2 )
1125 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1127 else
1129 // #23178: replicate the functionality of Mid$ as a command
1130 // by adding a replacement-string as a fourth parameter.
1131 // In contrast to the original the third parameter (nLength)
1132 // can't be left out here. That's considered in bWrite already.
1133 if( nArgCount == 4 )
1135 bWrite = true;
1137 OUString aArgStr = rPar.Get(1)->GetOUString();
1138 sal_Int32 nStartPos = rPar.Get(2)->GetLong();
1139 if ( nStartPos < 1 )
1141 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1143 else
1145 nStartPos--;
1146 sal_Int32 nLen = -1;
1147 bool bWriteNoLenParam = false;
1148 if ( nArgCount == 3 || bWrite )
1150 sal_Int32 n = rPar.Get(3)->GetLong();
1151 if( bWrite && n == -1 )
1153 bWriteNoLenParam = true;
1155 nLen = n;
1157 if ( bWrite )
1159 sal_Int32 nArgLen = aArgStr.getLength();
1160 if( nStartPos > nArgLen )
1162 SbiInstance* pInst = GetSbData()->pInst;
1163 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1164 if( bCompatibility )
1166 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1167 return;
1169 nStartPos = nArgLen;
1172 OUString aReplaceStr = rPar.Get(4)->GetOUString();
1173 sal_Int32 nReplaceStrLen = aReplaceStr.getLength();
1174 sal_Int32 nReplaceLen;
1175 if( bWriteNoLenParam )
1177 nReplaceLen = nArgLen - nStartPos;
1179 else
1181 nReplaceLen = nLen;
1182 if( nReplaceLen < 0 || nReplaceLen > nArgLen - nStartPos )
1184 nReplaceLen = nArgLen - nStartPos;
1188 OUStringBuffer aResultStr(aArgStr);
1189 sal_Int32 nErase = nReplaceLen;
1190 aResultStr.remove( nStartPos, nErase );
1191 aResultStr.insert(
1192 nStartPos, aReplaceStr.getStr(), std::min(nReplaceLen, nReplaceStrLen));
1194 rPar.Get(1)->PutString(aResultStr.makeStringAndClear());
1196 else
1198 OUString aResultStr;
1199 if (nStartPos > aArgStr.getLength())
1201 // do nothing
1203 else if(nArgCount == 2)
1205 aResultStr = aArgStr.copy( nStartPos);
1207 else
1209 if (nLen < 0)
1210 nLen = 0;
1211 if(nStartPos + nLen > aArgStr.getLength())
1213 nLen = aArgStr.getLength() - nStartPos;
1215 if (nLen > 0)
1216 aResultStr = aArgStr.copy( nStartPos, nLen );
1218 rPar.Get(0)->PutString(aResultStr);
1224 void SbRtl_Oct(StarBASIC *, SbxArray & rPar, bool)
1226 if (rPar.Count() < 2)
1228 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1230 else
1232 char aBuffer[16];
1233 SbxVariableRef pArg = rPar.Get(1);
1234 if ( pArg->IsInteger() )
1236 snprintf( aBuffer, sizeof(aBuffer), "%o", pArg->GetInteger() );
1238 else
1240 snprintf( aBuffer, sizeof(aBuffer), "%lo", static_cast<long unsigned int>(pArg->GetLong()) );
1242 rPar.Get(0)->PutString(OUString::createFromAscii(aBuffer));
1246 // Replace(expression, find, replace[, start[, count[, compare]]])
1248 void SbRtl_Replace(StarBASIC *, SbxArray & rPar, bool)
1250 const sal_uInt32 nArgCount = rPar.Count() - 1;
1251 if ( nArgCount < 3 || nArgCount > 6 )
1253 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1254 return;
1257 sal_Int32 lStartPos = 1;
1258 if (nArgCount >= 4)
1260 if (rPar.Get(4)->GetType() != SbxEMPTY)
1262 lStartPos = rPar.Get(4)->GetLong();
1264 if (lStartPos < 1)
1266 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
1267 return;
1270 --lStartPos; // Make it 0-based
1272 sal_Int32 lCount = -1;
1273 if (nArgCount >= 5)
1275 if (rPar.Get(5)->GetType() != SbxEMPTY)
1277 lCount = rPar.Get(5)->GetLong();
1279 if (lCount < -1)
1281 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
1282 return;
1286 bool bCaseInsensitive;
1287 if (nArgCount == 6)
1289 bCaseInsensitive = rPar.Get(6)->GetInteger();
1291 else
1293 SbiInstance* pInst = GetSbData()->pInst;
1294 if (pInst && pInst->IsCompatibility())
1296 SbiRuntime* pRT = pInst->pRun;
1297 bCaseInsensitive = pRT && pRT->IsImageFlag(SbiImageFlags::COMPARETEXT);
1299 else
1301 bCaseInsensitive = true;
1305 const OUString aExpStr = rPar.Get(1)->GetOUString();
1306 OUString aFindStr = rPar.Get(2)->GetOUString();
1307 const OUString aReplaceStr = rPar.Get(3)->GetOUString();
1309 OUString aSrcStr(aExpStr);
1310 sal_Int32 nPrevPos = std::min(lStartPos, aSrcStr.getLength());
1311 css::uno::Sequence<sal_Int32> aOffset;
1312 if (bCaseInsensitive)
1314 // tdf#132389: case-insensitive operation for non-ASCII characters
1315 // tdf#142487: use css::i18n::Transliteration to correctly handle ß -> ss expansion
1316 // tdf#132388: We can't use utl::TextSearch (css::i18n::XTextSearch), because each call to
1317 // css::i18n::XTextSearch::SearchForward transliterates input string, making
1318 // performance of repeated calls unacceptable
1319 auto xTrans = css::i18n::Transliteration::create(comphelper::getProcessComponentContext());
1320 xTrans->loadModule(css::i18n::TransliterationModules_IGNORE_CASE, {});
1321 aFindStr = xTrans->transliterate(aFindStr, 0, aFindStr.getLength(), aOffset);
1322 aSrcStr = xTrans->transliterate(aSrcStr, nPrevPos, aSrcStr.getLength() - nPrevPos, aOffset);
1323 nPrevPos = std::distance(aOffset.begin(),
1324 std::lower_bound(aOffset.begin(), aOffset.end(), nPrevPos));
1327 auto getExpStrPos = [aOffset, nExpLen = aExpStr.getLength()](sal_Int32 nSrcStrPos) -> sal_Int32
1329 assert(!aOffset.hasElements() || aOffset.getLength() >= nSrcStrPos);
1330 if (!aOffset.hasElements())
1331 return nSrcStrPos;
1332 return aOffset.getLength() > nSrcStrPos ? aOffset[nSrcStrPos] : nExpLen;
1335 // Note: the result starts from lStartPos, removing everything to the left. See i#94895.
1336 OUStringBuffer sResult(aSrcStr.getLength() - nPrevPos);
1337 sal_Int32 nCounts = 0;
1338 while (lCount == -1 || lCount > nCounts)
1340 sal_Int32 nPos = aSrcStr.indexOf(aFindStr, nPrevPos);
1341 if (nPos < 0)
1342 break;
1344 lStartPos = getExpStrPos(nPrevPos);
1345 sResult.append(aExpStr.getStr() + lStartPos, getExpStrPos(nPos) - lStartPos);
1346 sResult.append(aReplaceStr);
1347 nPrevPos = nPos + aFindStr.getLength();
1348 nCounts++;
1350 lStartPos = getExpStrPos(nPrevPos);
1351 sResult.append(aExpStr.getStr() + lStartPos, aExpStr.getLength() - lStartPos);
1352 rPar.Get(0)->PutString(sResult.makeStringAndClear());
1355 void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool)
1357 if (rPar.Count() < 3)
1359 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1361 else
1363 const OUString& rStr = rPar.Get(1)->GetOUString();
1364 int nResultLen = rPar.Get(2)->GetLong();
1365 if( nResultLen < 0 )
1367 nResultLen = 0;
1368 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1370 int nStrLen = rStr.getLength();
1371 if ( nResultLen > nStrLen )
1373 nResultLen = nStrLen;
1375 OUString aResultStr = rStr.copy( nStrLen - nResultLen );
1376 rPar.Get(0)->PutString(aResultStr);
1380 void SbRtl_RTL(StarBASIC * pBasic, SbxArray & rPar, bool)
1382 rPar.Get(0)->PutObject(pBasic->getRTL().get());
1385 void SbRtl_RTrim(StarBASIC *, SbxArray & rPar, bool)
1387 if (rPar.Count() < 2)
1389 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1391 else
1393 OUString aStr(comphelper::string::stripEnd(rPar.Get(1)->GetOUString(), ' '));
1394 rPar.Get(0)->PutString(aStr);
1398 void SbRtl_Sgn(StarBASIC *, SbxArray & rPar, bool)
1400 if (rPar.Count() < 2)
1402 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1404 else
1406 double aDouble = rPar.Get(1)->GetDouble();
1407 sal_Int16 nResult = 0;
1408 if ( aDouble > 0 )
1410 nResult = 1;
1412 else if ( aDouble < 0 )
1414 nResult = -1;
1416 rPar.Get(0)->PutInteger(nResult);
1420 void SbRtl_Space(StarBASIC *, SbxArray & rPar, bool)
1422 if (rPar.Count() < 2)
1424 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1426 else
1428 OUStringBuffer aBuf;
1429 string::padToLength(aBuf, rPar.Get(1)->GetLong(), ' ');
1430 rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1434 void SbRtl_Spc(StarBASIC *, SbxArray & rPar, bool)
1436 if (rPar.Count() < 2)
1438 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1440 else
1442 OUStringBuffer aBuf;
1443 string::padToLength(aBuf, rPar.Get(1)->GetLong(), ' ');
1444 rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1448 void SbRtl_Sqr(StarBASIC *, SbxArray & rPar, bool)
1450 if (rPar.Count() < 2)
1452 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1454 else
1456 double aDouble = rPar.Get(1)->GetDouble();
1457 if ( aDouble >= 0 )
1459 rPar.Get(0)->PutDouble(sqrt(aDouble));
1461 else
1463 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1468 void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
1470 if (rPar.Count() < 2)
1472 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1474 else
1476 OUString aStr;
1477 OUString aStrNew("");
1478 SbxVariableRef pArg = rPar.Get(1);
1479 pArg->Format( aStr );
1481 // Numbers start with a space
1482 if( pArg->IsNumericRTL() )
1484 // replace commas by points so that it's symmetric to Val!
1485 aStr = aStr.replaceFirst( ",", "." );
1487 SbiInstance* pInst = GetSbData()->pInst;
1488 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1489 if( bCompatibility )
1491 sal_Int32 nLen = aStr.getLength();
1493 const sal_Unicode* pBuf = aStr.getStr();
1495 bool bNeg = ( pBuf[0] == '-' );
1496 sal_Int32 iZeroSearch = 0;
1497 if( bNeg )
1499 aStrNew += "-";
1500 iZeroSearch++;
1502 else
1504 if( pBuf[0] != ' ' )
1506 aStrNew += " ";
1509 sal_Int32 iNext = iZeroSearch + 1;
1510 if( pBuf[iZeroSearch] == '0' && nLen > iNext && pBuf[iNext] == '.' )
1512 iZeroSearch += 1;
1514 aStrNew += aStr.subView(iZeroSearch);
1516 else
1518 aStrNew = " " + aStr;
1521 else
1523 aStrNew = aStr;
1525 rPar.Get(0)->PutString(aStrNew);
1529 void SbRtl_StrComp(StarBASIC *, SbxArray & rPar, bool)
1531 if (rPar.Count() < 3)
1533 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1534 rPar.Get(0)->PutEmpty();
1535 return;
1537 const OUString& rStr1 = rPar.Get(1)->GetOUString();
1538 const OUString& rStr2 = rPar.Get(2)->GetOUString();
1540 SbiInstance* pInst = GetSbData()->pInst;
1541 bool bTextCompare;
1542 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1543 if( bCompatibility )
1545 SbiRuntime* pRT = pInst->pRun;
1546 bTextCompare = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
1548 else
1550 bTextCompare = true;
1552 if (rPar.Count() == 4)
1553 bTextCompare = rPar.Get(3)->GetInteger();
1555 if( !bCompatibility )
1557 bTextCompare = !bTextCompare;
1559 sal_Int32 nRetValue = 0;
1560 if( bTextCompare )
1562 ::utl::TransliterationWrapper* pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
1563 if( !pTransliterationWrapper )
1565 uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
1566 GetSbData()->pTransliterationWrapper.reset(
1567 new ::utl::TransliterationWrapper( xContext,
1568 TransliterationFlags::IGNORE_CASE |
1569 TransliterationFlags::IGNORE_KANA |
1570 TransliterationFlags::IGNORE_WIDTH ) );
1571 pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
1574 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
1575 pTransliterationWrapper->loadModuleIfNeeded( eLangType );
1576 nRetValue = pTransliterationWrapper->compareString( rStr1, rStr2 );
1578 else
1580 sal_Int32 aResult;
1581 aResult = rStr1.compareTo( rStr2 );
1582 if ( aResult < 0 )
1584 nRetValue = -1;
1586 else if ( aResult > 0)
1588 nRetValue = 1;
1591 rPar.Get(0)->PutInteger(sal::static_int_cast<sal_Int16>(nRetValue));
1594 void SbRtl_String(StarBASIC *, SbxArray & rPar, bool)
1596 if (rPar.Count() < 2)
1598 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1600 else
1602 sal_Unicode aFiller;
1603 sal_Int32 lCount = rPar.Get(1)->GetLong();
1604 if( lCount < 0 || lCount > 0xffff )
1606 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1608 if (rPar.Get(2)->GetType() == SbxINTEGER)
1610 aFiller = static_cast<sal_Unicode>(rPar.Get(2)->GetInteger());
1612 else
1614 const OUString& rStr = rPar.Get(2)->GetOUString();
1615 aFiller = rStr[0];
1617 OUStringBuffer aBuf(lCount);
1618 string::padToLength(aBuf, lCount, aFiller);
1619 rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1623 void SbRtl_Tab(StarBASIC *, SbxArray & rPar, bool)
1625 if (rPar.Count() < 2)
1626 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1627 else
1629 OUStringBuffer aStr;
1630 comphelper::string::padToLength(aStr, rPar.Get(1)->GetLong(), '\t');
1631 rPar.Get(0)->PutString(aStr.makeStringAndClear());
1635 void SbRtl_Tan(StarBASIC *, SbxArray & rPar, bool)
1637 if (rPar.Count() < 2)
1639 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1641 else
1643 SbxVariableRef pArg = rPar.Get(1);
1644 rPar.Get(0)->PutDouble(tan(pArg->GetDouble()));
1648 void SbRtl_UCase(StarBASIC *, SbxArray & rPar, bool)
1650 if (rPar.Count() < 2)
1652 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1654 else
1656 const CharClass& rCharClass = GetCharClass();
1657 OUString aStr(rPar.Get(1)->GetOUString());
1658 aStr = rCharClass.uppercase( aStr );
1659 rPar.Get(0)->PutString(aStr);
1664 void SbRtl_Val(StarBASIC *, SbxArray & rPar, bool)
1666 if (rPar.Count() < 2)
1668 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1670 else
1672 double nResult = 0.0;
1673 char* pEndPtr;
1675 OUString aStr(rPar.Get(1)->GetOUString());
1677 FilterWhiteSpace( aStr );
1678 if ( aStr.getLength() > 1 && aStr[0] == '&' )
1680 int nRadix = 10;
1681 char aChar = static_cast<char>(aStr[1]);
1682 if ( aChar == 'h' || aChar == 'H' )
1684 nRadix = 16;
1686 else if ( aChar == 'o' || aChar == 'O' )
1688 nRadix = 8;
1690 if ( nRadix != 10 )
1692 OString aByteStr(OUStringToOString(aStr, osl_getThreadTextEncoding()));
1693 sal_Int16 nlResult = static_cast<sal_Int16>(strtol( aByteStr.getStr()+2, &pEndPtr, nRadix));
1694 nResult = static_cast<double>(nlResult);
1697 else
1699 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
1700 sal_Int32 nParseEnd = 0;
1701 nResult = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd );
1702 if ( eStatus != rtl_math_ConversionStatus_Ok )
1703 StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW );
1704 /* TODO: we should check whether all characters were parsed here,
1705 * but earlier code silently ignored trailing nonsense such as "1x"
1706 * resulting in 1 with the side effect that any alpha-only-string
1707 * like "x" resulted in 0. Not changing that now (2013-03-22) as
1708 * user macros may rely on it. */
1709 #if 0
1710 else if ( nParseEnd != aStr.getLength() )
1711 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
1712 #endif
1715 rPar.Get(0)->PutDouble(nResult);
1720 // Helper functions for date conversion
1721 sal_Int16 implGetDateDay( double aDate )
1723 aDate -= 2.0; // standardize: 1.1.1900 => 0.0
1724 aDate = floor( aDate );
1725 Date aRefDate( 1, 1, 1900 );
1726 aRefDate.AddDays( aDate );
1728 sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetDay() );
1729 return nRet;
1732 sal_Int16 implGetDateMonth( double aDate )
1734 Date aRefDate( 1,1,1900 );
1735 sal_Int32 nDays = static_cast<sal_Int32>(aDate);
1736 nDays -= 2; // standardize: 1.1.1900 => 0.0
1737 aRefDate.AddDays( nDays );
1738 sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetMonth() );
1739 return nRet;
1742 css::util::Date SbxDateToUNODate( const SbxValue* const pVal )
1744 double aDate = pVal->GetDate();
1746 css::util::Date aUnoDate;
1747 aUnoDate.Day = implGetDateDay ( aDate );
1748 aUnoDate.Month = implGetDateMonth( aDate );
1749 aUnoDate.Year = implGetDateYear ( aDate );
1751 return aUnoDate;
1754 void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate)
1756 double dDate;
1757 if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, SbDateCorrection::None, dDate ) )
1759 pVal->PutDate( dDate );
1763 // Function to convert date to UNO date (com.sun.star.util.Date)
1764 void SbRtl_CDateToUnoDate(StarBASIC *, SbxArray & rPar, bool)
1766 if (rPar.Count() != 2)
1768 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1769 return;
1772 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODate(rPar.Get(1))));
1775 // Function to convert date from UNO date (com.sun.star.util.Date)
1776 void SbRtl_CDateFromUnoDate(StarBASIC *, SbxArray & rPar, bool)
1778 if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
1780 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1781 return;
1784 Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Date>::get()));
1785 css::util::Date aUnoDate;
1786 if(aAny >>= aUnoDate)
1787 SbxDateFromUNODate(rPar.Get(0), aUnoDate);
1788 else
1789 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1792 css::util::Time SbxDateToUNOTime( const SbxValue* const pVal )
1794 double aDate = pVal->GetDate();
1796 css::util::Time aUnoTime;
1797 aUnoTime.Hours = implGetHour ( aDate );
1798 aUnoTime.Minutes = implGetMinute ( aDate );
1799 aUnoTime.Seconds = implGetSecond ( aDate );
1800 aUnoTime.NanoSeconds = 0;
1802 return aUnoTime;
1805 void SbxDateFromUNOTime( SbxValue *pVal, const css::util::Time& aUnoTime)
1807 pVal->PutDate( implTimeSerial(aUnoTime.Hours, aUnoTime.Minutes, aUnoTime.Seconds) );
1810 // Function to convert date to UNO time (com.sun.star.util.Time)
1811 void SbRtl_CDateToUnoTime(StarBASIC *, SbxArray & rPar, bool)
1813 if (rPar.Count() != 2)
1815 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1816 return;
1819 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNOTime(rPar.Get(1))));
1822 // Function to convert date from UNO time (com.sun.star.util.Time)
1823 void SbRtl_CDateFromUnoTime(StarBASIC *, SbxArray & rPar, bool)
1825 if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
1827 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1828 return;
1831 Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Time>::get()));
1832 css::util::Time aUnoTime;
1833 if(aAny >>= aUnoTime)
1834 SbxDateFromUNOTime(rPar.Get(0), aUnoTime);
1835 else
1836 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1839 css::util::DateTime SbxDateToUNODateTime( const SbxValue* const pVal )
1841 double aDate = pVal->GetDate();
1843 css::util::DateTime aUnoDT;
1844 aUnoDT.Day = implGetDateDay ( aDate );
1845 aUnoDT.Month = implGetDateMonth( aDate );
1846 aUnoDT.Year = implGetDateYear ( aDate );
1847 aUnoDT.Hours = implGetHour ( aDate );
1848 aUnoDT.Minutes = implGetMinute ( aDate );
1849 aUnoDT.Seconds = implGetSecond ( aDate );
1850 aUnoDT.NanoSeconds = 0;
1852 return aUnoDT;
1855 void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT)
1857 double dDate(0.0);
1858 if( implDateTimeSerial( aUnoDT.Year, aUnoDT.Month, aUnoDT.Day,
1859 aUnoDT.Hours, aUnoDT.Minutes, aUnoDT.Seconds,
1860 dDate ) )
1862 pVal->PutDate( dDate );
1866 // Function to convert date to UNO date (com.sun.star.util.Date)
1867 void SbRtl_CDateToUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
1869 if (rPar.Count() != 2)
1871 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1872 return;
1875 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODateTime(rPar.Get(1))));
1878 // Function to convert date from UNO date (com.sun.star.util.Date)
1879 void SbRtl_CDateFromUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
1881 if (rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT)
1883 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1884 return;
1887 Any aAny(sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::DateTime>::get()));
1888 css::util::DateTime aUnoDT;
1889 if(aAny >>= aUnoDT)
1890 SbxDateFromUNODateTime(rPar.Get(0), aUnoDT);
1891 else
1892 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1895 // Function to convert date to ISO 8601 date format YYYYMMDD
1896 void SbRtl_CDateToIso(StarBASIC *, SbxArray & rPar, bool)
1898 if (rPar.Count() == 2)
1900 double aDate = rPar.Get(1)->GetDate();
1902 // Date may actually even be -YYYYYMMDD
1903 char Buffer[11];
1904 sal_Int16 nYear = implGetDateYear( aDate );
1905 snprintf( Buffer, sizeof( Buffer ), (nYear < 0 ? "%05d%02d%02d" : "%04d%02d%02d"),
1906 static_cast<int>(nYear),
1907 static_cast<int>(implGetDateMonth( aDate )),
1908 static_cast<int>(implGetDateDay( aDate )) );
1909 OUString aRetStr = OUString::createFromAscii( Buffer );
1910 rPar.Get(0)->PutString(aRetStr);
1912 else
1914 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1918 // Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD
1919 // And even YYMMDD for compatibility, sigh...
1920 void SbRtl_CDateFromIso(StarBASIC *, SbxArray & rPar, bool)
1922 if (rPar.Count() == 2)
1926 OUString aStr = rPar.Get(1)->GetOUString();
1927 if (aStr.isEmpty())
1928 break;
1930 // Valid formats are
1931 // YYYYMMDD -YYYMMDD YYYYYMMDD -YYYYYMMDD YYMMDD
1932 // YYYY-MM-DD -YYYY-MM-DD YYYYY-MM-DD -YYYYY-MM-DD
1934 sal_Int32 nSign = 1;
1935 if (aStr[0] == '-')
1937 nSign = -1;
1938 aStr = aStr.copy(1);
1940 const sal_Int32 nLen = aStr.getLength();
1942 // Signed YYMMDD two digit year is invalid.
1943 if (nLen == 6 && nSign == -1)
1944 break;
1946 // Now valid
1947 // YYYYMMDD YYYYYMMDD YYMMDD
1948 // YYYY-MM-DD YYYYY-MM-DD
1949 if (nLen != 6 && (nLen < 8 || 11 < nLen))
1950 break;
1952 bool bUseTwoDigitYear = false;
1953 OUString aYearStr, aMonthStr, aDayStr;
1954 if (nLen == 6 || nLen == 8 || nLen == 9)
1956 // ((Y)YY)YYMMDD
1957 if (!comphelper::string::isdigitAsciiString(aStr))
1958 break;
1960 const sal_Int32 nMonthPos = (nLen == 8 ? 4 : (nLen == 6 ? 2 : 5));
1961 if (nMonthPos == 2)
1962 bUseTwoDigitYear = true;
1963 aYearStr = aStr.copy( 0, nMonthPos );
1964 aMonthStr = aStr.copy( nMonthPos, 2 );
1965 aDayStr = aStr.copy( nMonthPos + 2, 2 );
1967 else
1969 // (Y)YYYY-MM-DD
1970 const sal_Int32 nMonthSep = (nLen == 11 ? 5 : 4);
1971 if (aStr.indexOf('-') != nMonthSep)
1972 break;
1973 if (aStr.indexOf('-', nMonthSep + 1) != nMonthSep + 3)
1974 break;
1976 aYearStr = aStr.copy( 0, nMonthSep );
1977 aMonthStr = aStr.copy( nMonthSep + 1, 2 );
1978 aDayStr = aStr.copy( nMonthSep + 4, 2 );
1979 if ( !comphelper::string::isdigitAsciiString(aYearStr) ||
1980 !comphelper::string::isdigitAsciiString(aMonthStr) ||
1981 !comphelper::string::isdigitAsciiString(aDayStr))
1982 break;
1985 double dDate;
1986 if (!implDateSerial( static_cast<sal_Int16>(nSign * aYearStr.toInt32()),
1987 static_cast<sal_Int16>(aMonthStr.toInt32()), static_cast<sal_Int16>(aDayStr.toInt32()),
1988 bUseTwoDigitYear, SbDateCorrection::None, dDate ))
1989 break;
1991 rPar.Get(0)->PutDate(dDate);
1993 return;
1995 while (false);
1997 SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER );
1999 else
2001 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2005 void SbRtl_DateSerial(StarBASIC *, SbxArray & rPar, bool)
2007 if (rPar.Count() < 4)
2009 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2010 return;
2012 sal_Int16 nYear = rPar.Get(1)->GetInteger();
2013 sal_Int16 nMonth = rPar.Get(2)->GetInteger();
2014 sal_Int16 nDay = rPar.Get(3)->GetInteger();
2016 double dDate;
2017 if( implDateSerial( nYear, nMonth, nDay, true, SbDateCorrection::RollOver, dDate ) )
2019 rPar.Get(0)->PutDate(dDate);
2023 void SbRtl_TimeSerial(StarBASIC *, SbxArray & rPar, bool)
2025 if (rPar.Count() < 4)
2027 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2028 return;
2030 sal_Int16 nHour = rPar.Get(1)->GetInteger();
2031 if ( nHour == 24 )
2033 nHour = 0; // because of UNO DateTimes, which go till 24 o'clock
2035 sal_Int16 nMinute = rPar.Get(2)->GetInteger();
2036 sal_Int16 nSecond = rPar.Get(3)->GetInteger();
2037 if ((nHour < 0 || nHour > 23) ||
2038 (nMinute < 0 || nMinute > 59 ) ||
2039 (nSecond < 0 || nSecond > 59 ))
2041 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2042 return;
2045 rPar.Get(0)->PutDate(implTimeSerial(nHour, nMinute, nSecond)); // JSM
2048 void SbRtl_DateValue(StarBASIC *, SbxArray & rPar, bool)
2050 if (rPar.Count() < 2)
2052 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2054 else
2056 // #39629 check GetSbData()->pInst, can be called from the URL line
2057 std::shared_ptr<SvNumberFormatter> pFormatter;
2058 if( GetSbData()->pInst )
2060 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2062 else
2064 sal_uInt32 n; // Dummy
2065 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
2068 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
2069 sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
2070 double fResult;
2071 OUString aStr(rPar.Get(1)->GetOUString());
2072 bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
2073 SvNumFormatType nType = pFormatter->GetType( nIndex );
2075 // DateValue("February 12, 1969") raises error if the system locale is not en_US
2076 // It seems that both locale number formatter and English number
2077 // formatter are supported in Visual Basic.
2078 if( !bSuccess && ( eLangType != LANGUAGE_ENGLISH_US ) )
2080 // Try using LANGUAGE_ENGLISH_US to get the date value.
2081 nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US);
2082 bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
2083 nType = pFormatter->GetType( nIndex );
2086 if(bSuccess && (nType==SvNumFormatType::DATE || nType==SvNumFormatType::DATETIME))
2088 if ( nType == SvNumFormatType::DATETIME )
2090 // cut time
2091 if ( fResult > 0.0 )
2093 fResult = floor( fResult );
2095 else
2097 fResult = ceil( fResult );
2100 rPar.Get(0)->PutDate(fResult);
2102 else
2104 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
2109 void SbRtl_TimeValue(StarBASIC *, SbxArray & rPar, bool)
2111 if (rPar.Count() < 2)
2113 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2115 else
2117 std::shared_ptr<SvNumberFormatter> pFormatter;
2118 if( GetSbData()->pInst )
2119 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2120 else
2122 sal_uInt32 n;
2123 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
2126 sal_uInt32 nIndex = 0;
2127 double fResult;
2128 bool bSuccess = pFormatter->IsNumberFormat(rPar.Get(1)->GetOUString(),
2129 nIndex, fResult );
2130 SvNumFormatType nType = pFormatter->GetType(nIndex);
2131 if(bSuccess && (nType==SvNumFormatType::TIME||nType==SvNumFormatType::DATETIME))
2133 if ( nType == SvNumFormatType::DATETIME )
2135 // cut days
2136 fResult = fmod( fResult, 1 );
2138 rPar.Get(0)->PutDate(fResult);
2140 else
2142 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
2147 void SbRtl_Day(StarBASIC *, SbxArray & rPar, bool)
2149 if (rPar.Count() < 2)
2151 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2153 else
2155 SbxVariableRef pArg = rPar.Get(1);
2156 double aDate = pArg->GetDate();
2158 sal_Int16 nDay = implGetDateDay( aDate );
2159 rPar.Get(0)->PutInteger(nDay);
2163 void SbRtl_Year(StarBASIC *, SbxArray & rPar, bool)
2165 if (rPar.Count() < 2)
2167 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2169 else
2171 sal_Int16 nYear = implGetDateYear(rPar.Get(1)->GetDate());
2172 rPar.Get(0)->PutInteger(nYear);
2176 sal_Int16 implGetHour( double dDate )
2178 double nFrac = dDate - floor( dDate );
2179 nFrac *= 86400.0;
2180 sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
2181 sal_Int16 nHour = static_cast<sal_Int16>(nSeconds / 3600);
2182 return nHour;
2185 void SbRtl_Hour(StarBASIC *, SbxArray & rPar, bool)
2187 if (rPar.Count() < 2)
2189 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2191 else
2193 double nArg = rPar.Get(1)->GetDate();
2194 sal_Int16 nHour = implGetHour( nArg );
2195 rPar.Get(0)->PutInteger(nHour);
2199 void SbRtl_Minute(StarBASIC *, SbxArray & rPar, bool)
2201 if (rPar.Count() < 2)
2203 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2205 else
2207 double nArg = rPar.Get(1)->GetDate();
2208 sal_Int16 nMin = implGetMinute( nArg );
2209 rPar.Get(0)->PutInteger(nMin);
2213 void SbRtl_Month(StarBASIC *, SbxArray & rPar, bool)
2215 if (rPar.Count() < 2)
2217 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2219 else
2221 sal_Int16 nMonth = implGetDateMonth(rPar.Get(1)->GetDate());
2222 rPar.Get(0)->PutInteger(nMonth);
2226 sal_Int16 implGetSecond( double dDate )
2228 double nFrac = dDate - floor( dDate );
2229 nFrac *= 86400.0;
2230 sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
2231 sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds / 3600);
2232 nSeconds -= nTemp * 3600;
2233 nTemp = static_cast<sal_Int16>(nSeconds / 60);
2234 nSeconds -= nTemp * 60;
2236 sal_Int16 nRet = static_cast<sal_Int16>(nSeconds);
2237 return nRet;
2240 void SbRtl_Second(StarBASIC *, SbxArray & rPar, bool)
2242 if (rPar.Count() < 2)
2244 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2246 else
2248 double nArg = rPar.Get(1)->GetDate();
2249 sal_Int16 nSecond = implGetSecond( nArg );
2250 rPar.Get(0)->PutInteger(nSecond);
2254 double Now_Impl()
2256 DateTime aDateTime( DateTime::SYSTEM );
2257 double aSerial = static_cast<double>(GetDayDiff( aDateTime ));
2258 tools::Long nSeconds = aDateTime.GetHour();
2259 nSeconds *= 3600;
2260 nSeconds += aDateTime.GetMin() * 60;
2261 nSeconds += aDateTime.GetSec();
2262 double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
2263 aSerial += nDays;
2264 return aSerial;
2267 // Date Now()
2269 void SbRtl_Now(StarBASIC*, SbxArray& rPar, bool) { rPar.Get(0)->PutDate(Now_Impl()); }
2271 // Date Time()
2273 void SbRtl_Time(StarBASIC *, SbxArray & rPar, bool bWrite)
2275 if ( !bWrite )
2277 tools::Time aTime( tools::Time::SYSTEM );
2278 SbxVariable* pMeth = rPar.Get(0);
2279 OUString aRes;
2280 if( pMeth->IsFixed() )
2282 // Time$: hh:mm:ss
2283 char buf[ 20 ];
2284 snprintf( buf, sizeof(buf), "%02d:%02d:%02d",
2285 aTime.GetHour(), aTime.GetMin(), aTime.GetSec() );
2286 aRes = OUString::createFromAscii( buf );
2288 else
2290 // Time: system dependent
2291 tools::Long nSeconds=aTime.GetHour();
2292 nSeconds *= 3600;
2293 nSeconds += aTime.GetMin() * 60;
2294 nSeconds += aTime.GetSec();
2295 double nDays = static_cast<double>(nSeconds) * ( 1.0 / (24.0*3600.0) );
2296 const Color* pCol;
2298 std::shared_ptr<SvNumberFormatter> pFormatter;
2299 sal_uInt32 nIndex;
2300 if( GetSbData()->pInst )
2302 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2303 nIndex = GetSbData()->pInst->GetStdTimeIdx();
2305 else
2307 sal_uInt32 n; // Dummy
2308 pFormatter = SbiInstance::PrepareNumberFormatter( n, nIndex, n );
2311 pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
2313 pMeth->PutString( aRes );
2315 else
2317 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
2321 void SbRtl_Timer(StarBASIC *, SbxArray & rPar, bool)
2323 tools::Time aTime( tools::Time::SYSTEM );
2324 tools::Long nSeconds = aTime.GetHour();
2325 nSeconds *= 3600;
2326 nSeconds += aTime.GetMin() * 60;
2327 nSeconds += aTime.GetSec();
2328 rPar.Get(0)->PutDate(static_cast<double>(nSeconds));
2332 void SbRtl_Date(StarBASIC *, SbxArray & rPar, bool bWrite)
2334 if ( !bWrite )
2336 Date aToday( Date::SYSTEM );
2337 double nDays = static_cast<double>(GetDayDiff( aToday ));
2338 SbxVariable* pMeth = rPar.Get(0);
2339 if( pMeth->IsString() )
2341 OUString aRes;
2342 const Color* pCol;
2344 std::shared_ptr<SvNumberFormatter> pFormatter;
2345 sal_uInt32 nIndex;
2346 if( GetSbData()->pInst )
2348 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2349 nIndex = GetSbData()->pInst->GetStdDateIdx();
2351 else
2353 sal_uInt32 n;
2354 pFormatter = SbiInstance::PrepareNumberFormatter( nIndex, n, n );
2357 pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
2358 pMeth->PutString( aRes );
2360 else
2362 pMeth->PutDate( nDays );
2365 else
2367 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
2371 void SbRtl_IsArray(StarBASIC *, SbxArray & rPar, bool)
2373 if (rPar.Count() < 2)
2375 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2377 else
2379 rPar.Get(0)->PutBool((rPar.Get(1)->GetType() & SbxARRAY) != 0);
2383 void SbRtl_IsObject(StarBASIC *, SbxArray & rPar, bool)
2385 if (rPar.Count() < 2)
2387 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2389 else
2391 SbxVariable* pVar = rPar.Get(1);
2392 bool bObject = pVar->IsObject();
2393 SbxBase* pObj = (bObject ? pVar->GetObject() : nullptr);
2395 if( auto pUnoClass = dynamic_cast<SbUnoClass*>( pObj) )
2397 bObject = pUnoClass->getUnoClass().is();
2399 rPar.Get(0)->PutBool(bObject);
2403 void SbRtl_IsDate(StarBASIC *, SbxArray & rPar, bool)
2405 if (rPar.Count() < 2)
2407 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2409 else
2411 // #46134 only string is converted, all other types result in sal_False
2412 SbxVariableRef xArg = rPar.Get(1);
2413 SbxDataType eType = xArg->GetType();
2414 bool bDate = false;
2416 if( eType == SbxDATE )
2418 bDate = true;
2420 else if( eType == SbxSTRING )
2422 ErrCode nPrevError = SbxBase::GetError();
2423 SbxBase::ResetError();
2425 // force conversion of the parameter to SbxDATE
2426 xArg->SbxValue::GetDate();
2428 bDate = !SbxBase::IsError();
2430 SbxBase::ResetError();
2431 SbxBase::SetError( nPrevError );
2433 rPar.Get(0)->PutBool(bDate);
2437 void SbRtl_IsEmpty(StarBASIC *, SbxArray & rPar, bool)
2439 if (rPar.Count() < 2)
2441 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2443 else
2445 SbxVariable* pVar = nullptr;
2446 if( SbiRuntime::isVBAEnabled() )
2448 pVar = getDefaultProp(rPar.Get(1));
2450 if ( pVar )
2452 pVar->Broadcast( SfxHintId::BasicDataWanted );
2453 rPar.Get(0)->PutBool(pVar->IsEmpty());
2455 else
2457 rPar.Get(0)->PutBool(rPar.Get(1)->IsEmpty());
2462 void SbRtl_IsError(StarBASIC *, SbxArray & rPar, bool)
2464 if (rPar.Count() < 2)
2466 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2468 else
2470 SbxVariable* pVar = rPar.Get(1);
2471 SbUnoObject* pObj = dynamic_cast<SbUnoObject*>( pVar );
2472 if ( !pObj )
2474 if ( SbxBase* pBaseObj = (pVar->IsObject() ? pVar->GetObject() : nullptr) )
2476 pObj = dynamic_cast<SbUnoObject*>( pBaseObj );
2479 uno::Reference< script::XErrorQuery > xError;
2480 if ( pObj )
2482 xError.set( pObj->getUnoAny(), uno::UNO_QUERY );
2484 if ( xError.is() )
2486 rPar.Get(0)->PutBool(xError->hasError());
2488 else
2490 rPar.Get(0)->PutBool(rPar.Get(1)->IsErr());
2495 void SbRtl_IsNull(StarBASIC *, SbxArray & rPar, bool)
2497 if (rPar.Count() < 2)
2499 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2501 else
2503 // #51475 because of Uno-objects return true
2504 // even if the pObj value is NULL
2505 SbxVariableRef pArg = rPar.Get(1);
2506 bool bNull = rPar.Get(1)->IsNull();
2507 if( !bNull && pArg->GetType() == SbxOBJECT )
2509 SbxBase* pObj = pArg->GetObject();
2510 if( !pObj )
2512 bNull = true;
2515 rPar.Get(0)->PutBool(bNull);
2519 void SbRtl_IsNumeric(StarBASIC *, SbxArray & rPar, bool)
2521 if (rPar.Count() < 2)
2523 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2525 else
2527 rPar.Get(0)->PutBool(rPar.Get(1)->IsNumericRTL());
2532 void SbRtl_IsMissing(StarBASIC *, SbxArray & rPar, bool)
2534 if (rPar.Count() < 2)
2536 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2538 else
2540 // #57915 Missing is reported by an error
2541 rPar.Get(0)->PutBool(rPar.Get(1)->IsErr());
2545 // Function looks for wildcards, removes them and always returns the pure path
2546 static OUString implSetupWildcard(const OUString& rFileParam, SbiRTLData& rRTLData)
2548 static const char cDelim1 = '/';
2549 static const char cDelim2 = '\\';
2550 static const char cWild1 = '*';
2551 static const char cWild2 = '?';
2553 rRTLData.pWildCard.reset();
2554 rRTLData.sFullNameToBeChecked.clear();
2556 OUString aFileParam = rFileParam;
2557 sal_Int32 nLastWild = aFileParam.lastIndexOf( cWild1 );
2558 if( nLastWild < 0 )
2560 nLastWild = aFileParam.lastIndexOf( cWild2 );
2562 bool bHasWildcards = ( nLastWild >= 0 );
2565 sal_Int32 nLastDelim = aFileParam.lastIndexOf( cDelim1 );
2566 if( nLastDelim < 0 )
2568 nLastDelim = aFileParam.lastIndexOf( cDelim2 );
2570 if( bHasWildcards )
2572 // Wildcards in path?
2573 if( nLastDelim >= 0 && nLastDelim > nLastWild )
2575 return aFileParam;
2578 else
2580 OUString aPathStr = getFullPath( aFileParam );
2581 if( nLastDelim != aFileParam.getLength() - 1 )
2583 rRTLData.sFullNameToBeChecked = aPathStr;
2585 return aPathStr;
2588 OUString aPureFileName;
2589 if( nLastDelim < 0 )
2591 aPureFileName = aFileParam;
2592 aFileParam.clear();
2594 else
2596 aPureFileName = aFileParam.copy( nLastDelim + 1 );
2597 aFileParam = aFileParam.copy( 0, nLastDelim );
2600 // Try again to get a valid URL/UNC-path with only the path
2601 OUString aPathStr = getFullPath( aFileParam );
2603 // Is there a pure file name left? Otherwise the path is
2604 // invalid anyway because it was not accepted by OSL before
2605 if (aPureFileName != "*")
2607 rRTLData.pWildCard = std::make_unique<WildCard>(aPureFileName);
2609 return aPathStr;
2612 static bool implCheckWildcard(std::u16string_view rName, SbiRTLData const& rRTLData)
2614 bool bMatch = true;
2616 if (rRTLData.pWildCard)
2618 bMatch = rRTLData.pWildCard->Matches(rName);
2620 return bMatch;
2624 static bool isRootDir( const OUString& aDirURLStr )
2626 INetURLObject aDirURLObj( aDirURLStr );
2627 bool bRoot = false;
2629 // Check if it's a root directory
2630 sal_Int32 nCount = aDirURLObj.getSegmentCount();
2632 // No segment means Unix root directory "file:///"
2633 if( nCount == 0 )
2635 bRoot = true;
2637 // Exactly one segment needs further checking, because it
2638 // can be Unix "file:///foo/" -> no root
2639 // or Windows "file:///c:/" -> root
2640 else if( nCount == 1 )
2642 OUString aSeg1 = aDirURLObj.getName( 0, true,
2643 INetURLObject::DecodeMechanism::WithCharset );
2644 if( aSeg1[1] == ':' )
2646 bRoot = true;
2649 // More than one segments can never be root
2650 // so bRoot remains false
2652 return bRoot;
2655 void SbRtl_Dir(StarBASIC *, SbxArray & rPar, bool)
2657 OUString aPath;
2659 const sal_uInt32 nParCount = rPar.Count();
2660 if( nParCount > 3 )
2662 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2664 else
2666 SbiRTLData& rRTLData = GetSbData()->pInst->GetRTLData();
2668 if( hasUno() )
2670 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2671 if( xSFI.is() )
2673 if ( nParCount >= 2 )
2675 OUString aFileParam = rPar.Get(1)->GetOUString();
2677 OUString aFileURLStr = implSetupWildcard(aFileParam, rRTLData);
2678 if (!rRTLData.sFullNameToBeChecked.isEmpty())
2680 bool bExists = false;
2681 try { bExists = xSFI->exists( aFileURLStr ); }
2682 catch(const Exception & ) {}
2684 OUString aNameOnlyStr;
2685 if( bExists )
2687 INetURLObject aFileURL( aFileURLStr );
2688 aNameOnlyStr = aFileURL.getName( INetURLObject::LAST_SEGMENT,
2689 true, INetURLObject::DecodeMechanism::WithCharset );
2691 rPar.Get(0)->PutString(aNameOnlyStr);
2692 return;
2697 OUString aDirURLStr;
2698 bool bFolder = xSFI->isFolder( aFileURLStr );
2700 if( bFolder )
2702 aDirURLStr = aFileURLStr;
2704 else
2706 rPar.Get(0)->PutString("");
2709 SbAttributes nFlags = SbAttributes::NONE;
2710 if ( nParCount > 2 )
2712 rRTLData.nDirFlags = nFlags
2713 = static_cast<SbAttributes>(rPar.Get(2)->GetInteger());
2715 else
2717 rRTLData.nDirFlags = SbAttributes::NONE;
2719 // Read directory
2720 bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
2721 rRTLData.aDirSeq = xSFI->getFolderContents(aDirURLStr, bIncludeFolders);
2722 rRTLData.nCurDirPos = 0;
2724 // #78651 Add "." and ".." directories for VB compatibility
2725 if( bIncludeFolders )
2727 bool bRoot = isRootDir( aDirURLStr );
2729 // If it's no root directory we flag the need for
2730 // the "." and ".." directories by the value -2
2731 // for the actual position. Later for -2 will be
2732 // returned "." and for -1 ".."
2733 if( !bRoot )
2735 rRTLData.nCurDirPos = -2;
2739 catch(const Exception & )
2745 if (rRTLData.aDirSeq.hasElements())
2747 bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY);
2749 SbiInstance* pInst = GetSbData()->pInst;
2750 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
2751 for( ;; )
2753 if (rRTLData.nCurDirPos < 0)
2755 if (rRTLData.nCurDirPos == -2)
2757 aPath = ".";
2759 else if (rRTLData.nCurDirPos == -1)
2761 aPath = "..";
2763 rRTLData.nCurDirPos++;
2765 else if (rRTLData.nCurDirPos >= rRTLData.aDirSeq.getLength())
2767 rRTLData.aDirSeq.realloc(0);
2768 aPath.clear();
2769 break;
2771 else
2773 OUString aFile
2774 = rRTLData.aDirSeq.getConstArray()[rRTLData.nCurDirPos++];
2776 if( bCompatibility )
2778 if( !bFolderFlag )
2780 bool bFolder = xSFI->isFolder( aFile );
2781 if( bFolder )
2783 continue;
2787 else
2789 // Only directories
2790 if( bFolderFlag )
2792 bool bFolder = xSFI->isFolder( aFile );
2793 if( !bFolder )
2795 continue;
2800 INetURLObject aURL( aFile );
2801 aPath = aURL.getName( INetURLObject::LAST_SEGMENT, true,
2802 INetURLObject::DecodeMechanism::WithCharset );
2805 bool bMatch = implCheckWildcard(aPath, rRTLData);
2806 if( !bMatch )
2808 continue;
2810 break;
2813 rPar.Get(0)->PutString(aPath);
2816 else
2818 // TODO: OSL
2819 if ( nParCount >= 2 )
2821 OUString aFileParam = rPar.Get(1)->GetOUString();
2823 OUString aDirURL = implSetupWildcard(aFileParam, rRTLData);
2825 SbAttributes nFlags = SbAttributes::NONE;
2826 if ( nParCount > 2 )
2828 rRTLData.nDirFlags = nFlags
2829 = static_cast<SbAttributes>(rPar.Get(2)->GetInteger());
2831 else
2833 rRTLData.nDirFlags = SbAttributes::NONE;
2836 // Read directory
2837 bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
2838 rRTLData.pDir = std::make_unique<Directory>(aDirURL);
2839 FileBase::RC nRet = rRTLData.pDir->open();
2840 if( nRet != FileBase::E_None )
2842 rRTLData.pDir.reset();
2843 rPar.Get(0)->PutString(OUString());
2844 return;
2847 // #86950 Add "." and ".." directories for VB compatibility
2848 rRTLData.nCurDirPos = 0;
2849 if( bIncludeFolders )
2851 bool bRoot = isRootDir( aDirURL );
2853 // If it's no root directory we flag the need for
2854 // the "." and ".." directories by the value -2
2855 // for the actual position. Later for -2 will be
2856 // returned "." and for -1 ".."
2857 if( !bRoot )
2859 rRTLData.nCurDirPos = -2;
2865 if (rRTLData.pDir)
2867 bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY);
2868 for( ;; )
2870 if (rRTLData.nCurDirPos < 0)
2872 if (rRTLData.nCurDirPos == -2)
2874 aPath = ".";
2876 else if (rRTLData.nCurDirPos == -1)
2878 aPath = "..";
2880 rRTLData.nCurDirPos++;
2882 else
2884 DirectoryItem aItem;
2885 FileBase::RC nRet = rRTLData.pDir->getNextItem(aItem);
2886 if( nRet != FileBase::E_None )
2888 rRTLData.pDir.reset();
2889 aPath.clear();
2890 break;
2893 // Handle flags
2894 FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName );
2895 nRet = aItem.getFileStatus( aFileStatus );
2896 if( nRet != FileBase::E_None )
2898 SAL_WARN("basic", "getFileStatus failed");
2899 continue;
2902 // Only directories?
2903 if( bFolderFlag )
2905 FileStatus::Type aType = aFileStatus.getFileType();
2906 bool bFolder = isFolder( aType );
2907 if( !bFolder )
2909 continue;
2913 aPath = aFileStatus.getFileName();
2916 bool bMatch = implCheckWildcard(aPath, rRTLData);
2917 if( !bMatch )
2919 continue;
2921 break;
2924 rPar.Get(0)->PutString(aPath);
2930 void SbRtl_GetAttr(StarBASIC *, SbxArray & rPar, bool)
2932 if (rPar.Count() == 2)
2934 sal_Int16 nFlags = 0;
2936 // In Windows, we want to use Windows API to get the file attributes
2937 // for VBA interoperability.
2938 #if defined(_WIN32)
2939 if( SbiRuntime::isVBAEnabled() )
2941 OUString aPathURL = getFullPath(rPar.Get(1)->GetOUString());
2942 OUString aPath;
2943 FileBase::getSystemPathFromFileURL( aPathURL, aPath );
2944 DWORD nRealFlags = GetFileAttributesW (o3tl::toW(aPath.getStr()));
2945 if (nRealFlags != 0xffffffff)
2947 if (nRealFlags == FILE_ATTRIBUTE_NORMAL)
2949 nRealFlags = 0;
2951 nFlags = static_cast<sal_Int16>(nRealFlags);
2953 else
2955 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
2957 rPar.Get(0)->PutInteger(nFlags);
2959 return;
2961 #endif
2963 if( hasUno() )
2965 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2966 if( xSFI.is() )
2970 OUString aPath = getFullPath(rPar.Get(1)->GetOUString());
2971 bool bExists = false;
2972 try { bExists = xSFI->exists( aPath ); }
2973 catch(const Exception & ) {}
2974 if( !bExists )
2976 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
2977 return;
2980 bool bReadOnly = xSFI->isReadOnly( aPath );
2981 bool bHidden = xSFI->isHidden( aPath );
2982 bool bDirectory = xSFI->isFolder( aPath );
2983 if( bReadOnly )
2985 nFlags |= sal_uInt16(SbAttributes::READONLY);
2987 if( bHidden )
2989 nFlags |= sal_uInt16(SbAttributes::HIDDEN);
2991 if( bDirectory )
2993 nFlags |= sal_uInt16(SbAttributes::DIRECTORY);
2996 catch(const Exception & )
2998 StarBASIC::Error( ERRCODE_IO_GENERAL );
3002 else
3004 DirectoryItem aItem;
3005 (void)DirectoryItem::get(getFullPath(rPar.Get(1)->GetOUString()), aItem);
3006 FileStatus aFileStatus( osl_FileStatus_Mask_Attributes | osl_FileStatus_Mask_Type );
3007 (void)aItem.getFileStatus( aFileStatus );
3008 sal_uInt64 nAttributes = aFileStatus.getAttributes();
3009 bool bReadOnly = (nAttributes & osl_File_Attribute_ReadOnly) != 0;
3011 FileStatus::Type aType = aFileStatus.getFileType();
3012 bool bDirectory = isFolder( aType );
3013 if( bReadOnly )
3015 nFlags |= sal_uInt16(SbAttributes::READONLY);
3017 if( bDirectory )
3019 nFlags |= sal_uInt16(SbAttributes::DIRECTORY);
3022 rPar.Get(0)->PutInteger(nFlags);
3024 else
3026 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3031 void SbRtl_FileDateTime(StarBASIC *, SbxArray & rPar, bool)
3033 if (rPar.Count() != 2)
3035 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3037 else
3039 OUString aPath = rPar.Get(1)->GetOUString();
3040 tools::Time aTime( tools::Time::EMPTY );
3041 Date aDate( Date::EMPTY );
3042 if( hasUno() )
3044 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
3045 if( xSFI.is() )
3049 util::DateTime aUnoDT = xSFI->getDateTimeModified( aPath );
3050 aTime = tools::Time( aUnoDT );
3051 aDate = Date( aUnoDT );
3053 catch(const Exception & )
3055 StarBASIC::Error( ERRCODE_IO_GENERAL );
3059 else
3061 bool bSuccess = false;
3064 DirectoryItem aItem;
3065 if (DirectoryItem::get( getFullPath( aPath ), aItem ) != FileBase::E_None)
3066 break;
3068 FileStatus aFileStatus( osl_FileStatus_Mask_ModifyTime );
3069 if (aItem.getFileStatus( aFileStatus ) != FileBase::E_None)
3070 break;
3072 TimeValue aTimeVal = aFileStatus.getModifyTime();
3073 oslDateTime aDT;
3074 if (!osl_getDateTimeFromTimeValue( &aTimeVal, &aDT ))
3075 // Strictly spoken this is not an i/o error but some other failure.
3076 break;
3078 aTime = tools::Time( aDT.Hours, aDT.Minutes, aDT.Seconds, aDT.NanoSeconds );
3079 aDate = Date( aDT.Day, aDT.Month, aDT.Year );
3080 bSuccess = true;
3082 while(false);
3084 if (!bSuccess)
3085 StarBASIC::Error( ERRCODE_IO_GENERAL );
3088 // An empty date shall not result in a formatted null-date (1899-12-30
3089 // or 1900-01-01) or even worse -0001-12-03 or some such due to how
3090 // GetDayDiff() treats things. There should be an error set in this
3091 // case anyway because of a missing file or other error above, but... so
3092 // do not even bother to use the number formatter.
3093 OUString aRes;
3094 if (aDate.IsEmpty())
3096 aRes = "0000-00-00 00:00:00";
3098 else
3100 double fSerial = static_cast<double>(GetDayDiff( aDate ));
3101 tools::Long nSeconds = aTime.GetHour();
3102 nSeconds *= 3600;
3103 nSeconds += aTime.GetMin() * 60;
3104 nSeconds += aTime.GetSec();
3105 double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
3106 fSerial += nDays;
3108 const Color* pCol;
3110 std::shared_ptr<SvNumberFormatter> pFormatter;
3111 sal_uInt32 nIndex;
3112 if( GetSbData()->pInst )
3114 pFormatter = GetSbData()->pInst->GetNumberFormatter();
3115 nIndex = GetSbData()->pInst->GetStdDateTimeIdx();
3117 else
3119 sal_uInt32 n;
3120 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, nIndex );
3123 pFormatter->GetOutputString( fSerial, nIndex, aRes, &pCol );
3125 rPar.Get(0)->PutString(aRes);
3130 void SbRtl_EOF(StarBASIC *, SbxArray & rPar, bool)
3132 // No changes for UCB
3133 if (rPar.Count() != 2)
3135 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3137 else
3139 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3140 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3141 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3142 if ( !pSbStrm )
3144 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3145 return;
3147 bool beof;
3148 SvStream* pSvStrm = pSbStrm->GetStrm();
3149 if ( pSbStrm->IsText() )
3151 char cBla;
3152 (*pSvStrm).ReadChar( cBla ); // can we read another character?
3153 beof = pSvStrm->eof();
3154 if ( !beof )
3156 pSvStrm->SeekRel( -1 );
3159 else
3161 beof = pSvStrm->eof(); // for binary data!
3163 rPar.Get(0)->PutBool(beof);
3167 void SbRtl_FileAttr(StarBASIC *, SbxArray & rPar, bool)
3169 // No changes for UCB
3170 // #57064 Although this function doesn't operate with DirEntry, it is
3171 // not touched by the adjustment to virtual URLs, as it only works on
3172 // already opened files and the name doesn't matter there.
3174 if (rPar.Count() != 3)
3176 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3178 else
3180 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3181 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3182 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3183 if ( !pSbStrm )
3185 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3186 return;
3188 sal_Int16 nRet;
3189 if (rPar.Get(2)->GetInteger() == 1)
3191 nRet = static_cast<sal_Int16>(pSbStrm->GetMode());
3193 else
3195 nRet = 0; // System file handle not supported
3197 rPar.Get(0)->PutInteger(nRet);
3200 void SbRtl_Loc(StarBASIC *, SbxArray & rPar, bool)
3202 // No changes for UCB
3203 if (rPar.Count() != 2)
3205 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3207 else
3209 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3210 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3211 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3212 if ( !pSbStrm )
3214 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3215 return;
3217 SvStream* pSvStrm = pSbStrm->GetStrm();
3218 std::size_t nPos;
3219 if( pSbStrm->IsRandom())
3221 short nBlockLen = pSbStrm->GetBlockLen();
3222 nPos = nBlockLen ? (pSvStrm->Tell() / nBlockLen) : 0;
3223 nPos++; // block positions starting at 1
3225 else if ( pSbStrm->IsText() )
3227 nPos = pSbStrm->GetLine();
3229 else if( pSbStrm->IsBinary() )
3231 nPos = pSvStrm->Tell();
3233 else if ( pSbStrm->IsSeq() )
3235 nPos = ( pSvStrm->Tell()+1 ) / 128;
3237 else
3239 nPos = pSvStrm->Tell();
3241 rPar.Get(0)->PutLong(static_cast<sal_Int32>(nPos));
3245 void SbRtl_Lof(StarBASIC *, SbxArray & rPar, bool)
3247 // No changes for UCB
3248 if (rPar.Count() != 2)
3250 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3252 else
3254 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3255 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3256 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3257 if ( !pSbStrm )
3259 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3260 return;
3262 SvStream* pSvStrm = pSbStrm->GetStrm();
3263 sal_uInt64 const nLen = pSvStrm->TellEnd();
3264 rPar.Get(0)->PutLong(static_cast<sal_Int32>(nLen));
3269 void SbRtl_Seek(StarBASIC *, SbxArray & rPar, bool)
3271 // No changes for UCB
3272 int nArgs = static_cast<int>(rPar.Count());
3273 if ( nArgs < 2 || nArgs > 3 )
3275 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3276 return;
3278 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3279 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3280 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3281 if ( !pSbStrm )
3283 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3284 return;
3286 SvStream* pStrm = pSbStrm->GetStrm();
3288 if ( nArgs == 2 ) // Seek-Function
3290 sal_uInt64 nPos = pStrm->Tell();
3291 if( pSbStrm->IsRandom() )
3293 nPos = nPos / pSbStrm->GetBlockLen();
3295 nPos++; // Basic counts from 1
3296 rPar.Get(0)->PutLong(static_cast<sal_Int32>(nPos));
3298 else // Seek-Statement
3300 sal_Int32 nPos = rPar.Get(2)->GetLong();
3301 if ( nPos < 1 )
3303 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3304 return;
3306 nPos--; // Basic counts from 1, SvStreams count from 0
3307 pSbStrm->SetExpandOnWriteTo( 0 );
3308 if ( pSbStrm->IsRandom() )
3310 nPos *= pSbStrm->GetBlockLen();
3312 pStrm->Seek( static_cast<sal_uInt64>(nPos) );
3313 pSbStrm->SetExpandOnWriteTo( nPos );
3317 void SbRtl_Format(StarBASIC *, SbxArray & rPar, bool)
3319 const sal_uInt32 nArgCount = rPar.Count();
3320 if ( nArgCount < 2 || nArgCount > 3 )
3322 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3324 else
3326 OUString aResult;
3327 if( nArgCount == 2 )
3329 rPar.Get(1)->Format(aResult);
3331 else
3333 OUString aFmt(rPar.Get(2)->GetOUString());
3334 rPar.Get(1)->Format(aResult, &aFmt);
3336 rPar.Get(0)->PutString(aResult);
3340 static void lcl_FormatNumberPercent(SbxArray& rPar, bool isPercent)
3342 const sal_uInt32 nArgCount = rPar.Count();
3343 if (nArgCount < 2 || nArgCount > 6)
3345 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3346 return;
3349 // The UI locale never changes -> we can use static value here
3350 static const LocaleDataWrapper localeData(Application::GetSettings().GetUILanguageTag());
3351 sal_Int16 nNumDigitsAfterDecimal = -1;
3352 if (nArgCount > 2 && !rPar.Get(2)->IsEmpty())
3354 nNumDigitsAfterDecimal = rPar.Get(2)->GetInteger();
3355 if (nNumDigitsAfterDecimal < -1)
3357 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3358 return;
3360 else if (nNumDigitsAfterDecimal > 255)
3361 nNumDigitsAfterDecimal %= 256;
3363 if (nNumDigitsAfterDecimal == -1)
3364 nNumDigitsAfterDecimal = LocaleDataWrapper::getNumDigits();
3366 bool bIncludeLeadingDigit = LocaleDataWrapper::isNumLeadingZero();
3367 if (nArgCount > 3 && !rPar.Get(3)->IsEmpty())
3369 switch (rPar.Get(3)->GetInteger())
3371 case ooo::vba::VbTriState::vbFalse:
3372 bIncludeLeadingDigit = false;
3373 break;
3374 case ooo::vba::VbTriState::vbTrue:
3375 bIncludeLeadingDigit = true;
3376 break;
3377 case ooo::vba::VbTriState::vbUseDefault:
3378 // do nothing;
3379 break;
3380 default:
3381 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3382 return;
3386 bool bUseParensForNegativeNumbers = false;
3387 if (nArgCount > 4 && !rPar.Get(4)->IsEmpty())
3389 switch (rPar.Get(4)->GetInteger())
3391 case ooo::vba::VbTriState::vbFalse:
3392 case ooo::vba::VbTriState::vbUseDefault:
3393 // do nothing
3394 break;
3395 case ooo::vba::VbTriState::vbTrue:
3396 bUseParensForNegativeNumbers = true;
3397 break;
3398 default:
3399 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3400 return;
3404 bool bGroupDigits = false;
3405 if (nArgCount > 5 && !rPar.Get(5)->IsEmpty())
3407 switch (rPar.Get(5)->GetInteger())
3409 case ooo::vba::VbTriState::vbFalse:
3410 case ooo::vba::VbTriState::vbUseDefault:
3411 // do nothing
3412 break;
3413 case ooo::vba::VbTriState::vbTrue:
3414 bGroupDigits = true;
3415 break;
3416 default:
3417 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3418 return;
3422 double fVal = rPar.Get(1)->GetDouble();
3423 if (isPercent)
3424 fVal *= 100;
3425 const bool bNegative = fVal < 0;
3426 if (bNegative)
3427 fVal = fabs(fVal); // Always work with non-negatives, to easily handle leading zero
3429 static const sal_Unicode decSep = localeData.getNumDecimalSep().toChar();
3430 OUString aResult = rtl::math::doubleToUString(
3431 fVal, rtl_math_StringFormat_F, nNumDigitsAfterDecimal, decSep,
3432 bGroupDigits ? localeData.getDigitGrouping().getConstArray() : nullptr,
3433 localeData.getNumThousandSep().toChar());
3435 if (!bIncludeLeadingDigit && aResult.getLength() > 1 && aResult.startsWith("0"))
3436 aResult = aResult.copy(1);
3438 if (nNumDigitsAfterDecimal > 0)
3440 sal_Int32 nActualDigits;
3441 const sal_Int32 nSepPos = aResult.indexOf(decSep);
3442 if (nSepPos == -1)
3443 nActualDigits = 0;
3444 else
3445 nActualDigits = aResult.getLength() - nSepPos - 1;
3447 // VBA allows up to 255 digits; rtl::math::doubleToUString outputs up to 15 digits
3448 // for ~small numbers, so pad them as appropriate.
3449 if (nActualDigits < nNumDigitsAfterDecimal)
3451 OUStringBuffer sBuf;
3452 comphelper::string::padToLength(sBuf, nNumDigitsAfterDecimal - nActualDigits, '0');
3453 aResult += sBuf;
3457 if (bNegative)
3459 if (bUseParensForNegativeNumbers)
3460 aResult = "(" + aResult + ")";
3461 else
3462 aResult = "-" + aResult;
3465 rPar.Get(0)->PutString(aResult);
3466 if (isPercent)
3467 aResult += "%";
3470 // https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatnumber-function
3471 void SbRtl_FormatNumber(StarBASIC*, SbxArray& rPar, bool)
3473 return lcl_FormatNumberPercent(rPar, false);
3476 // https://docs.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/formatpercent-function
3477 void SbRtl_FormatPercent(StarBASIC*, SbxArray& rPar, bool)
3479 return lcl_FormatNumberPercent(rPar, true);
3482 namespace {
3484 // note: BASIC does not use comphelper::random, because
3485 // Randomize(int) must be supported and should not affect non-BASIC random use
3486 struct RandomNumberGenerator
3488 std::mt19937 global_rng;
3490 RandomNumberGenerator()
3494 std::random_device rd;
3495 // initialises the state of the global random number generator
3496 // should only be called once.
3497 // (note, a few std::variate_generator<> (like normal) have their
3498 // own state which would need a reset as well to guarantee identical
3499 // sequence of numbers, e.g. via myrand.distribution().reset())
3500 global_rng.seed(rd() ^ time(nullptr));
3502 catch (std::runtime_error& e)
3504 SAL_WARN("basic", "Using std::random_device failed: " << e.what());
3505 global_rng.seed(time(nullptr));
3510 class theRandomNumberGenerator : public rtl::Static<RandomNumberGenerator, theRandomNumberGenerator> {};
3514 void SbRtl_Randomize(StarBASIC *, SbxArray & rPar, bool)
3516 if (rPar.Count() > 2)
3518 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3520 if (rPar.Count() == 2)
3522 int nSeed = static_cast<int>(rPar.Get(1)->GetInteger());
3523 theRandomNumberGenerator::get().global_rng.seed(nSeed);
3525 // without parameter, no need to do anything - RNG is seeded at first use
3528 void SbRtl_Rnd(StarBASIC *, SbxArray & rPar, bool)
3530 if (rPar.Count() > 2)
3532 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3534 else
3536 std::uniform_real_distribution<double> dist(0.0, 1.0);
3537 double const tmp(dist(theRandomNumberGenerator::get().global_rng));
3538 rPar.Get(0)->PutDouble(tmp);
3543 // Syntax: Shell("Path",[ Window-Style,[ "Params", [ bSync = sal_False ]]])
3544 // WindowStyles (VBA compatible):
3545 // 2 == Minimized
3546 // 3 == Maximized
3547 // 10 == Full-Screen (text mode applications OS/2, WIN95, WNT)
3548 // HACK: The WindowStyle will be passed to
3549 // Application::StartApp in Creator. Format: "xxxx2"
3552 void SbRtl_Shell(StarBASIC *, SbxArray & rPar, bool)
3554 const sal_uInt32 nArgCount = rPar.Count();
3555 if ( nArgCount < 2 || nArgCount > 5 )
3557 rPar.Get(0)->PutLong(0);
3558 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3560 else
3562 oslProcessOption nOptions = osl_Process_SEARCHPATH | osl_Process_DETACHED;
3564 OUString aCmdLine = rPar.Get(1)->GetOUString();
3565 // attach additional parameters - everything must be parsed anyway
3566 if( nArgCount >= 4 )
3568 OUString tmp = rPar.Get(3)->GetOUString().trim();
3569 if (!tmp.isEmpty())
3571 aCmdLine += " " + tmp;
3574 else if( aCmdLine.isEmpty() )
3576 // avoid special treatment (empty list)
3577 aCmdLine += " ";
3579 sal_Int32 nLen = aCmdLine.getLength();
3581 // #55735 if there are parameters, they have to be separated
3582 // #72471 also separate the single parameters
3583 std::vector<OUString> aTokenVector;
3584 OUString aToken;
3585 sal_Int32 i = 0;
3586 sal_Unicode c;
3587 while( i < nLen )
3589 for ( ;; ++i )
3591 c = aCmdLine[ i ];
3592 if ( c != ' ' && c != '\t' )
3594 break;
3598 if( c == '\"' || c == '\'' )
3600 sal_Int32 iFoundPos = aCmdLine.indexOf( c, i + 1 );
3602 if( iFoundPos < 0 )
3604 aToken = aCmdLine.copy( i);
3605 i = nLen;
3607 else
3609 aToken = aCmdLine.copy( i + 1, (iFoundPos - i - 1) );
3610 i = iFoundPos + 1;
3613 else
3615 sal_Int32 iFoundSpacePos = aCmdLine.indexOf( ' ', i );
3616 sal_Int32 iFoundTabPos = aCmdLine.indexOf( '\t', i );
3617 sal_Int32 iFoundPos = iFoundSpacePos >= 0 ? iFoundTabPos >= 0 ? std::min( iFoundSpacePos, iFoundTabPos ) : iFoundSpacePos : -1;
3619 if( iFoundPos < 0 )
3621 aToken = aCmdLine.copy( i );
3622 i = nLen;
3624 else
3626 aToken = aCmdLine.copy( i, (iFoundPos - i) );
3627 i = iFoundPos;
3631 // insert into the list
3632 aTokenVector.push_back( aToken );
3634 // #55735 / #72471 end
3636 sal_Int16 nWinStyle = 0;
3637 if( nArgCount >= 3 )
3639 nWinStyle = rPar.Get(2)->GetInteger();
3640 switch( nWinStyle )
3642 case 2:
3643 nOptions |= osl_Process_MINIMIZED;
3644 break;
3645 case 3:
3646 nOptions |= osl_Process_MAXIMIZED;
3647 break;
3648 case 10:
3649 nOptions |= osl_Process_FULLSCREEN;
3650 break;
3653 bool bSync = false;
3654 if( nArgCount >= 5 )
3656 bSync = rPar.Get(4)->GetBool();
3658 if( bSync )
3660 nOptions |= osl_Process_WAIT;
3664 // #72471 work parameter(s) up
3665 std::vector<OUString>::const_iterator iter = aTokenVector.begin();
3666 OUString aOUStrProgURL = getFullPath( *iter );
3668 ++iter;
3670 sal_uInt16 nParamCount = sal::static_int_cast< sal_uInt16 >(aTokenVector.size() - 1 );
3671 std::unique_ptr<rtl_uString*[]> pParamList;
3672 if( nParamCount )
3674 pParamList.reset( new rtl_uString*[nParamCount]);
3675 for(int iVector = 0; iter != aTokenVector.end(); ++iVector, ++iter)
3677 const OUString& rParamStr = *iter;
3678 pParamList[iVector] = nullptr;
3679 rtl_uString_assign(&(pParamList[iVector]), rParamStr.pData);
3683 oslProcess pApp;
3684 bool bSucc = osl_executeProcess(
3685 aOUStrProgURL.pData,
3686 pParamList.get(),
3687 nParamCount,
3688 nOptions,
3689 nullptr,
3690 nullptr,
3691 nullptr, 0,
3692 &pApp ) == osl_Process_E_None;
3694 // 53521 only free process handle on success
3695 if (bSucc)
3697 osl_freeProcessHandle( pApp );
3700 for(int j = 0; j < nParamCount; ++j)
3702 rtl_uString_release(pParamList[j]);
3705 if( !bSucc )
3707 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
3709 else
3711 rPar.Get(0)->PutLong(0);
3716 void SbRtl_VarType(StarBASIC *, SbxArray & rPar, bool)
3718 if (rPar.Count() != 2)
3720 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3722 else
3724 SbxDataType eType = rPar.Get(1)->GetType();
3725 rPar.Get(0)->PutInteger(static_cast<sal_Int16>(eType));
3729 // Exported function
3730 OUString getBasicTypeName( SbxDataType eType )
3732 static const char* pTypeNames[] =
3734 "Empty", // SbxEMPTY
3735 "Null", // SbxNULL
3736 "Integer", // SbxINTEGER
3737 "Long", // SbxLONG
3738 "Single", // SbxSINGLE
3739 "Double", // SbxDOUBLE
3740 "Currency", // SbxCURRENCY
3741 "Date", // SbxDATE
3742 "String", // SbxSTRING
3743 "Object", // SbxOBJECT
3744 "Error", // SbxERROR
3745 "Boolean", // SbxBOOL
3746 "Variant", // SbxVARIANT
3747 "DataObject", // SbxDATAOBJECT
3748 "Unknown Type",
3749 "Unknown Type",
3750 "Char", // SbxCHAR
3751 "Byte", // SbxBYTE
3752 "UShort", // SbxUSHORT
3753 "ULong", // SbxULONG
3754 "Long64", // SbxLONG64
3755 "ULong64", // SbxULONG64
3756 "Int", // SbxINT
3757 "UInt", // SbxUINT
3758 "Void", // SbxVOID
3759 "HResult", // SbxHRESULT
3760 "Pointer", // SbxPOINTER
3761 "DimArray", // SbxDIMARRAY
3762 "CArray", // SbxCARRAY
3763 "Userdef", // SbxUSERDEF
3764 "Lpstr", // SbxLPSTR
3765 "Lpwstr", // SbxLPWSTR
3766 "Unknown Type", // SbxCoreSTRING
3767 "WString", // SbxWSTRING
3768 "WChar", // SbxWCHAR
3769 "Int64", // SbxSALINT64
3770 "UInt64", // SbxSALUINT64
3771 "Decimal", // SbxDECIMAL
3774 size_t nPos = static_cast<size_t>(eType) & 0x0FFF;
3775 const size_t nTypeNameCount = SAL_N_ELEMENTS( pTypeNames );
3776 if ( nPos >= nTypeNameCount )
3778 nPos = nTypeNameCount - 1;
3780 return OUString::createFromAscii(pTypeNames[nPos]);
3783 static OUString getObjectTypeName( SbxVariable* pVar )
3785 OUString sRet( "Object" );
3786 if ( pVar )
3788 SbxBase* pBaseObj = pVar->GetObject();
3789 if( !pBaseObj )
3791 sRet = "Nothing";
3793 else
3795 SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pVar );
3796 if ( !pUnoObj )
3798 pUnoObj = dynamic_cast<SbUnoObject*>( pBaseObj );
3800 if ( pUnoObj )
3802 Any aObj = pUnoObj->getUnoAny();
3803 // For upstreaming unless we start to build oovbaapi by default
3804 // we need to get detect the vba-ness of the object in some
3805 // other way
3806 // note: Automation objects do not support XServiceInfo
3807 uno::Reference< XServiceInfo > xServInfo( aObj, uno::UNO_QUERY );
3808 if ( xServInfo.is() )
3810 // is this a VBA object ?
3811 Sequence< OUString > sServices = xServInfo->getSupportedServiceNames();
3812 if ( sServices.hasElements() )
3814 sRet = sServices[ 0 ];
3817 else
3819 uno::Reference< bridge::oleautomation::XAutomationObject > xAutoMation( aObj, uno::UNO_QUERY );
3820 if ( xAutoMation.is() )
3822 uno::Reference< script::XInvocation > xInv( aObj, uno::UNO_QUERY );
3823 if ( xInv.is() )
3827 xInv->getValue( "$GetTypeName" ) >>= sRet;
3829 catch(const Exception& )
3835 sal_Int32 nDot = sRet.lastIndexOf( '.' );
3836 if ( nDot != -1 && nDot < sRet.getLength() )
3838 sRet = sRet.copy( nDot + 1 );
3843 return sRet;
3846 void SbRtl_TypeName(StarBASIC *, SbxArray & rPar, bool)
3848 if (rPar.Count() != 2)
3850 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3852 else
3854 SbxDataType eType = rPar.Get(1)->GetType();
3855 bool bIsArray = ( ( eType & SbxARRAY ) != 0 );
3857 OUString aRetStr;
3858 if ( SbiRuntime::isVBAEnabled() && eType == SbxOBJECT )
3860 aRetStr = getObjectTypeName(rPar.Get(1));
3862 else
3864 aRetStr = getBasicTypeName( eType );
3866 if( bIsArray )
3868 aRetStr += "()";
3870 rPar.Get(0)->PutString(aRetStr);
3874 void SbRtl_Len(StarBASIC *, SbxArray & rPar, bool)
3876 if (rPar.Count() != 2)
3878 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3880 else
3882 const OUString& rStr = rPar.Get(1)->GetOUString();
3883 rPar.Get(0)->PutLong(rStr.getLength());
3887 void SbRtl_DDEInitiate(StarBASIC *, SbxArray & rPar, bool)
3889 int nArgs = static_cast<int>(rPar.Count());
3890 if ( nArgs != 3 )
3892 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3893 return;
3895 const OUString& rApp = rPar.Get(1)->GetOUString();
3896 const OUString& rTopic = rPar.Get(2)->GetOUString();
3898 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3899 size_t nChannel;
3900 ErrCode nDdeErr = pDDE->Initiate( rApp, rTopic, nChannel );
3901 if( nDdeErr )
3903 StarBASIC::Error( nDdeErr );
3905 else
3907 rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nChannel));
3911 void SbRtl_DDETerminate(StarBASIC *, SbxArray & rPar, bool)
3913 rPar.Get(0)->PutEmpty();
3914 int nArgs = static_cast<int>(rPar.Count());
3915 if ( nArgs != 2 )
3917 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3918 return;
3920 size_t nChannel = rPar.Get(1)->GetInteger();
3921 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3922 ErrCode nDdeErr = pDDE->Terminate( nChannel );
3923 if( nDdeErr )
3925 StarBASIC::Error( nDdeErr );
3929 void SbRtl_DDETerminateAll(StarBASIC *, SbxArray & rPar, bool)
3931 rPar.Get(0)->PutEmpty();
3932 int nArgs = static_cast<int>(rPar.Count());
3933 if ( nArgs != 1 )
3935 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3936 return;
3939 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3940 ErrCode nDdeErr = pDDE->TerminateAll();
3941 if( nDdeErr )
3943 StarBASIC::Error( nDdeErr );
3947 void SbRtl_DDERequest(StarBASIC *, SbxArray & rPar, bool)
3949 int nArgs = static_cast<int>(rPar.Count());
3950 if ( nArgs != 3 )
3952 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3953 return;
3955 size_t nChannel = rPar.Get(1)->GetInteger();
3956 const OUString& rItem = rPar.Get(2)->GetOUString();
3957 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3958 OUString aResult;
3959 ErrCode nDdeErr = pDDE->Request( nChannel, rItem, aResult );
3960 if( nDdeErr )
3962 StarBASIC::Error( nDdeErr );
3964 else
3966 rPar.Get(0)->PutString(aResult);
3970 void SbRtl_DDEExecute(StarBASIC *, SbxArray & rPar, bool)
3972 rPar.Get(0)->PutEmpty();
3973 int nArgs = static_cast<int>(rPar.Count());
3974 if ( nArgs != 3 )
3976 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3977 return;
3979 size_t nChannel = rPar.Get(1)->GetInteger();
3980 const OUString& rCommand = rPar.Get(2)->GetOUString();
3981 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3982 ErrCode nDdeErr = pDDE->Execute( nChannel, rCommand );
3983 if( nDdeErr )
3985 StarBASIC::Error( nDdeErr );
3989 void SbRtl_DDEPoke(StarBASIC *, SbxArray & rPar, bool)
3991 rPar.Get(0)->PutEmpty();
3992 int nArgs = static_cast<int>(rPar.Count());
3993 if ( nArgs != 4 )
3995 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3996 return;
3998 size_t nChannel = rPar.Get(1)->GetInteger();
3999 const OUString& rItem = rPar.Get(2)->GetOUString();
4000 const OUString& rData = rPar.Get(3)->GetOUString();
4001 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
4002 ErrCode nDdeErr = pDDE->Poke( nChannel, rItem, rData );
4003 if( nDdeErr )
4005 StarBASIC::Error( nDdeErr );
4010 void SbRtl_FreeFile(StarBASIC *, SbxArray & rPar, bool)
4012 if (rPar.Count() != 1)
4014 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4015 return;
4017 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
4018 short nChannel = 1;
4019 while( nChannel < CHANNELS )
4021 SbiStream* pStrm = pIO->GetStream( nChannel );
4022 if( !pStrm )
4024 rPar.Get(0)->PutInteger(nChannel);
4025 return;
4027 nChannel++;
4029 StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES );
4032 void SbRtl_LBound(StarBASIC *, SbxArray & rPar, bool)
4034 const sal_uInt32 nParCount = rPar.Count();
4035 if ( nParCount != 3 && nParCount != 2 )
4037 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4038 return;
4040 SbxBase* pParObj = rPar.Get(1)->GetObject();
4041 SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
4042 if( pArr )
4044 sal_Int32 nLower, nUpper;
4045 short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
4046 if (!pArr->GetDim(nDim, nLower, nUpper))
4047 StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
4048 else
4049 rPar.Get(0)->PutLong(nLower);
4051 else
4052 StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
4055 void SbRtl_UBound(StarBASIC *, SbxArray & rPar, bool)
4057 const sal_uInt32 nParCount = rPar.Count();
4058 if ( nParCount != 3 && nParCount != 2 )
4060 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4061 return;
4064 SbxBase* pParObj = rPar.Get(1)->GetObject();
4065 SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
4066 if( pArr )
4068 sal_Int32 nLower, nUpper;
4069 short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
4070 if (!pArr->GetDim(nDim, nLower, nUpper))
4071 StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
4072 else
4073 rPar.Get(0)->PutLong(nUpper);
4075 else
4076 StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
4079 void SbRtl_RGB(StarBASIC *, SbxArray & rPar, bool)
4081 if (rPar.Count() != 4)
4083 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4084 return;
4087 sal_Int32 nRed = rPar.Get(1)->GetInteger() & 0xFF;
4088 sal_Int32 nGreen = rPar.Get(2)->GetInteger() & 0xFF;
4089 sal_Int32 nBlue = rPar.Get(3)->GetInteger() & 0xFF;
4090 sal_Int32 nRGB;
4092 SbiInstance* pInst = GetSbData()->pInst;
4093 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
4094 // See discussion in tdf#145725, here's the quotation from a link indicated in the bugtracker
4095 // which explains why we need to manage RGB differently according to VB compatibility
4096 // "In other words, the individual color components are stored in the opposite order one would expect.
4097 // VB stores the red color component in the low-order byte of the long integer's low-order word,
4098 // 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"
4099 if( bCompatibility )
4101 nRGB = (nBlue << 16) | (nGreen << 8) | nRed;
4103 else
4105 nRGB = (nRed << 16) | (nGreen << 8) | nBlue;
4107 rPar.Get(0)->PutLong(nRGB);
4110 void SbRtl_QBColor(StarBASIC *, SbxArray & rPar, bool)
4112 static const sal_Int32 pRGB[] =
4114 0x000000,
4115 0x800000,
4116 0x008000,
4117 0x808000,
4118 0x000080,
4119 0x800080,
4120 0x008080,
4121 0xC0C0C0,
4122 0x808080,
4123 0xFF0000,
4124 0x00FF00,
4125 0xFFFF00,
4126 0x0000FF,
4127 0xFF00FF,
4128 0x00FFFF,
4129 0xFFFFFF,
4132 if (rPar.Count() != 2)
4134 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4135 return;
4138 sal_Int16 nCol = rPar.Get(1)->GetInteger();
4139 if( nCol < 0 || nCol > 15 )
4141 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4142 return;
4144 sal_Int32 nRGB = pRGB[ nCol ];
4145 rPar.Get(0)->PutLong(nRGB);
4148 // StrConv(string, conversion, LCID)
4149 void SbRtl_StrConv(StarBASIC *, SbxArray & rPar, bool)
4151 const sal_uInt32 nArgCount = rPar.Count() - 1;
4152 if( nArgCount < 2 || nArgCount > 3 )
4154 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4155 return;
4158 OUString aOldStr = rPar.Get(1)->GetOUString();
4159 sal_Int32 nConversion = rPar.Get(2)->GetLong();
4160 LanguageType nLanguage = LANGUAGE_SYSTEM;
4161 if (nArgCount == 3)
4163 sal_Int32 lcid = rPar.Get(3)->GetLong();
4164 nLanguage = LanguageType(lcid);
4166 OUString sLanguage = LanguageTag(nLanguage).getLanguage();
4167 rtl_TextEncoding encodingVal = utl_getWinTextEncodingFromLangStr(sLanguage);
4169 sal_Int32 nOldLen = aOldStr.getLength();
4170 if( nOldLen == 0 )
4172 // null string,return
4173 rPar.Get(0)->PutString(aOldStr);
4174 return;
4177 TransliterationFlags nType = TransliterationFlags::NONE;
4178 if ( (nConversion & 0x03) == 3 ) // vbProperCase
4180 const CharClass& rCharClass = GetCharClass();
4181 aOldStr = rCharClass.titlecase( aOldStr.toAsciiLowerCase(), 0, nOldLen );
4183 else if ( (nConversion & 0x01) == 1 ) // vbUpperCase
4185 nType |= TransliterationFlags::LOWERCASE_UPPERCASE;
4187 else if ( (nConversion & 0x02) == 2 ) // vbLowerCase
4189 nType |= TransliterationFlags::UPPERCASE_LOWERCASE;
4191 if ( (nConversion & 0x04) == 4 ) // vbWide
4193 nType |= TransliterationFlags::HALFWIDTH_FULLWIDTH;
4195 else if ( (nConversion & 0x08) == 8 ) // vbNarrow
4197 nType |= TransliterationFlags::FULLWIDTH_HALFWIDTH;
4199 if ( (nConversion & 0x10) == 16) // vbKatakana
4201 nType |= TransliterationFlags::HIRAGANA_KATAKANA;
4203 else if ( (nConversion & 0x20) == 32 ) // vbHiragana
4205 nType |= TransliterationFlags::KATAKANA_HIRAGANA;
4207 OUString aNewStr( aOldStr );
4208 if( nType != TransliterationFlags::NONE )
4210 uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
4211 ::utl::TransliterationWrapper aTransliterationWrapper( xContext, nType );
4212 uno::Sequence<sal_Int32> aOffsets;
4213 aTransliterationWrapper.loadModuleIfNeeded( nLanguage );
4214 aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets );
4217 if ( (nConversion & 0x40) == 64 ) // vbUnicode
4219 // convert the string to byte string, preserving unicode (2 bytes per character)
4220 sal_Int32 nSize = aNewStr.getLength()*2;
4221 const sal_Unicode* pSrc = aNewStr.getStr();
4222 std::unique_ptr<char[]> pChar(new char[nSize+1]);
4223 for( sal_Int32 i=0; i < nSize; i++ )
4225 pChar[i] = static_cast< char >( (i%2) ? ((*pSrc) >> 8) & 0xff : (*pSrc) & 0xff );
4226 if( i%2 )
4228 pSrc++;
4231 pChar[nSize] = '\0';
4232 OString aOStr(pChar.get());
4234 // there is no concept about default codepage in unix. so it is incorrectly in unix
4235 OUString aOUStr = OStringToOUString(aOStr, encodingVal);
4236 rPar.Get(0)->PutString(aOUStr);
4237 return;
4239 else if ( (nConversion & 0x80) == 128 ) // vbFromUnicode
4241 // there is no concept about default codepage in unix. so it is incorrectly in unix
4242 OString aOStr = OUStringToOString(aNewStr, encodingVal);
4243 const char* pChar = aOStr.getStr();
4244 sal_Int32 nArraySize = aOStr.getLength();
4245 SbxDimArray* pArray = new SbxDimArray(SbxBYTE);
4246 bool bIncIndex = IsBaseIndexOne();
4247 if(nArraySize)
4249 if( bIncIndex )
4251 pArray->AddDim(1, nArraySize);
4253 else
4255 pArray->AddDim(0, nArraySize - 1);
4258 else
4260 pArray->unoAddDim(0, -1);
4263 for( sal_Int32 i=0; i< nArraySize; i++)
4265 SbxVariable* pNew = new SbxVariable( SbxBYTE );
4266 pNew->PutByte(*pChar);
4267 pChar++;
4268 pNew->SetFlag( SbxFlagBits::Write );
4269 sal_Int32 aIdx[1];
4270 aIdx[0] = i;
4271 if( bIncIndex )
4273 ++aIdx[0];
4275 pArray->Put(pNew, aIdx);
4278 SbxVariableRef refVar = rPar.Get(0);
4279 SbxFlagBits nFlags = refVar->GetFlags();
4280 refVar->ResetFlag( SbxFlagBits::Fixed );
4281 refVar->PutObject( pArray );
4282 refVar->SetFlags( nFlags );
4283 refVar->SetParameters( nullptr );
4284 return;
4286 rPar.Get(0)->PutString(aNewStr);
4290 void SbRtl_Beep(StarBASIC *, SbxArray & rPar, bool)
4292 if (rPar.Count() != 1)
4294 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4295 return;
4297 Sound::Beep();
4300 void SbRtl_Load(StarBASIC *, SbxArray & rPar, bool)
4302 if (rPar.Count() != 2)
4304 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4305 return;
4309 SbxBase* pObj = rPar.Get(1)->GetObject();
4310 if ( !pObj )
4311 return;
4313 if (SbUserFormModule* pModule = dynamic_cast<SbUserFormModule*>(pObj))
4315 pModule->Load();
4317 else if (SbxObject* pSbxObj = dynamic_cast<SbxObject*>(pObj))
4319 SbxVariable* pVar = pSbxObj->Find("Load", SbxClassType::Method);
4320 if( pVar )
4322 pVar->GetInteger();
4327 void SbRtl_Unload(StarBASIC *, SbxArray & rPar, bool)
4329 rPar.Get(0)->PutEmpty();
4330 if (rPar.Count() != 2)
4332 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4333 return;
4337 SbxBase* pObj = rPar.Get(1)->GetObject();
4338 if ( !pObj )
4339 return;
4341 if (SbUserFormModule* pFormModule = dynamic_cast<SbUserFormModule*>(pObj))
4343 pFormModule->Unload();
4345 else if (SbxObject *pSbxObj = dynamic_cast<SbxObject*>(pObj))
4347 SbxVariable* pVar = pSbxObj->Find("Unload", SbxClassType::Method);
4348 if( pVar )
4350 pVar->GetInteger();
4355 void SbRtl_LoadPicture(StarBASIC *, SbxArray & rPar, bool)
4357 if (rPar.Count() != 2)
4359 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4360 return;
4363 OUString aFileURL = getFullPath(rPar.Get(1)->GetOUString());
4364 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aFileURL, StreamMode::READ ));
4365 if( pStream )
4367 Bitmap aBmp;
4368 ReadDIB(aBmp, *pStream, true);
4369 BitmapEx aBitmapEx(aBmp);
4370 Graphic aGraphic(aBitmapEx);
4372 SbxObjectRef xRef = new SbStdPicture;
4373 static_cast<SbStdPicture*>(xRef.get())->SetGraphic( aGraphic );
4374 rPar.Get(0)->PutObject(xRef.get());
4378 void SbRtl_SavePicture(StarBASIC *, SbxArray & rPar, bool)
4380 rPar.Get(0)->PutEmpty();
4381 if (rPar.Count() != 3)
4383 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4384 return;
4387 SbxBase* pObj = rPar.Get(1)->GetObject();
4388 if (SbStdPicture *pPicture = dynamic_cast<SbStdPicture*>(pObj))
4390 SvFileStream aOStream(rPar.Get(2)->GetOUString(), StreamMode::WRITE | StreamMode::TRUNC);
4391 const Graphic& aGraphic = pPicture->GetGraphic();
4392 TypeSerializer aSerializer(aOStream);
4393 aSerializer.writeGraphic(aGraphic);
4397 void SbRtl_MsgBox(StarBASIC *, SbxArray & rPar, bool)
4399 const sal_uInt32 nArgCount = rPar.Count();
4400 if( nArgCount < 2 || nArgCount > 6 )
4402 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4403 return;
4405 WinBits nType = 0; // MB_OK
4406 if( nArgCount >= 3 )
4407 nType = static_cast<WinBits>(rPar.Get(2)->GetInteger());
4408 WinBits nStyle = nType;
4409 nStyle &= 15; // delete bits 4-16
4410 if (nStyle > 5)
4411 nStyle = 0;
4413 enum BasicResponse
4415 Ok = 1,
4416 Cancel = 2,
4417 Abort = 3,
4418 Retry = 4,
4419 Ignore = 5,
4420 Yes = 6,
4421 No = 7
4424 OUString aMsg = rPar.Get(1)->GetOUString();
4425 OUString aTitle;
4426 if( nArgCount >= 4 )
4428 aTitle = rPar.Get(3)->GetOUString();
4430 else
4432 aTitle = Application::GetDisplayName();
4435 WinBits nDialogType = nType & (16+32+64);
4437 SolarMutexGuard aSolarGuard;
4438 weld::Widget* pParent = Application::GetDefDialogParent();
4440 VclMessageType eType = VclMessageType::Other;
4442 switch (nDialogType)
4444 case 16:
4445 eType = VclMessageType::Error;
4446 break;
4447 case 32:
4448 eType = VclMessageType::Question;
4449 break;
4450 case 48:
4451 eType = VclMessageType::Warning;
4452 break;
4453 case 64:
4454 eType = VclMessageType::Info;
4455 break;
4458 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
4459 eType, VclButtonsType::NONE, aMsg));
4461 switch (nStyle)
4463 case 0: // MB_OK
4464 default:
4465 xBox->add_button(GetStandardText(StandardButtonType::OK), BasicResponse::Ok);
4466 break;
4467 case 1: // MB_OKCANCEL
4468 xBox->add_button(GetStandardText(StandardButtonType::OK), BasicResponse::Ok);
4469 xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4471 if (nType & 256 || nType & 512)
4472 xBox->set_default_response(BasicResponse::Cancel);
4473 else
4474 xBox->set_default_response(BasicResponse::Ok);
4476 break;
4477 case 2: // MB_ABORTRETRYIGNORE
4478 xBox->add_button(GetStandardText(StandardButtonType::Abort), BasicResponse::Abort);
4479 xBox->add_button(GetStandardText(StandardButtonType::Retry), BasicResponse::Retry);
4480 xBox->add_button(GetStandardText(StandardButtonType::Ignore), BasicResponse::Ignore);
4482 if (nType & 256)
4483 xBox->set_default_response(BasicResponse::Retry);
4484 else if (nType & 512)
4485 xBox->set_default_response(BasicResponse::Ignore);
4486 else
4487 xBox->set_default_response(BasicResponse::Cancel);
4489 break;
4490 case 3: // MB_YESNOCANCEL
4491 xBox->add_button(GetStandardText(StandardButtonType::Yes), BasicResponse::Yes);
4492 xBox->add_button(GetStandardText(StandardButtonType::No), BasicResponse::No);
4493 xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4495 if (nType & 256 || nType & 512)
4496 xBox->set_default_response(BasicResponse::Cancel);
4497 else
4498 xBox->set_default_response(BasicResponse::Yes);
4500 break;
4501 case 4: // MB_YESNO
4502 xBox->add_button(GetStandardText(StandardButtonType::Yes), BasicResponse::Yes);
4503 xBox->add_button(GetStandardText(StandardButtonType::No), BasicResponse::No);
4505 if (nType & 256 || nType & 512)
4506 xBox->set_default_response(BasicResponse::No);
4507 else
4508 xBox->set_default_response(BasicResponse::Yes);
4510 break;
4511 case 5: // MB_RETRYCANCEL
4512 xBox->add_button(GetStandardText(StandardButtonType::Retry), BasicResponse::Retry);
4513 xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4515 if (nType & 256 || nType & 512)
4516 xBox->set_default_response(BasicResponse::Cancel);
4517 else
4518 xBox->set_default_response(BasicResponse::Retry);
4520 break;
4523 xBox->set_title(aTitle);
4524 sal_Int16 nRet = xBox->run();
4525 rPar.Get(0)->PutInteger(nRet);
4528 void SbRtl_SetAttr(StarBASIC *, SbxArray & rPar, bool)
4530 rPar.Get(0)->PutEmpty();
4531 if (rPar.Count() == 3)
4533 OUString aStr = rPar.Get(1)->GetOUString();
4534 SbAttributes nFlags = static_cast<SbAttributes>(rPar.Get(2)->GetInteger());
4536 if( hasUno() )
4538 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
4539 if( xSFI.is() )
4543 bool bReadOnly = bool(nFlags & SbAttributes::READONLY);
4544 xSFI->setReadOnly( aStr, bReadOnly );
4545 bool bHidden = bool(nFlags & SbAttributes::HIDDEN);
4546 xSFI->setHidden( aStr, bHidden );
4548 catch(const Exception & )
4550 StarBASIC::Error( ERRCODE_IO_GENERAL );
4555 else
4557 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4561 void SbRtl_Reset(StarBASIC *, SbxArray &, bool)
4563 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
4564 if (pIO)
4566 pIO->CloseAll();
4570 void SbRtl_DumpAllObjects(StarBASIC * pBasic, SbxArray & rPar, bool)
4572 const sal_uInt32 nArgCount = rPar.Count();
4573 if( nArgCount < 2 || nArgCount > 3 )
4575 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4577 else if( !pBasic )
4579 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
4581 else
4583 SbxObject* p = pBasic;
4584 while( p->GetParent() )
4586 p = p->GetParent();
4588 SvFileStream aStrm(rPar.Get(1)->GetOUString(),
4589 StreamMode::WRITE | StreamMode::TRUNC );
4590 p->Dump(aStrm, rPar.Get(2)->GetBool());
4591 aStrm.Close();
4592 if( aStrm.GetError() != ERRCODE_NONE )
4594 StarBASIC::Error( ERRCODE_BASIC_IO_ERROR );
4600 void SbRtl_FileExists(StarBASIC *, SbxArray & rPar, bool)
4602 if (rPar.Count() == 2)
4604 OUString aStr = rPar.Get(1)->GetOUString();
4605 bool bExists = false;
4607 if( hasUno() )
4609 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
4610 if( xSFI.is() )
4614 bExists = xSFI->exists( aStr );
4616 catch(const Exception & )
4618 StarBASIC::Error( ERRCODE_IO_GENERAL );
4622 else
4624 DirectoryItem aItem;
4625 FileBase::RC nRet = DirectoryItem::get( getFullPath( aStr ), aItem );
4626 bExists = (nRet == FileBase::E_None);
4628 rPar.Get(0)->PutBool(bExists);
4630 else
4632 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4636 void SbRtl_Partition(StarBASIC *, SbxArray & rPar, bool)
4638 if (rPar.Count() != 5)
4640 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4641 return;
4644 sal_Int32 nNumber = rPar.Get(1)->GetLong();
4645 sal_Int32 nStart = rPar.Get(2)->GetLong();
4646 sal_Int32 nStop = rPar.Get(3)->GetLong();
4647 sal_Int32 nInterval = rPar.Get(4)->GetLong();
4649 if( nStart < 0 || nStop <= nStart || nInterval < 1 )
4651 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4652 return;
4655 // the Partition function inserts leading spaces before lowervalue and uppervalue
4656 // so that they both have the same number of characters as the string
4657 // representation of the value (Stop + 1). This ensures that if you use the output
4658 // of the Partition function with several values of Number, the resulting text
4659 // will be handled properly during any subsequent sort operation.
4661 // calculate the maximum number of characters before lowervalue and uppervalue
4662 OUString aBeforeStart = OUString::number( nStart - 1 );
4663 OUString aAfterStop = OUString::number( nStop + 1 );
4664 sal_Int32 nLen1 = aBeforeStart.getLength();
4665 sal_Int32 nLen2 = aAfterStop.getLength();
4666 sal_Int32 nLen = nLen1 >= nLen2 ? nLen1:nLen2;
4668 OUStringBuffer aRetStr( nLen * 2 + 1);
4669 OUString aLowerValue;
4670 OUString aUpperValue;
4671 if( nNumber < nStart )
4673 aUpperValue = aBeforeStart;
4675 else if( nNumber > nStop )
4677 aLowerValue = aAfterStop;
4679 else
4681 sal_Int32 nLowerValue = nNumber;
4682 sal_Int32 nUpperValue = nLowerValue;
4683 if( nInterval > 1 )
4685 nLowerValue = ((( nNumber - nStart ) / nInterval ) * nInterval ) + nStart;
4686 nUpperValue = nLowerValue + nInterval - 1;
4688 aLowerValue = OUString::number( nLowerValue );
4689 aUpperValue = OUString::number( nUpperValue );
4692 nLen1 = aLowerValue.getLength();
4693 nLen2 = aUpperValue.getLength();
4695 if( nLen > nLen1 )
4697 // appending the leading spaces for the lowervalue
4698 for ( sal_Int32 i= nLen - nLen1; i > 0; --i )
4700 aRetStr.append(" ");
4703 aRetStr.append( aLowerValue + ":");
4704 if( nLen > nLen2 )
4706 // appending the leading spaces for the uppervalue
4707 for ( sal_Int32 i= nLen - nLen2; i > 0; --i )
4709 aRetStr.append(" ");
4712 aRetStr.append( aUpperValue );
4713 rPar.Get(0)->PutString(aRetStr.makeStringAndClear());
4716 #endif
4718 static tools::Long GetDayDiff( const Date& rDate )
4720 Date aRefDate( 1,1,1900 );
4721 tools::Long nDiffDays;
4722 if ( aRefDate > rDate )
4724 nDiffDays = aRefDate - rDate;
4725 nDiffDays *= -1;
4727 else
4729 nDiffDays = rDate - aRefDate;
4731 nDiffDays += 2; // adjustment VisualBasic: 1.Jan.1900 == 2
4732 return nDiffDays;
4735 sal_Int16 implGetDateYear( double aDate )
4737 Date aRefDate( 1,1,1900 );
4738 tools::Long nDays = static_cast<tools::Long>(aDate);
4739 nDays -= 2; // standardize: 1.1.1900 => 0.0
4740 aRefDate.AddDays( nDays );
4741 sal_Int16 nRet = aRefDate.GetYear();
4742 return nRet;
4745 bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
4746 bool bUseTwoDigitYear, SbDateCorrection eCorr, double& rdRet )
4748 // XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and
4749 // 30..99 can not be input as they are 2-digit for 2000..2029 and
4750 // 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always
4751 // true). For VBA years > 9999 are invalid.
4752 // For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99
4753 // can not be input as they are 2-digit for 1900..1999, years<0 are
4754 // accepted. If bUseTwoDigitYear==false then all years are accepted, but
4755 // year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01).
4756 #if HAVE_FEATURE_SCRIPTING
4757 if ( (nYear < 0 || 9999 < nYear) && SbiRuntime::isVBAEnabled() )
4759 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4760 return false;
4762 else if ( nYear < 30 && SbiRuntime::isVBAEnabled() )
4764 nYear += 2000;
4766 else
4767 #endif
4769 if ( 0 <= nYear && nYear < 100 &&
4770 #if HAVE_FEATURE_SCRIPTING
4771 (bUseTwoDigitYear || SbiRuntime::isVBAEnabled())
4772 #else
4773 bUseTwoDigitYear
4774 #endif
4777 nYear += 1900;
4781 sal_Int32 nAddMonths = 0;
4782 sal_Int32 nAddDays = 0;
4783 // Always sanitize values to set date and to use for validity detection.
4784 if (nMonth < 1 || 12 < nMonth)
4786 sal_Int16 nM = ((nMonth < 1) ? (12 + (nMonth % 12)) : (nMonth % 12));
4787 nAddMonths = nMonth - nM;
4788 nMonth = nM;
4790 // Day 0 would already be normalized during Date::Normalize(), include
4791 // it in negative days, also to detect non-validity. The actual day of
4792 // month is 1+(nDay-1)
4793 if (nDay < 1)
4795 nAddDays = nDay - 1;
4796 nDay = 1;
4798 else if (nDay > 31)
4800 nAddDays = nDay - 31;
4801 nDay = 31;
4804 Date aCurDate( nDay, nMonth, nYear );
4806 /* TODO: we could enable the same rollover mechanism for StarBASIC to be
4807 * compatible with VBA (just with our wider supported date range), then
4808 * documentation would need to be adapted. As is, the DateSerial() runtime
4809 * function works as dumb as documented... (except that the resulting date
4810 * is checked for validity now and not just day<=31 and month<=12).
4811 * If change wanted then simply remove overriding RollOver here and adapt
4812 * documentation.*/
4813 #if HAVE_FEATURE_SCRIPTING
4814 if (eCorr == SbDateCorrection::RollOver && !SbiRuntime::isVBAEnabled())
4815 eCorr = SbDateCorrection::None;
4816 #endif
4818 if (nYear == 0 || (eCorr == SbDateCorrection::None && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
4820 #if HAVE_FEATURE_SCRIPTING
4821 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4822 #endif
4823 return false;
4826 if (eCorr != SbDateCorrection::None)
4828 aCurDate.Normalize();
4829 if (nAddMonths)
4830 aCurDate.AddMonths( nAddMonths);
4831 if (nAddDays)
4832 aCurDate.AddDays( nAddDays);
4833 if (eCorr == SbDateCorrection::TruncateToMonth && aCurDate.GetMonth() != nMonth)
4835 if (aCurDate.GetYear() == SAL_MAX_INT16 && nMonth == 12)
4837 // Roll over and back not possible, hard max.
4838 aCurDate.SetMonth(12);
4839 aCurDate.SetDay(31);
4841 else
4843 aCurDate.SetMonth(nMonth);
4844 aCurDate.SetDay(1);
4845 aCurDate.AddMonths(1);
4846 aCurDate.AddDays(-1);
4851 tools::Long nDiffDays = GetDayDiff( aCurDate );
4852 rdRet = static_cast<double>(nDiffDays);
4853 return true;
4856 double implTimeSerial( sal_Int16 nHours, sal_Int16 nMinutes, sal_Int16 nSeconds )
4858 return
4859 static_cast<double>( nHours * ::tools::Time::secondPerHour +
4860 nMinutes * ::tools::Time::secondPerMinute +
4861 nSeconds)
4863 static_cast<double>( ::tools::Time::secondPerDay );
4866 bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
4867 sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond,
4868 double& rdRet )
4870 double dDate;
4871 if(!implDateSerial(nYear, nMonth, nDay, false/*bUseTwoDigitYear*/, SbDateCorrection::None, dDate))
4872 return false;
4873 rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond);
4874 return true;
4877 sal_Int16 implGetMinute( double dDate )
4879 double nFrac = dDate - floor( dDate );
4880 nFrac *= 86400.0;
4881 sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
4882 sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds % 3600);
4883 sal_Int16 nMin = nTemp / 60;
4884 return nMin;
4887 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */