Bump version to 6.4.7.2.M8
[LibreOffice.git] / basic / source / runtime / methods.cxx
blob30ad9dc101889b83ba9a05a7359ab5e18b2ef7c3
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 <tools/wldcrd.hxx>
43 #include <i18nlangtag/lang.h>
44 #include <rtl/string.hxx>
45 #include <sal/log.hxx>
47 #include <runtime.hxx>
48 #include <sbunoobj.hxx>
49 #include <osl/file.hxx>
50 #include <errobject.hxx>
52 #include <comphelper/string.hxx>
53 #include <comphelper/processfactory.hxx>
55 #include <com/sun/star/uno/Sequence.hxx>
56 #include <com/sun/star/util/DateTime.hpp>
57 #include <com/sun/star/lang/Locale.hpp>
58 #include <com/sun/star/lang/XServiceInfo.hpp>
59 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
60 #include <com/sun/star/script/XErrorQuery.hpp>
61 #include <ooo/vba/VbTriState.hpp>
62 #include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp>
63 #include <memory>
64 #include <random>
65 #include <o3tl/char16_t2wchar_t.hxx>
67 using namespace comphelper;
68 using namespace osl;
69 using namespace com::sun::star;
70 using namespace com::sun::star::lang;
71 using namespace com::sun::star::uno;
73 #include <date.hxx>
74 #include <sbstdobj.hxx>
75 #include <rtlproto.hxx>
76 #include <image.hxx>
77 #include <iosys.hxx>
78 #include "ddectrl.hxx"
79 #include <sbintern.hxx>
80 #include <basic/vbahelper.hxx>
82 #include <vector>
83 #include <math.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <errno.h>
88 #include <sbobjmod.hxx>
89 #include <sbxmod.hxx>
91 #ifdef _WIN32
92 #include <prewin.h>
93 #include <direct.h>
94 #include <io.h>
95 #include <postwin.h>
96 #else
97 #include <unistd.h>
98 #endif
100 #if HAVE_FEATURE_SCRIPTING
102 static void FilterWhiteSpace( OUString& rStr )
104 if (rStr.isEmpty())
106 return;
108 OUStringBuffer aRet;
110 for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
112 sal_Unicode cChar = rStr[i];
113 if ((cChar != ' ') && (cChar != '\t') &&
114 (cChar != '\n') && (cChar != '\r'))
116 aRet.append(cChar);
120 rStr = aRet.makeStringAndClear();
123 static long GetDayDiff( const Date& rDate );
125 static const CharClass& GetCharClass()
127 static CharClass aCharClass( Application::GetSettings().GetLanguageTag() );
128 return aCharClass;
131 static bool isFolder( FileStatus::Type aType )
133 return ( aType == FileStatus::Directory || aType == FileStatus::Volume );
137 //*** UCB file access ***
139 // Converts possibly relative paths to absolute paths
140 // according to the setting done by ChDir/ChDrive
141 OUString getFullPath( const OUString& aRelPath )
143 OUString aFileURL;
145 // #80204 Try first if it already is a valid URL
146 INetURLObject aURLObj( aRelPath );
147 aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
149 if( aFileURL.isEmpty() )
151 File::getFileURLFromSystemPath( aRelPath, aFileURL );
154 return aFileURL;
157 // TODO: -> SbiGlobals
158 static uno::Reference< ucb::XSimpleFileAccess3 > const & getFileAccess()
160 static uno::Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
161 return xSFI;
165 // Properties and methods lie down the return value at the Get (bPut = sal_False) in the
166 // element 0 of the Argv; the value of element 0 is saved at Put (bPut = sal_True)
168 // CreateObject( class )
170 void SbRtl_CreateObject(StarBASIC * pBasic, SbxArray & rPar, bool)
172 OUString aClass( rPar.Get( 1 )->GetOUString() );
173 SbxObjectRef p = SbxBase::CreateObject( aClass );
174 if( !p.is() )
175 StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD );
176 else
178 // Convenience: enter BASIC as parent
179 p->SetParent( pBasic );
180 rPar.Get( 0 )->PutObject( p.get() );
184 // Error( n )
186 void SbRtl_Error(StarBASIC * pBasic, SbxArray & rPar, bool)
188 if( !pBasic )
189 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
190 else
192 OUString aErrorMsg;
193 ErrCode nErr = ERRCODE_NONE;
194 sal_Int32 nCode = 0;
195 if( rPar.Count() == 1 )
197 nErr = StarBASIC::GetErrBasic();
198 aErrorMsg = StarBASIC::GetErrorMsg();
200 else
202 nCode = rPar.Get( 1 )->GetLong();
203 if( nCode > 65535 )
205 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
207 else
209 nErr = StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16>(nCode) );
213 bool bVBA = SbiRuntime::isVBAEnabled();
214 OUString tmpErrMsg;
215 if( bVBA && !aErrorMsg.isEmpty())
217 tmpErrMsg = aErrorMsg;
219 else
221 StarBASIC::MakeErrorText( nErr, aErrorMsg );
222 tmpErrMsg = StarBASIC::GetErrorText();
224 // If this rtlfunc 'Error' passed an errcode the same as the active Err Objects's
225 // current err then return the description for the error message if it is set
226 // ( complicated isn't it ? )
227 if ( bVBA && rPar.Count() > 1 )
229 uno::Reference< ooo::vba::XErrObject > xErrObj( SbxErrObject::getUnoErrObject() );
230 if ( xErrObj.is() && xErrObj->getNumber() == nCode && !xErrObj->getDescription().isEmpty() )
232 tmpErrMsg = xErrObj->getDescription();
235 rPar.Get( 0 )->PutString( tmpErrMsg );
239 // Sinus
241 void SbRtl_Sin(StarBASIC *, SbxArray & rPar, bool)
243 if ( rPar.Count() < 2 )
244 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
245 else
247 SbxVariableRef pArg = rPar.Get( 1 );
248 rPar.Get( 0 )->PutDouble( sin( pArg->GetDouble() ) );
253 void SbRtl_Cos(StarBASIC *, SbxArray & rPar, bool)
255 if ( rPar.Count() < 2 )
256 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
257 else
259 SbxVariableRef pArg = rPar.Get( 1 );
260 rPar.Get( 0 )->PutDouble( cos( pArg->GetDouble() ) );
265 void SbRtl_Atn(StarBASIC *, SbxArray & rPar, bool)
267 if ( rPar.Count() < 2 )
268 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
269 else
271 SbxVariableRef pArg = rPar.Get( 1 );
272 rPar.Get( 0 )->PutDouble( atan( pArg->GetDouble() ) );
277 void SbRtl_Abs(StarBASIC *, SbxArray & rPar, bool)
279 if ( rPar.Count() < 2 )
281 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
283 else
285 SbxVariableRef pArg = rPar.Get( 1 );
286 rPar.Get( 0 )->PutDouble( fabs( pArg->GetDouble() ) );
291 void SbRtl_Asc(StarBASIC *, SbxArray & rPar, bool)
293 if ( rPar.Count() < 2 )
295 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
297 else
299 SbxVariableRef pArg = rPar.Get( 1 );
300 OUString aStr( pArg->GetOUString() );
301 if ( aStr.isEmpty())
303 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
304 rPar.Get(0)->PutEmpty();
306 else
308 sal_Unicode aCh = aStr[0];
309 rPar.Get(0)->PutLong( aCh );
314 static void implChr( SbxArray& rPar, bool bChrW )
316 if ( rPar.Count() < 2 )
318 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
320 else
322 SbxVariableRef pArg = rPar.Get( 1 );
324 OUString aStr;
325 if( !bChrW && SbiRuntime::isVBAEnabled() )
327 sal_Char c = static_cast<sal_Char>(pArg->GetByte());
328 aStr = OUString(&c, 1, osl_getThreadTextEncoding());
330 else
332 // Map negative 16-bit values to large positive ones, so that code like Chr(&H8000)
333 // still works after the fix for tdf#62326 changed those four-digit hex notations to
334 // produce negative values:
335 sal_Int32 aCh = pArg->GetLong();
336 if (aCh < -0x8000 || aCh > 0xFFFF) {
337 StarBASIC::Error(ERRCODE_BASIC_MATH_OVERFLOW);
338 aCh = 0;
340 aStr = OUString(static_cast<sal_Unicode>(aCh));
342 rPar.Get(0)->PutString( aStr );
346 void SbRtl_Chr(StarBASIC *, SbxArray & rPar, bool)
348 implChr( rPar, false/*bChrW*/ );
351 void SbRtl_ChrW(StarBASIC *, SbxArray & rPar, bool)
353 implChr( rPar, true/*bChrW*/ );
356 #if defined _WIN32
358 namespace {
360 extern "C" void invalidParameterHandler(
361 wchar_t const * expression, wchar_t const * function, wchar_t const * file, unsigned int line,
362 uintptr_t)
364 SAL_INFO(
365 "basic",
366 "invalid parameter during _wgetdcwd; \"" << (expression ? o3tl::toU(expression) : u"???")
367 << "\" (" << (function ? o3tl::toU(function) : u"???") << ") at "
368 << (file ? o3tl::toU(file) : u"???") << ":" << line);
373 #endif
375 void SbRtl_CurDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
377 (void)pBasic;
378 (void)bWrite;
380 // #57064 Although this function doesn't work with DirEntry, it isn't touched
381 // by the adjustment to virtual URLs, as, using the DirEntry-functionality,
382 // there's no possibility to detect the current one in a way that a virtual URL
383 // could be delivered.
385 #if defined(_WIN32)
386 int nCurDir = 0; // Current dir // JSM
387 if ( rPar.Count() == 2 )
389 OUString aDrive = rPar.Get(1)->GetOUString();
390 if ( aDrive.getLength() != 1 )
392 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
393 return;
395 auto c = rtl::toAsciiUpperCase(aDrive[0]);
396 if ( !rtl::isAsciiUpperCase( c ) )
398 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
399 return;
401 nCurDir = c - 'A' + 1;
403 wchar_t pBuffer[ _MAX_PATH ];
404 // _wgetdcwd calls the C runtime's invalid parameter handler (which by default terminates the
405 // process) if nCurDir does not correspond to an existing drive, so temporarily set a "harmless"
406 // handler:
407 auto const handler = _set_thread_local_invalid_parameter_handler(&invalidParameterHandler);
408 auto const ok = _wgetdcwd( nCurDir, pBuffer, _MAX_PATH ) != nullptr;
409 _set_thread_local_invalid_parameter_handler(handler);
410 if ( ok )
412 rPar.Get(0)->PutString( o3tl::toU(pBuffer) );
414 else
416 StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE );
419 #else
421 const int PATH_INCR = 250;
423 int nSize = PATH_INCR;
424 std::unique_ptr<char[]> pMem;
425 while( true )
427 pMem.reset(new char[nSize]);
428 if( !pMem )
430 StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY );
431 return;
433 if( getcwd( pMem.get(), nSize-1 ) != nullptr )
435 rPar.Get(0)->PutString( OUString::createFromAscii(pMem.get()) );
436 return;
438 if( errno != ERANGE )
440 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
441 return;
443 nSize += PATH_INCR;
446 #endif
449 void SbRtl_ChDir(StarBASIC * pBasic, SbxArray & rPar, bool)
451 rPar.Get(0)->PutEmpty();
452 if (rPar.Count() == 2)
454 // VBA: track current directory per document type (separately for Writer, Calc, Impress, etc.)
455 if( SbiRuntime::isVBAEnabled() )
457 ::basic::vba::registerCurrentDirectory( getDocumentModel( pBasic ), rPar.Get(1)->GetOUString() );
460 else
462 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
466 void SbRtl_ChDrive(StarBASIC *, SbxArray & rPar, bool)
468 rPar.Get(0)->PutEmpty();
469 if (rPar.Count() != 2)
471 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
476 // Implementation of StepRENAME with UCB
477 void implStepRenameUCB( const OUString& aSource, const OUString& aDest )
479 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
480 if( xSFI.is() )
484 OUString aSourceFullPath = getFullPath( aSource );
485 if( !xSFI->exists( aSourceFullPath ) )
487 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
488 return;
491 OUString aDestFullPath = getFullPath( aDest );
492 if( xSFI->exists( aDestFullPath ) )
494 StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS );
496 else
498 xSFI->move( aSourceFullPath, aDestFullPath );
501 catch(const Exception & )
503 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
508 // Implementation of StepRENAME with OSL
509 void implStepRenameOSL( const OUString& aSource, const OUString& aDest )
511 FileBase::RC nRet = File::move( getFullPath( aSource ), getFullPath( aDest ) );
512 if( nRet != FileBase::E_None )
514 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
518 void SbRtl_FileCopy(StarBASIC *, SbxArray & rPar, bool)
520 rPar.Get(0)->PutEmpty();
521 if (rPar.Count() == 3)
523 OUString aSource = rPar.Get(1)->GetOUString();
524 OUString aDest = rPar.Get(2)->GetOUString();
525 if( hasUno() )
527 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
528 if( xSFI.is() )
532 xSFI->copy( getFullPath( aSource ), getFullPath( aDest ) );
534 catch(const Exception & )
536 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
540 else
542 FileBase::RC nRet = File::copy( getFullPath( aSource ), getFullPath( aDest ) );
543 if( nRet != FileBase::E_None )
545 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
549 else
550 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
553 void SbRtl_Kill(StarBASIC *, SbxArray & rPar, bool)
555 rPar.Get(0)->PutEmpty();
556 if (rPar.Count() == 2)
558 OUString aFileSpec = rPar.Get(1)->GetOUString();
560 if( hasUno() )
562 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
563 if( xSFI.is() )
565 OUString aFullPath = getFullPath( aFileSpec );
566 if( !xSFI->exists( aFullPath ) || xSFI->isFolder( aFullPath ) )
568 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
569 return;
573 xSFI->kill( aFullPath );
575 catch(const Exception & )
577 StarBASIC::Error( ERRCODE_IO_GENERAL );
581 else
583 File::remove( getFullPath( aFileSpec ) );
586 else
588 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
592 void SbRtl_MkDir(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
594 rPar.Get(0)->PutEmpty();
595 if (rPar.Count() == 2)
597 OUString aPath = rPar.Get(1)->GetOUString();
598 if ( SbiRuntime::isVBAEnabled() )
600 // In vba if the full path is not specified then
601 // folder is created relative to the curdir
602 INetURLObject aURLObj( getFullPath( aPath ) );
603 if ( aURLObj.GetProtocol() != INetProtocol::File )
605 SbxArrayRef pPar = new SbxArray();
606 SbxVariableRef pResult = new SbxVariable();
607 SbxVariableRef pParam = new SbxVariable();
608 pPar->Insert( pResult.get(), pPar->Count() );
609 pPar->Insert( pParam.get(), pPar->Count() );
610 SbRtl_CurDir( pBasic, *pPar, bWrite );
612 OUString sCurPathURL;
613 File::getFileURLFromSystemPath( pPar->Get(0)->GetOUString(), sCurPathURL );
615 aURLObj.SetURL( sCurPathURL );
616 aURLObj.Append( aPath );
617 File::getSystemPathFromFileURL(aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ),aPath ) ;
621 if( hasUno() )
623 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
624 if( xSFI.is() )
628 xSFI->createFolder( getFullPath( aPath ) );
630 catch(const Exception & )
632 StarBASIC::Error( ERRCODE_IO_GENERAL );
636 else
638 Directory::create( getFullPath( aPath ) );
641 else
643 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
648 // In OSL only empty directories can be deleted
649 // so we have to delete all files recursively
650 static void implRemoveDirRecursive( const OUString& aDirPath )
652 DirectoryItem aItem;
653 FileBase::RC nRet = DirectoryItem::get( aDirPath, aItem );
654 bool bExists = (nRet == FileBase::E_None);
656 FileStatus aFileStatus( osl_FileStatus_Mask_Type );
657 nRet = aItem.getFileStatus( aFileStatus );
658 bool bFolder = nRet == FileBase::E_None
659 && isFolder( aFileStatus.getFileType() );
661 if( !bExists || !bFolder )
663 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
664 return;
667 Directory aDir( aDirPath );
668 nRet = aDir.open();
669 if( nRet != FileBase::E_None )
671 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
672 return;
675 for( ;; )
677 DirectoryItem aItem2;
678 nRet = aDir.getNextItem( aItem2 );
679 if( nRet != FileBase::E_None )
681 break;
683 // Handle flags
684 FileStatus aFileStatus2( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL );
685 nRet = aItem2.getFileStatus( aFileStatus2 );
686 if( nRet != FileBase::E_None )
688 SAL_WARN("basic", "getFileStatus failed");
689 continue;
691 OUString aPath = aFileStatus2.getFileURL();
693 // Directory?
694 FileStatus::Type aType2 = aFileStatus2.getFileType();
695 bool bFolder2 = isFolder( aType2 );
696 if( bFolder2 )
698 implRemoveDirRecursive( aPath );
700 else
702 File::remove( aPath );
705 aDir.close();
707 Directory::remove( aDirPath );
711 void SbRtl_RmDir(StarBASIC *, SbxArray & rPar, bool)
713 rPar.Get(0)->PutEmpty();
714 if (rPar.Count() == 2)
716 OUString aPath = rPar.Get(1)->GetOUString();
717 if( hasUno() )
719 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
720 if( xSFI.is() )
724 if( !xSFI->isFolder( aPath ) )
726 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND );
727 return;
729 SbiInstance* pInst = GetSbData()->pInst;
730 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
731 if( bCompatibility )
733 Sequence< OUString > aContent = xSFI->getFolderContents( aPath, true );
734 if( aContent.hasElements() )
736 StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR );
737 return;
741 xSFI->kill( getFullPath( aPath ) );
743 catch(const Exception & )
745 StarBASIC::Error( ERRCODE_IO_GENERAL );
749 else
751 implRemoveDirRecursive( getFullPath( aPath ) );
754 else
756 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
760 void SbRtl_SendKeys(StarBASIC *, SbxArray & rPar, bool)
762 rPar.Get(0)->PutEmpty();
763 StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
766 void SbRtl_Exp(StarBASIC *, SbxArray & rPar, bool)
768 if( rPar.Count() < 2 )
769 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
770 else
772 double aDouble = rPar.Get( 1 )->GetDouble();
773 aDouble = exp( aDouble );
774 checkArithmeticOverflow( aDouble );
775 rPar.Get( 0 )->PutDouble( aDouble );
779 void SbRtl_FileLen(StarBASIC *, SbxArray & rPar, bool)
781 if ( rPar.Count() < 2 )
783 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
785 else
787 SbxVariableRef pArg = rPar.Get( 1 );
788 OUString aStr( pArg->GetOUString() );
789 sal_Int32 nLen = 0;
790 if( hasUno() )
792 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
793 if( xSFI.is() )
797 nLen = xSFI->getSize( getFullPath( aStr ) );
799 catch(const Exception & )
801 StarBASIC::Error( ERRCODE_IO_GENERAL );
805 else
807 DirectoryItem aItem;
808 (void)DirectoryItem::get( getFullPath( aStr ), aItem );
809 FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
810 (void)aItem.getFileStatus( aFileStatus );
811 nLen = static_cast<sal_Int32>(aFileStatus.getFileSize());
813 rPar.Get(0)->PutLong( static_cast<long>(nLen) );
818 void SbRtl_Hex(StarBASIC *, SbxArray & rPar, bool)
820 if ( rPar.Count() < 2 )
822 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
824 else
826 SbxVariableRef pArg = rPar.Get( 1 );
827 // converting value to unsigned and limit to 2 or 4 byte representation
828 sal_uInt32 nVal = pArg->IsInteger() ?
829 static_cast<sal_uInt16>(pArg->GetInteger()) :
830 static_cast<sal_uInt32>(pArg->GetLong());
831 OUString aStr(OUString::number( nVal, 16 ));
832 aStr = aStr.toAsciiUpperCase();
833 rPar.Get(0)->PutString( aStr );
837 void SbRtl_FuncCaller(StarBASIC *, SbxArray & rPar, bool)
839 if ( SbiRuntime::isVBAEnabled() && GetSbData()->pInst && GetSbData()->pInst->pRun )
841 if ( GetSbData()->pInst->pRun->GetExternalCaller() )
842 *rPar.Get(0) = *GetSbData()->pInst->pRun->GetExternalCaller();
843 else
845 SbxVariableRef pVar = new SbxVariable(SbxVARIANT);
846 *rPar.Get(0) = *pVar;
849 else
851 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
855 // InStr( [start],string,string,[compare] )
857 void SbRtl_InStr(StarBASIC *, SbxArray & rPar, bool)
859 std::size_t nArgCount = rPar.Count()-1;
860 if ( nArgCount < 2 )
861 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
862 else
864 sal_Int32 nStartPos = 1;
865 sal_Int32 nFirstStringPos = 1;
867 if ( nArgCount >= 3 )
869 nStartPos = rPar.Get(1)->GetLong();
870 if( nStartPos <= 0 )
872 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
873 nStartPos = 1;
875 nFirstStringPos++;
878 SbiInstance* pInst = GetSbData()->pInst;
879 bool bTextMode;
880 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
881 if( bCompatibility )
883 SbiRuntime* pRT = pInst->pRun;
884 bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
886 else
888 bTextMode = true;
890 if ( nArgCount == 4 )
892 bTextMode = rPar.Get(4)->GetInteger();
894 sal_Int32 nPos;
895 const OUString& rToken = rPar.Get(nFirstStringPos+1)->GetOUString();
897 // #97545 Always find empty string
898 if( rToken.isEmpty() )
900 nPos = nStartPos;
902 else
904 if( !bTextMode )
906 const OUString& rStr1 = rPar.Get(nFirstStringPos)->GetOUString();
907 nPos = rStr1.indexOf( rToken, nStartPos - 1 ) + 1;
909 else
911 OUString aStr1 = rPar.Get(nFirstStringPos)->GetOUString();
912 OUString aToken = rToken;
914 aStr1 = aStr1.toAsciiUpperCase();
915 aToken = aToken.toAsciiUpperCase();
917 nPos = aStr1.indexOf( aToken, nStartPos-1 ) + 1;
920 rPar.Get(0)->PutLong( nPos );
925 // InstrRev(string1, string2[, start[, compare]])
927 void SbRtl_InStrRev(StarBASIC *, SbxArray & rPar, bool)
929 std::size_t nArgCount = rPar.Count()-1;
930 if ( nArgCount < 2 )
932 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
934 else
936 OUString aStr1 = rPar.Get(1)->GetOUString();
937 OUString aToken = rPar.Get(2)->GetOUString();
939 sal_Int32 nStartPos = -1;
940 if ( nArgCount >= 3 )
942 nStartPos = rPar.Get(3)->GetLong();
943 if( nStartPos <= 0 && nStartPos != -1 )
945 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
946 nStartPos = -1;
950 SbiInstance* pInst = GetSbData()->pInst;
951 bool bTextMode;
952 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
953 if( bCompatibility )
955 SbiRuntime* pRT = pInst->pRun;
956 bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
958 else
960 bTextMode = true;
962 if ( nArgCount == 4 )
964 bTextMode = rPar.Get(4)->GetInteger();
966 sal_Int32 nStrLen = aStr1.getLength();
967 if( nStartPos == -1 )
969 nStartPos = nStrLen;
972 sal_Int32 nPos = 0;
973 if( nStartPos <= nStrLen )
975 sal_Int32 nTokenLen = aToken.getLength();
976 if( !nTokenLen )
978 // Always find empty string
979 nPos = nStartPos;
981 else if( nStrLen > 0 )
983 if( !bTextMode )
985 nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
987 else
989 aStr1 = aStr1.toAsciiUpperCase();
990 aToken = aToken.toAsciiUpperCase();
992 nPos = aStr1.lastIndexOf( aToken, nStartPos ) + 1;
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 std::size_t nArgCount = rPar.Count()-1;
1251 if ( nArgCount < 3 || nArgCount > 6 )
1253 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1255 else
1257 OUString aExpStr = rPar.Get(1)->GetOUString();
1258 OUString aFindStr = rPar.Get(2)->GetOUString();
1259 OUString aReplaceStr = rPar.Get(3)->GetOUString();
1261 sal_Int32 lStartPos = 1;
1262 if ( nArgCount >= 4 )
1264 if( rPar.Get(4)->GetType() != SbxEMPTY )
1266 lStartPos = rPar.Get(4)->GetLong();
1268 if( lStartPos < 1)
1270 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1271 lStartPos = 1;
1275 sal_Int32 lCount = -1;
1276 if( nArgCount >=5 )
1278 if( rPar.Get(5)->GetType() != SbxEMPTY )
1280 lCount = rPar.Get(5)->GetLong();
1282 if( lCount < -1)
1284 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1285 lCount = -1;
1289 SbiInstance* pInst = GetSbData()->pInst;
1290 bool bTextMode;
1291 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1292 if( bCompatibility )
1294 SbiRuntime* pRT = pInst->pRun;
1295 bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
1297 else
1299 bTextMode = true;
1301 if ( nArgCount == 6 )
1303 bTextMode = rPar.Get(6)->GetInteger();
1305 sal_Int32 nExpStrLen = aExpStr.getLength();
1306 sal_Int32 nFindStrLen = aFindStr.getLength();
1307 sal_Int32 nReplaceStrLen = aReplaceStr.getLength();
1309 if( lStartPos <= nExpStrLen )
1311 sal_Int32 nPos = lStartPos - 1;
1312 sal_Int32 nCounts = 0;
1313 while( lCount == -1 || lCount > nCounts )
1315 OUString aSrcStr( aExpStr );
1316 if( bTextMode )
1318 aSrcStr = aSrcStr.toAsciiUpperCase();
1319 aFindStr = aFindStr.toAsciiUpperCase();
1321 nPos = aSrcStr.indexOf( aFindStr, nPos );
1322 if( nPos >= 0 )
1324 aExpStr = aExpStr.replaceAt( nPos, nFindStrLen, aReplaceStr );
1325 nPos = nPos + nReplaceStrLen;
1326 nCounts++;
1328 else
1330 break;
1334 rPar.Get(0)->PutString( aExpStr.copy( lStartPos - 1 ) );
1338 void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool)
1340 if ( rPar.Count() < 3 )
1342 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1344 else
1346 const OUString& rStr = rPar.Get(1)->GetOUString();
1347 int nResultLen = rPar.Get(2)->GetLong();
1348 if( nResultLen < 0 )
1350 nResultLen = 0;
1351 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1353 int nStrLen = rStr.getLength();
1354 if ( nResultLen > nStrLen )
1356 nResultLen = nStrLen;
1358 OUString aResultStr = rStr.copy( nStrLen - nResultLen );
1359 rPar.Get(0)->PutString( aResultStr );
1363 void SbRtl_RTL(StarBASIC * pBasic, SbxArray & rPar, bool)
1365 rPar.Get( 0 )->PutObject( pBasic->getRTL().get() );
1368 void SbRtl_RTrim(StarBASIC *, SbxArray & rPar, bool)
1370 if ( rPar.Count() < 2 )
1372 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1374 else
1376 OUString aStr(comphelper::string::stripEnd(rPar.Get(1)->GetOUString(), ' '));
1377 rPar.Get(0)->PutString(aStr);
1381 void SbRtl_Sgn(StarBASIC *, SbxArray & rPar, bool)
1383 if ( rPar.Count() < 2 )
1385 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1387 else
1389 double aDouble = rPar.Get(1)->GetDouble();
1390 sal_Int16 nResult = 0;
1391 if ( aDouble > 0 )
1393 nResult = 1;
1395 else if ( aDouble < 0 )
1397 nResult = -1;
1399 rPar.Get(0)->PutInteger( nResult );
1403 void SbRtl_Space(StarBASIC *, SbxArray & rPar, bool)
1405 if ( rPar.Count() < 2 )
1407 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1409 else
1411 OUStringBuffer aBuf;
1412 string::padToLength(aBuf, rPar.Get(1)->GetLong(), ' ');
1413 rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1417 void SbRtl_Spc(StarBASIC *, SbxArray & rPar, bool)
1419 if ( rPar.Count() < 2 )
1421 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1423 else
1425 OUStringBuffer aBuf;
1426 string::padToLength(aBuf, rPar.Get(1)->GetLong(), ' ');
1427 rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1431 void SbRtl_Sqr(StarBASIC *, SbxArray & rPar, bool)
1433 if ( rPar.Count() < 2 )
1435 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1437 else
1439 double aDouble = rPar.Get(1)->GetDouble();
1440 if ( aDouble >= 0 )
1442 rPar.Get(0)->PutDouble( sqrt( aDouble ));
1444 else
1446 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1451 void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
1453 if ( rPar.Count() < 2 )
1455 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1457 else
1459 OUString aStr;
1460 OUString aStrNew("");
1461 SbxVariableRef pArg = rPar.Get( 1 );
1462 pArg->Format( aStr );
1464 // Numbers start with a space
1465 if( pArg->IsNumericRTL() )
1467 // replace commas by points so that it's symmetric to Val!
1468 aStr = aStr.replaceFirst( ",", "." );
1470 SbiInstance* pInst = GetSbData()->pInst;
1471 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1472 if( bCompatibility )
1474 sal_Int32 nLen = aStr.getLength();
1476 const sal_Unicode* pBuf = aStr.getStr();
1478 bool bNeg = ( pBuf[0] == '-' );
1479 sal_Int32 iZeroSearch = 0;
1480 if( bNeg )
1482 aStrNew += "-";
1483 iZeroSearch++;
1485 else
1487 if( pBuf[0] != ' ' )
1489 aStrNew += " ";
1492 sal_Int32 iNext = iZeroSearch + 1;
1493 if( pBuf[iZeroSearch] == '0' && nLen > iNext && pBuf[iNext] == '.' )
1495 iZeroSearch += 1;
1497 aStrNew += aStr.copy(iZeroSearch);
1499 else
1501 aStrNew = " " + aStr;
1504 else
1506 aStrNew = aStr;
1508 rPar.Get(0)->PutString( aStrNew );
1512 void SbRtl_StrComp(StarBASIC *, SbxArray & rPar, bool)
1514 if ( rPar.Count() < 3 )
1516 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1517 rPar.Get(0)->PutEmpty();
1518 return;
1520 const OUString& rStr1 = rPar.Get(1)->GetOUString();
1521 const OUString& rStr2 = rPar.Get(2)->GetOUString();
1523 SbiInstance* pInst = GetSbData()->pInst;
1524 bool bTextCompare;
1525 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
1526 if( bCompatibility )
1528 SbiRuntime* pRT = pInst->pRun;
1529 bTextCompare = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
1531 else
1533 bTextCompare = true;
1535 if ( rPar.Count() == 4 )
1536 bTextCompare = rPar.Get(3)->GetInteger();
1538 if( !bCompatibility )
1540 bTextCompare = !bTextCompare;
1542 sal_Int32 nRetValue = 0;
1543 if( bTextCompare )
1545 ::utl::TransliterationWrapper* pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
1546 if( !pTransliterationWrapper )
1548 uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
1549 GetSbData()->pTransliterationWrapper.reset(
1550 new ::utl::TransliterationWrapper( xContext,
1551 TransliterationFlags::IGNORE_CASE |
1552 TransliterationFlags::IGNORE_KANA |
1553 TransliterationFlags::IGNORE_WIDTH ) );
1554 pTransliterationWrapper = GetSbData()->pTransliterationWrapper.get();
1557 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
1558 pTransliterationWrapper->loadModuleIfNeeded( eLangType );
1559 nRetValue = pTransliterationWrapper->compareString( rStr1, rStr2 );
1561 else
1563 sal_Int32 aResult;
1564 aResult = rStr1.compareTo( rStr2 );
1565 if ( aResult < 0 )
1567 nRetValue = -1;
1569 else if ( aResult > 0)
1571 nRetValue = 1;
1574 rPar.Get(0)->PutInteger( sal::static_int_cast< sal_Int16 >( nRetValue ) );
1577 void SbRtl_String(StarBASIC *, SbxArray & rPar, bool)
1579 if ( rPar.Count() < 2 )
1581 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1583 else
1585 sal_Unicode aFiller;
1586 sal_Int32 lCount = rPar.Get(1)->GetLong();
1587 if( lCount < 0 || lCount > 0xffff )
1589 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1591 if( rPar.Get(2)->GetType() == SbxINTEGER )
1593 aFiller = static_cast<sal_Unicode>(rPar.Get(2)->GetInteger());
1595 else
1597 const OUString& rStr = rPar.Get(2)->GetOUString();
1598 aFiller = rStr[0];
1600 OUStringBuffer aBuf(lCount);
1601 string::padToLength(aBuf, lCount, aFiller);
1602 rPar.Get(0)->PutString(aBuf.makeStringAndClear());
1606 void SbRtl_Tab(StarBASIC *, SbxArray & rPar, bool)
1608 if ( rPar.Count() < 2 )
1609 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1610 else
1612 OUStringBuffer aStr;
1613 comphelper::string::padToLength(aStr, rPar.Get(1)->GetLong(), '\t');
1614 rPar.Get(0)->PutString(aStr.makeStringAndClear());
1618 void SbRtl_Tan(StarBASIC *, SbxArray & rPar, bool)
1620 if ( rPar.Count() < 2 )
1622 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1624 else
1626 SbxVariableRef pArg = rPar.Get( 1 );
1627 rPar.Get( 0 )->PutDouble( tan( pArg->GetDouble() ) );
1631 void SbRtl_UCase(StarBASIC *, SbxArray & rPar, bool)
1633 if ( rPar.Count() < 2 )
1635 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1637 else
1639 const CharClass& rCharClass = GetCharClass();
1640 OUString aStr( rPar.Get(1)->GetOUString() );
1641 aStr = rCharClass.uppercase( aStr );
1642 rPar.Get(0)->PutString( aStr );
1647 void SbRtl_Val(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
1649 (void)pBasic;
1650 (void)bWrite;
1652 if ( rPar.Count() < 2 )
1654 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1656 else
1658 double nResult = 0.0;
1659 char* pEndPtr;
1661 OUString aStr( rPar.Get(1)->GetOUString() );
1663 FilterWhiteSpace( aStr );
1664 if ( aStr.getLength() > 1 && aStr[0] == '&' )
1666 int nRadix = 10;
1667 char aChar = static_cast<char>(aStr[1]);
1668 if ( aChar == 'h' || aChar == 'H' )
1670 nRadix = 16;
1672 else if ( aChar == 'o' || aChar == 'O' )
1674 nRadix = 8;
1676 if ( nRadix != 10 )
1678 OString aByteStr(OUStringToOString(aStr, osl_getThreadTextEncoding()));
1679 sal_Int16 nlResult = static_cast<sal_Int16>(strtol( aByteStr.getStr()+2, &pEndPtr, nRadix));
1680 nResult = static_cast<double>(nlResult);
1683 else
1685 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
1686 sal_Int32 nParseEnd = 0;
1687 nResult = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd );
1688 if ( eStatus != rtl_math_ConversionStatus_Ok )
1689 StarBASIC::Error( ERRCODE_BASIC_MATH_OVERFLOW );
1690 /* TODO: we should check whether all characters were parsed here,
1691 * but earlier code silently ignored trailing nonsense such as "1x"
1692 * resulting in 1 with the side effect that any alpha-only-string
1693 * like "x" resulted in 0. Not changing that now (2013-03-22) as
1694 * user macros may rely on it. */
1695 #if 0
1696 else if ( nParseEnd != aStr.getLength() )
1697 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
1698 #endif
1701 rPar.Get(0)->PutDouble( nResult );
1706 // Helper functions for date conversion
1707 sal_Int16 implGetDateDay( double aDate )
1709 aDate -= 2.0; // standardize: 1.1.1900 => 0.0
1710 aDate = floor( aDate );
1711 Date aRefDate( 1, 1, 1900 );
1712 aRefDate.AddDays( aDate );
1714 sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetDay() );
1715 return nRet;
1718 sal_Int16 implGetDateMonth( double aDate )
1720 Date aRefDate( 1,1,1900 );
1721 sal_Int32 nDays = static_cast<sal_Int32>(aDate);
1722 nDays -= 2; // standardize: 1.1.1900 => 0.0
1723 aRefDate.AddDays( nDays );
1724 sal_Int16 nRet = static_cast<sal_Int16>( aRefDate.GetMonth() );
1725 return nRet;
1728 css::util::Date SbxDateToUNODate( const SbxValue* const pVal )
1730 double aDate = pVal->GetDate();
1732 css::util::Date aUnoDate;
1733 aUnoDate.Day = implGetDateDay ( aDate );
1734 aUnoDate.Month = implGetDateMonth( aDate );
1735 aUnoDate.Year = implGetDateYear ( aDate );
1737 return aUnoDate;
1740 void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate)
1742 double dDate;
1743 if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, SbDateCorrection::None, dDate ) )
1745 pVal->PutDate( dDate );
1749 // Function to convert date to UNO date (com.sun.star.util.Date)
1750 void SbRtl_CDateToUnoDate(StarBASIC *, SbxArray & rPar, bool)
1752 if ( rPar.Count() != 2 )
1754 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1755 return;
1758 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODate(rPar.Get(1))));
1761 // Function to convert date from UNO date (com.sun.star.util.Date)
1762 void SbRtl_CDateFromUnoDate(StarBASIC *, SbxArray & rPar, bool)
1764 if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
1766 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1767 return;
1770 Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Date>::get()));
1771 css::util::Date aUnoDate;
1772 if(aAny >>= aUnoDate)
1773 SbxDateFromUNODate(rPar.Get(0), aUnoDate);
1774 else
1775 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1778 css::util::Time SbxDateToUNOTime( const SbxValue* const pVal )
1780 double aDate = pVal->GetDate();
1782 css::util::Time aUnoTime;
1783 aUnoTime.Hours = implGetHour ( aDate );
1784 aUnoTime.Minutes = implGetMinute ( aDate );
1785 aUnoTime.Seconds = implGetSecond ( aDate );
1786 aUnoTime.NanoSeconds = 0;
1788 return aUnoTime;
1791 void SbxDateFromUNOTime( SbxValue *pVal, const css::util::Time& aUnoTime)
1793 pVal->PutDate( implTimeSerial(aUnoTime.Hours, aUnoTime.Minutes, aUnoTime.Seconds) );
1796 // Function to convert date to UNO time (com.sun.star.util.Time)
1797 void SbRtl_CDateToUnoTime(StarBASIC *, SbxArray & rPar, bool)
1799 if ( rPar.Count() != 2 )
1801 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1802 return;
1805 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNOTime(rPar.Get(1))));
1808 // Function to convert date from UNO time (com.sun.star.util.Time)
1809 void SbRtl_CDateFromUnoTime(StarBASIC *, SbxArray & rPar, bool)
1811 if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
1813 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1814 return;
1817 Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::Time>::get()));
1818 css::util::Time aUnoTime;
1819 if(aAny >>= aUnoTime)
1820 SbxDateFromUNOTime(rPar.Get(0), aUnoTime);
1821 else
1822 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1825 css::util::DateTime SbxDateToUNODateTime( const SbxValue* const pVal )
1827 double aDate = pVal->GetDate();
1829 css::util::DateTime aUnoDT;
1830 aUnoDT.Day = implGetDateDay ( aDate );
1831 aUnoDT.Month = implGetDateMonth( aDate );
1832 aUnoDT.Year = implGetDateYear ( aDate );
1833 aUnoDT.Hours = implGetHour ( aDate );
1834 aUnoDT.Minutes = implGetMinute ( aDate );
1835 aUnoDT.Seconds = implGetSecond ( aDate );
1836 aUnoDT.NanoSeconds = 0;
1838 return aUnoDT;
1841 void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT)
1843 double dDate(0.0);
1844 if( implDateTimeSerial( aUnoDT.Year, aUnoDT.Month, aUnoDT.Day,
1845 aUnoDT.Hours, aUnoDT.Minutes, aUnoDT.Seconds,
1846 dDate ) )
1848 pVal->PutDate( dDate );
1852 // Function to convert date to UNO date (com.sun.star.util.Date)
1853 void SbRtl_CDateToUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
1855 if ( rPar.Count() != 2 )
1857 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1858 return;
1861 unoToSbxValue(rPar.Get(0), Any(SbxDateToUNODateTime(rPar.Get(1))));
1864 // Function to convert date from UNO date (com.sun.star.util.Date)
1865 void SbRtl_CDateFromUnoDateTime(StarBASIC *, SbxArray & rPar, bool)
1867 if ( rPar.Count() != 2 || rPar.Get(1)->GetType() != SbxOBJECT )
1869 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1870 return;
1873 Any aAny (sbxToUnoValue(rPar.Get(1), cppu::UnoType<css::util::DateTime>::get()));
1874 css::util::DateTime aUnoDT;
1875 if(aAny >>= aUnoDT)
1876 SbxDateFromUNODateTime(rPar.Get(0), aUnoDT);
1877 else
1878 SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
1881 // Function to convert date to ISO 8601 date format YYYYMMDD
1882 void SbRtl_CDateToIso(StarBASIC *, SbxArray & rPar, bool)
1884 if ( rPar.Count() == 2 )
1886 double aDate = rPar.Get(1)->GetDate();
1888 // Date may actually even be -YYYYYMMDD
1889 char Buffer[11];
1890 sal_Int16 nYear = implGetDateYear( aDate );
1891 snprintf( Buffer, sizeof( Buffer ), (nYear < 0 ? "%05d%02d%02d" : "%04d%02d%02d"),
1892 static_cast<int>(nYear),
1893 static_cast<int>(implGetDateMonth( aDate )),
1894 static_cast<int>(implGetDateDay( aDate )) );
1895 OUString aRetStr = OUString::createFromAscii( Buffer );
1896 rPar.Get(0)->PutString( aRetStr );
1898 else
1900 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1904 // Function to convert date from ISO 8601 date format YYYYMMDD or YYYY-MM-DD
1905 // And even YYMMDD for compatibility, sigh...
1906 void SbRtl_CDateFromIso(StarBASIC *, SbxArray & rPar, bool)
1908 if ( rPar.Count() == 2 )
1912 OUString aStr = rPar.Get(1)->GetOUString();
1913 if (aStr.isEmpty())
1914 break;
1916 // Valid formats are
1917 // YYYYMMDD -YYYMMDD YYYYYMMDD -YYYYYMMDD YYMMDD
1918 // YYYY-MM-DD -YYYY-MM-DD YYYYY-MM-DD -YYYYY-MM-DD
1920 sal_Int32 nSign = 1;
1921 if (aStr[0] == '-')
1923 nSign = -1;
1924 aStr = aStr.copy(1);
1926 const sal_Int32 nLen = aStr.getLength();
1928 // Signed YYMMDD two digit year is invalid.
1929 if (nLen == 6 && nSign == -1)
1930 break;
1932 // Now valid
1933 // YYYYMMDD YYYYYMMDD YYMMDD
1934 // YYYY-MM-DD YYYYY-MM-DD
1935 if (nLen != 6 && (nLen < 8 || 11 < nLen))
1936 break;
1938 bool bUseTwoDigitYear = false;
1939 OUString aYearStr, aMonthStr, aDayStr;
1940 if (nLen == 6 || nLen == 8 || nLen == 9)
1942 // ((Y)YY)YYMMDD
1943 if (!comphelper::string::isdigitAsciiString(aStr))
1944 break;
1946 const sal_Int32 nMonthPos = (nLen == 8 ? 4 : (nLen == 6 ? 2 : 5));
1947 if (nMonthPos == 2)
1948 bUseTwoDigitYear = true;
1949 aYearStr = aStr.copy( 0, nMonthPos );
1950 aMonthStr = aStr.copy( nMonthPos, 2 );
1951 aDayStr = aStr.copy( nMonthPos + 2, 2 );
1953 else
1955 // (Y)YYYY-MM-DD
1956 const sal_Int32 nMonthSep = (nLen == 11 ? 5 : 4);
1957 if (aStr.indexOf('-') != nMonthSep)
1958 break;
1959 if (aStr.indexOf('-', nMonthSep + 1) != nMonthSep + 3)
1960 break;
1962 aYearStr = aStr.copy( 0, nMonthSep );
1963 aMonthStr = aStr.copy( nMonthSep + 1, 2 );
1964 aDayStr = aStr.copy( nMonthSep + 4, 2 );
1965 if ( !comphelper::string::isdigitAsciiString(aYearStr) ||
1966 !comphelper::string::isdigitAsciiString(aMonthStr) ||
1967 !comphelper::string::isdigitAsciiString(aDayStr))
1968 break;
1971 double dDate;
1972 if (!implDateSerial( static_cast<sal_Int16>(nSign * aYearStr.toInt32()),
1973 static_cast<sal_Int16>(aMonthStr.toInt32()), static_cast<sal_Int16>(aDayStr.toInt32()),
1974 bUseTwoDigitYear, SbDateCorrection::None, dDate ))
1975 break;
1977 rPar.Get(0)->PutDate( dDate );
1979 return;
1981 while (false);
1983 SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER );
1985 else
1987 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1991 void SbRtl_DateSerial(StarBASIC *, SbxArray & rPar, bool)
1993 if ( rPar.Count() < 4 )
1995 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
1996 return;
1998 sal_Int16 nYear = rPar.Get(1)->GetInteger();
1999 sal_Int16 nMonth = rPar.Get(2)->GetInteger();
2000 sal_Int16 nDay = rPar.Get(3)->GetInteger();
2002 double dDate;
2003 if( implDateSerial( nYear, nMonth, nDay, true, SbDateCorrection::RollOver, dDate ) )
2005 rPar.Get(0)->PutDate( dDate );
2009 void SbRtl_TimeSerial(StarBASIC *, SbxArray & rPar, bool)
2011 if ( rPar.Count() < 4 )
2013 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2014 return;
2016 sal_Int16 nHour = rPar.Get(1)->GetInteger();
2017 if ( nHour == 24 )
2019 nHour = 0; // because of UNO DateTimes, which go till 24 o'clock
2021 sal_Int16 nMinute = rPar.Get(2)->GetInteger();
2022 sal_Int16 nSecond = rPar.Get(3)->GetInteger();
2023 if ((nHour < 0 || nHour > 23) ||
2024 (nMinute < 0 || nMinute > 59 ) ||
2025 (nSecond < 0 || nSecond > 59 ))
2027 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2028 return;
2031 rPar.Get(0)->PutDate( implTimeSerial(nHour, nMinute, nSecond) ); // JSM
2034 void SbRtl_DateValue(StarBASIC *, SbxArray & rPar, bool)
2036 if ( rPar.Count() < 2 )
2038 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2040 else
2042 // #39629 check GetSbData()->pInst, can be called from the URL line
2043 std::shared_ptr<SvNumberFormatter> pFormatter;
2044 if( GetSbData()->pInst )
2046 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2048 else
2050 sal_uInt32 n; // Dummy
2051 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
2054 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
2055 sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
2056 double fResult;
2057 OUString aStr( rPar.Get(1)->GetOUString() );
2058 bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
2059 SvNumFormatType nType = pFormatter->GetType( nIndex );
2061 // DateValue("February 12, 1969") raises error if the system locale is not en_US
2062 // It seems that both locale number formatter and English number
2063 // formatter are supported in Visual Basic.
2064 if( !bSuccess && ( eLangType != LANGUAGE_ENGLISH_US ) )
2066 // Try using LANGUAGE_ENGLISH_US to get the date value.
2067 nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US);
2068 bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, fResult );
2069 nType = pFormatter->GetType( nIndex );
2072 if(bSuccess && (nType==SvNumFormatType::DATE || nType==SvNumFormatType::DATETIME))
2074 if ( nType == SvNumFormatType::DATETIME )
2076 // cut time
2077 if ( fResult > 0.0 )
2079 fResult = floor( fResult );
2081 else
2083 fResult = ceil( fResult );
2086 rPar.Get(0)->PutDate( fResult );
2088 else
2090 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
2095 void SbRtl_TimeValue(StarBASIC *, SbxArray & rPar, bool)
2097 if ( rPar.Count() < 2 )
2099 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2101 else
2103 std::shared_ptr<SvNumberFormatter> pFormatter;
2104 if( GetSbData()->pInst )
2105 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2106 else
2108 sal_uInt32 n;
2109 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
2112 sal_uInt32 nIndex = 0;
2113 double fResult;
2114 bool bSuccess = pFormatter->IsNumberFormat( rPar.Get(1)->GetOUString(),
2115 nIndex, fResult );
2116 SvNumFormatType nType = pFormatter->GetType(nIndex);
2117 if(bSuccess && (nType==SvNumFormatType::TIME||nType==SvNumFormatType::DATETIME))
2119 if ( nType == SvNumFormatType::DATETIME )
2121 // cut days
2122 fResult = fmod( fResult, 1 );
2124 rPar.Get(0)->PutDate( fResult );
2126 else
2128 StarBASIC::Error( ERRCODE_BASIC_CONVERSION );
2133 void SbRtl_Day(StarBASIC *, SbxArray & rPar, bool)
2135 if ( rPar.Count() < 2 )
2137 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2139 else
2141 SbxVariableRef pArg = rPar.Get( 1 );
2142 double aDate = pArg->GetDate();
2144 sal_Int16 nDay = implGetDateDay( aDate );
2145 rPar.Get(0)->PutInteger( nDay );
2149 void SbRtl_Year(StarBASIC *, SbxArray & rPar, bool)
2151 if ( rPar.Count() < 2 )
2153 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2155 else
2157 sal_Int16 nYear = implGetDateYear( rPar.Get(1)->GetDate() );
2158 rPar.Get(0)->PutInteger( nYear );
2162 sal_Int16 implGetHour( double dDate )
2164 double nFrac = dDate - floor( dDate );
2165 nFrac *= 86400.0;
2166 sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
2167 sal_Int16 nHour = static_cast<sal_Int16>(nSeconds / 3600);
2168 return nHour;
2171 void SbRtl_Hour(StarBASIC *, SbxArray & rPar, bool)
2173 if ( rPar.Count() < 2 )
2175 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2177 else
2179 double nArg = rPar.Get(1)->GetDate();
2180 sal_Int16 nHour = implGetHour( nArg );
2181 rPar.Get(0)->PutInteger( nHour );
2185 void SbRtl_Minute(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 nMin = implGetMinute( nArg );
2195 rPar.Get(0)->PutInteger( nMin );
2199 void SbRtl_Month(StarBASIC *, SbxArray & rPar, bool)
2201 if ( rPar.Count() < 2 )
2203 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2205 else
2207 sal_Int16 nMonth = implGetDateMonth( rPar.Get(1)->GetDate() );
2208 rPar.Get(0)->PutInteger( nMonth );
2212 sal_Int16 implGetSecond( double dDate )
2214 double nFrac = dDate - floor( dDate );
2215 nFrac *= 86400.0;
2216 sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
2217 sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds / 3600);
2218 nSeconds -= nTemp * 3600;
2219 nTemp = static_cast<sal_Int16>(nSeconds / 60);
2220 nSeconds -= nTemp * 60;
2222 sal_Int16 nRet = static_cast<sal_Int16>(nSeconds);
2223 return nRet;
2226 void SbRtl_Second(StarBASIC *, SbxArray & rPar, bool)
2228 if ( rPar.Count() < 2 )
2230 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2232 else
2234 double nArg = rPar.Get(1)->GetDate();
2235 sal_Int16 nSecond = implGetSecond( nArg );
2236 rPar.Get(0)->PutInteger( nSecond );
2240 double Now_Impl()
2242 DateTime aDateTime( DateTime::SYSTEM );
2243 double aSerial = static_cast<double>(GetDayDiff( aDateTime ));
2244 long nSeconds = aDateTime.GetHour();
2245 nSeconds *= 3600;
2246 nSeconds += aDateTime.GetMin() * 60;
2247 nSeconds += aDateTime.GetSec();
2248 double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
2249 aSerial += nDays;
2250 return aSerial;
2253 // Date Now()
2255 void SbRtl_Now(StarBASIC *, SbxArray & rPar, bool)
2257 rPar.Get(0)->PutDate( Now_Impl() );
2260 // Date Time()
2262 void SbRtl_Time(StarBASIC *, SbxArray & rPar, bool bWrite)
2264 if ( !bWrite )
2266 tools::Time aTime( tools::Time::SYSTEM );
2267 SbxVariable* pMeth = rPar.Get( 0 );
2268 OUString aRes;
2269 if( pMeth->IsFixed() )
2271 // Time$: hh:mm:ss
2272 char buf[ 20 ];
2273 snprintf( buf, sizeof(buf), "%02d:%02d:%02d",
2274 aTime.GetHour(), aTime.GetMin(), aTime.GetSec() );
2275 aRes = OUString::createFromAscii( buf );
2277 else
2279 // Time: system dependent
2280 long nSeconds=aTime.GetHour();
2281 nSeconds *= 3600;
2282 nSeconds += aTime.GetMin() * 60;
2283 nSeconds += aTime.GetSec();
2284 double nDays = static_cast<double>(nSeconds) * ( 1.0 / (24.0*3600.0) );
2285 Color* pCol;
2287 std::shared_ptr<SvNumberFormatter> pFormatter;
2288 sal_uInt32 nIndex;
2289 if( GetSbData()->pInst )
2291 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2292 nIndex = GetSbData()->pInst->GetStdTimeIdx();
2294 else
2296 sal_uInt32 n; // Dummy
2297 pFormatter = SbiInstance::PrepareNumberFormatter( n, nIndex, n );
2300 pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
2302 pMeth->PutString( aRes );
2304 else
2306 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
2310 void SbRtl_Timer(StarBASIC *, SbxArray & rPar, bool)
2312 tools::Time aTime( tools::Time::SYSTEM );
2313 long nSeconds = aTime.GetHour();
2314 nSeconds *= 3600;
2315 nSeconds += aTime.GetMin() * 60;
2316 nSeconds += aTime.GetSec();
2317 rPar.Get(0)->PutDate( static_cast<double>(nSeconds) );
2321 void SbRtl_Date(StarBASIC *, SbxArray & rPar, bool bWrite)
2323 if ( !bWrite )
2325 Date aToday( Date::SYSTEM );
2326 double nDays = static_cast<double>(GetDayDiff( aToday ));
2327 SbxVariable* pMeth = rPar.Get( 0 );
2328 if( pMeth->IsString() )
2330 OUString aRes;
2331 Color* pCol;
2333 std::shared_ptr<SvNumberFormatter> pFormatter;
2334 sal_uInt32 nIndex;
2335 if( GetSbData()->pInst )
2337 pFormatter = GetSbData()->pInst->GetNumberFormatter();
2338 nIndex = GetSbData()->pInst->GetStdDateIdx();
2340 else
2342 sal_uInt32 n;
2343 pFormatter = SbiInstance::PrepareNumberFormatter( nIndex, n, n );
2346 pFormatter->GetOutputString( nDays, nIndex, aRes, &pCol );
2347 pMeth->PutString( aRes );
2349 else
2351 pMeth->PutDate( nDays );
2354 else
2356 StarBASIC::Error( ERRCODE_BASIC_NOT_IMPLEMENTED );
2360 void SbRtl_IsArray(StarBASIC *, SbxArray & rPar, bool)
2362 if ( rPar.Count() < 2 )
2364 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2366 else
2368 rPar.Get(0)->PutBool((rPar.Get(1)->GetType() & SbxARRAY) != 0);
2372 void SbRtl_IsObject(StarBASIC *, SbxArray & rPar, bool)
2374 if ( rPar.Count() < 2 )
2376 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2378 else
2380 SbxVariable* pVar = rPar.Get(1);
2381 bool bObject = pVar->IsObject();
2382 SbxBase* pObj = (bObject ? pVar->GetObject() : nullptr);
2384 if( auto pUnoClass = dynamic_cast<SbUnoClass*>( pObj) )
2386 bObject = pUnoClass->getUnoClass().is();
2388 rPar.Get( 0 )->PutBool( bObject );
2392 void SbRtl_IsDate(StarBASIC *, SbxArray & rPar, bool)
2394 if ( rPar.Count() < 2 )
2396 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2398 else
2400 // #46134 only string is converted, all other types result in sal_False
2401 SbxVariableRef xArg = rPar.Get( 1 );
2402 SbxDataType eType = xArg->GetType();
2403 bool bDate = false;
2405 if( eType == SbxDATE )
2407 bDate = true;
2409 else if( eType == SbxSTRING )
2411 ErrCode nPrevError = SbxBase::GetError();
2412 SbxBase::ResetError();
2414 // force conversion of the parameter to SbxDATE
2415 xArg->SbxValue::GetDate();
2417 bDate = !SbxBase::IsError();
2419 SbxBase::ResetError();
2420 SbxBase::SetError( nPrevError );
2422 rPar.Get( 0 )->PutBool( bDate );
2426 void SbRtl_IsEmpty(StarBASIC *, SbxArray & rPar, bool)
2428 if ( rPar.Count() < 2 )
2430 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2432 else
2434 SbxVariable* pVar = nullptr;
2435 if( SbiRuntime::isVBAEnabled() )
2437 pVar = getDefaultProp( rPar.Get(1) );
2439 if ( pVar )
2441 pVar->Broadcast( SfxHintId::BasicDataWanted );
2442 rPar.Get( 0 )->PutBool( pVar->IsEmpty() );
2444 else
2446 rPar.Get( 0 )->PutBool( rPar.Get(1)->IsEmpty() );
2451 void SbRtl_IsError(StarBASIC *, SbxArray & rPar, bool)
2453 if ( rPar.Count() < 2 )
2455 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2457 else
2459 SbxVariable* pVar =rPar.Get( 1 );
2460 SbUnoObject* pObj = dynamic_cast<SbUnoObject*>( pVar );
2461 if ( !pObj )
2463 if ( SbxBase* pBaseObj = (pVar->IsObject() ? pVar->GetObject() : nullptr) )
2465 pObj = dynamic_cast<SbUnoObject*>( pBaseObj );
2468 uno::Reference< script::XErrorQuery > xError;
2469 if ( pObj )
2471 xError.set( pObj->getUnoAny(), uno::UNO_QUERY );
2473 if ( xError.is() )
2475 rPar.Get( 0 )->PutBool( xError->hasError() );
2477 else
2479 rPar.Get( 0 )->PutBool( rPar.Get(1)->IsErr() );
2484 void SbRtl_IsNull(StarBASIC *, SbxArray & rPar, bool)
2486 if ( rPar.Count() < 2 )
2488 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2490 else
2492 // #51475 because of Uno-objects return true
2493 // even if the pObj value is NULL
2494 SbxVariableRef pArg = rPar.Get( 1 );
2495 bool bNull = rPar.Get(1)->IsNull();
2496 if( !bNull && pArg->GetType() == SbxOBJECT )
2498 SbxBase* pObj = pArg->GetObject();
2499 if( !pObj )
2501 bNull = true;
2504 rPar.Get( 0 )->PutBool( bNull );
2508 void SbRtl_IsNumeric(StarBASIC *, SbxArray & rPar, bool)
2510 if ( rPar.Count() < 2 )
2512 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2514 else
2516 rPar.Get( 0 )->PutBool( rPar.Get( 1 )->IsNumericRTL() );
2521 void SbRtl_IsMissing(StarBASIC *, SbxArray & rPar, bool)
2523 if ( rPar.Count() < 2 )
2525 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2527 else
2529 // #57915 Missing is reported by an error
2530 rPar.Get( 0 )->PutBool( rPar.Get(1)->IsErr() );
2534 // Function looks for wildcards, removes them and always returns the pure path
2535 static OUString implSetupWildcard(const OUString& rFileParam, SbiRTLData& rRTLData)
2537 static const sal_Char cDelim1 = '/';
2538 static const sal_Char cDelim2 = '\\';
2539 static const sal_Char cWild1 = '*';
2540 static const sal_Char cWild2 = '?';
2542 rRTLData.pWildCard.reset();
2543 rRTLData.sFullNameToBeChecked.clear();
2545 OUString aFileParam = rFileParam;
2546 sal_Int32 nLastWild = aFileParam.lastIndexOf( cWild1 );
2547 if( nLastWild < 0 )
2549 nLastWild = aFileParam.lastIndexOf( cWild2 );
2551 bool bHasWildcards = ( nLastWild >= 0 );
2554 sal_Int32 nLastDelim = aFileParam.lastIndexOf( cDelim1 );
2555 if( nLastDelim < 0 )
2557 nLastDelim = aFileParam.lastIndexOf( cDelim2 );
2559 if( bHasWildcards )
2561 // Wildcards in path?
2562 if( nLastDelim >= 0 && nLastDelim > nLastWild )
2564 return aFileParam;
2567 else
2569 OUString aPathStr = getFullPath( aFileParam );
2570 if( nLastDelim != aFileParam.getLength() - 1 )
2572 rRTLData.sFullNameToBeChecked = aPathStr;
2574 return aPathStr;
2577 OUString aPureFileName;
2578 if( nLastDelim < 0 )
2580 aPureFileName = aFileParam;
2581 aFileParam.clear();
2583 else
2585 aPureFileName = aFileParam.copy( nLastDelim + 1 );
2586 aFileParam = aFileParam.copy( 0, nLastDelim );
2589 // Try again to get a valid URL/UNC-path with only the path
2590 OUString aPathStr = getFullPath( aFileParam );
2592 // Is there a pure file name left? Otherwise the path is
2593 // invalid anyway because it was not accepted by OSL before
2594 if (aPureFileName != "*")
2596 rRTLData.pWildCard = std::make_unique<WildCard>(aPureFileName);
2598 return aPathStr;
2601 static bool implCheckWildcard(const OUString& rName, SbiRTLData const& rRTLData)
2603 bool bMatch = true;
2605 if (rRTLData.pWildCard)
2607 bMatch = rRTLData.pWildCard->Matches(rName);
2609 return bMatch;
2613 static bool isRootDir( const OUString& aDirURLStr )
2615 INetURLObject aDirURLObj( aDirURLStr );
2616 bool bRoot = false;
2618 // Check if it's a root directory
2619 sal_Int32 nCount = aDirURLObj.getSegmentCount();
2621 // No segment means Unix root directory "file:///"
2622 if( nCount == 0 )
2624 bRoot = true;
2626 // Exactly one segment needs further checking, because it
2627 // can be Unix "file:///foo/" -> no root
2628 // or Windows "file:///c:/" -> root
2629 else if( nCount == 1 )
2631 OUString aSeg1 = aDirURLObj.getName( 0, true,
2632 INetURLObject::DecodeMechanism::WithCharset );
2633 if( aSeg1[1] == ':' )
2635 bRoot = true;
2638 // More than one segments can never be root
2639 // so bRoot remains false
2641 return bRoot;
2644 void SbRtl_Dir(StarBASIC *, SbxArray & rPar, bool)
2646 OUString aPath;
2648 sal_uInt16 nParCount = rPar.Count();
2649 if( nParCount > 3 )
2651 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
2653 else
2655 SbiRTLData& rRTLData = GetSbData()->pInst->GetRTLData();
2657 if( hasUno() )
2659 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2660 if( xSFI.is() )
2662 if ( nParCount >= 2 )
2664 OUString aFileParam = rPar.Get(1)->GetOUString();
2666 OUString aFileURLStr = implSetupWildcard(aFileParam, rRTLData);
2667 if (!rRTLData.sFullNameToBeChecked.isEmpty())
2669 bool bExists = false;
2670 try { bExists = xSFI->exists( aFileURLStr ); }
2671 catch(const Exception & ) {}
2673 OUString aNameOnlyStr;
2674 if( bExists )
2676 INetURLObject aFileURL( aFileURLStr );
2677 aNameOnlyStr = aFileURL.getName( INetURLObject::LAST_SEGMENT,
2678 true, INetURLObject::DecodeMechanism::WithCharset );
2680 rPar.Get(0)->PutString( aNameOnlyStr );
2681 return;
2686 OUString aDirURLStr;
2687 bool bFolder = xSFI->isFolder( aFileURLStr );
2689 if( bFolder )
2691 aDirURLStr = aFileURLStr;
2693 else
2695 rPar.Get(0)->PutString( "" );
2698 SbAttributes nFlags = SbAttributes::NONE;
2699 if ( nParCount > 2 )
2701 rRTLData.nDirFlags = nFlags
2702 = static_cast<SbAttributes>(rPar.Get(2)->GetInteger());
2704 else
2706 rRTLData.nDirFlags = SbAttributes::NONE;
2708 // Read directory
2709 bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
2710 rRTLData.aDirSeq = xSFI->getFolderContents(aDirURLStr, bIncludeFolders);
2711 rRTLData.nCurDirPos = 0;
2713 // #78651 Add "." and ".." directories for VB compatibility
2714 if( bIncludeFolders )
2716 bool bRoot = isRootDir( aDirURLStr );
2718 // If it's no root directory we flag the need for
2719 // the "." and ".." directories by the value -2
2720 // for the actual position. Later for -2 will be
2721 // returned "." and for -1 ".."
2722 if( !bRoot )
2724 rRTLData.nCurDirPos = -2;
2728 catch(const Exception & )
2734 if (rRTLData.aDirSeq.hasElements())
2736 bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY);
2738 SbiInstance* pInst = GetSbData()->pInst;
2739 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
2740 for( ;; )
2742 if (rRTLData.nCurDirPos < 0)
2744 if (rRTLData.nCurDirPos == -2)
2746 aPath = ".";
2748 else if (rRTLData.nCurDirPos == -1)
2750 aPath = "..";
2752 rRTLData.nCurDirPos++;
2754 else if (rRTLData.nCurDirPos >= rRTLData.aDirSeq.getLength())
2756 rRTLData.aDirSeq.realloc(0);
2757 aPath.clear();
2758 break;
2760 else
2762 OUString aFile
2763 = rRTLData.aDirSeq.getConstArray()[rRTLData.nCurDirPos++];
2765 if( bCompatibility )
2767 if( !bFolderFlag )
2769 bool bFolder = xSFI->isFolder( aFile );
2770 if( bFolder )
2772 continue;
2776 else
2778 // Only directories
2779 if( bFolderFlag )
2781 bool bFolder = xSFI->isFolder( aFile );
2782 if( !bFolder )
2784 continue;
2789 INetURLObject aURL( aFile );
2790 aPath = aURL.getName( INetURLObject::LAST_SEGMENT, true,
2791 INetURLObject::DecodeMechanism::WithCharset );
2794 bool bMatch = implCheckWildcard(aPath, rRTLData);
2795 if( !bMatch )
2797 continue;
2799 break;
2802 rPar.Get(0)->PutString( aPath );
2805 else
2807 // TODO: OSL
2808 if ( nParCount >= 2 )
2810 OUString aFileParam = rPar.Get(1)->GetOUString();
2812 OUString aDirURL = implSetupWildcard(aFileParam, rRTLData);
2814 SbAttributes nFlags = SbAttributes::NONE;
2815 if ( nParCount > 2 )
2817 rRTLData.nDirFlags = nFlags
2818 = static_cast<SbAttributes>(rPar.Get(2)->GetInteger());
2820 else
2822 rRTLData.nDirFlags = SbAttributes::NONE;
2825 // Read directory
2826 bool bIncludeFolders = bool(nFlags & SbAttributes::DIRECTORY);
2827 rRTLData.pDir = std::make_unique<Directory>(aDirURL);
2828 FileBase::RC nRet = rRTLData.pDir->open();
2829 if( nRet != FileBase::E_None )
2831 rRTLData.pDir.reset();
2832 rPar.Get(0)->PutString( OUString() );
2833 return;
2836 // #86950 Add "." and ".." directories for VB compatibility
2837 rRTLData.nCurDirPos = 0;
2838 if( bIncludeFolders )
2840 bool bRoot = isRootDir( aDirURL );
2842 // If it's no root directory we flag the need for
2843 // the "." and ".." directories by the value -2
2844 // for the actual position. Later for -2 will be
2845 // returned "." and for -1 ".."
2846 if( !bRoot )
2848 rRTLData.nCurDirPos = -2;
2854 if (rRTLData.pDir)
2856 bool bFolderFlag = bool(rRTLData.nDirFlags & SbAttributes::DIRECTORY);
2857 for( ;; )
2859 if (rRTLData.nCurDirPos < 0)
2861 if (rRTLData.nCurDirPos == -2)
2863 aPath = ".";
2865 else if (rRTLData.nCurDirPos == -1)
2867 aPath = "..";
2869 rRTLData.nCurDirPos++;
2871 else
2873 DirectoryItem aItem;
2874 FileBase::RC nRet = rRTLData.pDir->getNextItem(aItem);
2875 if( nRet != FileBase::E_None )
2877 rRTLData.pDir.reset();
2878 aPath.clear();
2879 break;
2882 // Handle flags
2883 FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName );
2884 nRet = aItem.getFileStatus( aFileStatus );
2885 if( nRet != FileBase::E_None )
2887 SAL_WARN("basic", "getFileStatus failed");
2888 continue;
2891 // Only directories?
2892 if( bFolderFlag )
2894 FileStatus::Type aType = aFileStatus.getFileType();
2895 bool bFolder = isFolder( aType );
2896 if( !bFolder )
2898 continue;
2902 aPath = aFileStatus.getFileName();
2905 bool bMatch = implCheckWildcard(aPath, rRTLData);
2906 if( !bMatch )
2908 continue;
2910 break;
2913 rPar.Get(0)->PutString( aPath );
2919 void SbRtl_GetAttr(StarBASIC * pBasic, SbxArray & rPar, bool bWrite)
2921 (void)pBasic;
2922 (void)bWrite;
2924 if ( rPar.Count() == 2 )
2926 sal_Int16 nFlags = 0;
2928 // In Windows, we want to use Windows API to get the file attributes
2929 // for VBA interoperability.
2930 #if defined(_WIN32)
2931 if( SbiRuntime::isVBAEnabled() )
2933 OUString aPathURL = getFullPath( rPar.Get(1)->GetOUString() );
2934 OUString aPath;
2935 FileBase::getSystemPathFromFileURL( aPathURL, aPath );
2936 DWORD nRealFlags = GetFileAttributesW (o3tl::toW(aPath.getStr()));
2937 if (nRealFlags != 0xffffffff)
2939 if (nRealFlags == FILE_ATTRIBUTE_NORMAL)
2941 nRealFlags = 0;
2943 nFlags = static_cast<sal_Int16>(nRealFlags);
2945 else
2947 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
2949 rPar.Get(0)->PutInteger( nFlags );
2951 return;
2953 #endif
2955 if( hasUno() )
2957 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
2958 if( xSFI.is() )
2962 OUString aPath = getFullPath( rPar.Get(1)->GetOUString() );
2963 bool bExists = false;
2964 try { bExists = xSFI->exists( aPath ); }
2965 catch(const Exception & ) {}
2966 if( !bExists )
2968 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
2969 return;
2972 bool bReadOnly = xSFI->isReadOnly( aPath );
2973 bool bHidden = xSFI->isHidden( aPath );
2974 bool bDirectory = xSFI->isFolder( aPath );
2975 if( bReadOnly )
2977 nFlags |= sal_uInt16(SbAttributes::READONLY);
2979 if( bHidden )
2981 nFlags |= sal_uInt16(SbAttributes::HIDDEN);
2983 if( bDirectory )
2985 nFlags |= sal_uInt16(SbAttributes::DIRECTORY);
2988 catch(const Exception & )
2990 StarBASIC::Error( ERRCODE_IO_GENERAL );
2994 else
2996 DirectoryItem aItem;
2997 (void)DirectoryItem::get( getFullPath( rPar.Get(1)->GetOUString() ), aItem );
2998 FileStatus aFileStatus( osl_FileStatus_Mask_Attributes | osl_FileStatus_Mask_Type );
2999 (void)aItem.getFileStatus( aFileStatus );
3000 sal_uInt64 nAttributes = aFileStatus.getAttributes();
3001 bool bReadOnly = (nAttributes & osl_File_Attribute_ReadOnly) != 0;
3003 FileStatus::Type aType = aFileStatus.getFileType();
3004 bool bDirectory = isFolder( aType );
3005 if( bReadOnly )
3007 nFlags |= sal_uInt16(SbAttributes::READONLY);
3009 if( bDirectory )
3011 nFlags |= sal_uInt16(SbAttributes::DIRECTORY);
3014 rPar.Get(0)->PutInteger( nFlags );
3016 else
3018 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3023 void SbRtl_FileDateTime(StarBASIC *, SbxArray & rPar, bool)
3025 if ( rPar.Count() != 2 )
3027 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3029 else
3031 OUString aPath = rPar.Get(1)->GetOUString();
3032 tools::Time aTime( tools::Time::EMPTY );
3033 Date aDate( Date::EMPTY );
3034 if( hasUno() )
3036 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
3037 if( xSFI.is() )
3041 util::DateTime aUnoDT = xSFI->getDateTimeModified( aPath );
3042 aTime = tools::Time( aUnoDT );
3043 aDate = Date( aUnoDT );
3045 catch(const Exception & )
3047 StarBASIC::Error( ERRCODE_IO_GENERAL );
3051 else
3053 bool bSuccess = false;
3056 DirectoryItem aItem;
3057 if (DirectoryItem::get( getFullPath( aPath ), aItem ) != FileBase::E_None)
3058 break;
3060 FileStatus aFileStatus( osl_FileStatus_Mask_ModifyTime );
3061 if (aItem.getFileStatus( aFileStatus ) != FileBase::E_None)
3062 break;
3064 TimeValue aTimeVal = aFileStatus.getModifyTime();
3065 oslDateTime aDT;
3066 if (!osl_getDateTimeFromTimeValue( &aTimeVal, &aDT ))
3067 // Strictly spoken this is not an i/o error but some other failure.
3068 break;
3070 aTime = tools::Time( aDT.Hours, aDT.Minutes, aDT.Seconds, aDT.NanoSeconds );
3071 aDate = Date( aDT.Day, aDT.Month, aDT.Year );
3072 bSuccess = true;
3074 while(false);
3076 if (!bSuccess)
3077 StarBASIC::Error( ERRCODE_IO_GENERAL );
3080 // An empty date shall not result in a formatted null-date (1899-12-30
3081 // or 1900-01-01) or even worse -0001-12-03 or some such due to how
3082 // GetDayDiff() treats things. There should be an error set in this
3083 // case anyway because of a missing file or other error above, but... so
3084 // do not even bother to use the number formatter.
3085 OUString aRes;
3086 if (aDate.IsEmpty())
3088 aRes = "0000-00-00 00:00:00";
3090 else
3092 double fSerial = static_cast<double>(GetDayDiff( aDate ));
3093 long nSeconds = aTime.GetHour();
3094 nSeconds *= 3600;
3095 nSeconds += aTime.GetMin() * 60;
3096 nSeconds += aTime.GetSec();
3097 double nDays = static_cast<double>(nSeconds) / (24.0*3600.0);
3098 fSerial += nDays;
3100 Color* pCol;
3102 std::shared_ptr<SvNumberFormatter> pFormatter;
3103 sal_uInt32 nIndex;
3104 if( GetSbData()->pInst )
3106 pFormatter = GetSbData()->pInst->GetNumberFormatter();
3107 nIndex = GetSbData()->pInst->GetStdDateTimeIdx();
3109 else
3111 sal_uInt32 n;
3112 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, nIndex );
3115 pFormatter->GetOutputString( fSerial, nIndex, aRes, &pCol );
3117 rPar.Get(0)->PutString( aRes );
3122 void SbRtl_EOF(StarBASIC *, SbxArray & rPar, bool)
3124 // No changes for UCB
3125 if ( rPar.Count() != 2 )
3127 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3129 else
3131 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3132 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3133 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3134 if ( !pSbStrm )
3136 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3137 return;
3139 bool beof;
3140 SvStream* pSvStrm = pSbStrm->GetStrm();
3141 if ( pSbStrm->IsText() )
3143 char cBla;
3144 (*pSvStrm).ReadChar( cBla ); // can we read another character?
3145 beof = pSvStrm->eof();
3146 if ( !beof )
3148 pSvStrm->SeekRel( -1 );
3151 else
3153 beof = pSvStrm->eof(); // for binary data!
3155 rPar.Get(0)->PutBool( beof );
3159 void SbRtl_FileAttr(StarBASIC *, SbxArray & rPar, bool)
3161 // No changes for UCB
3162 // #57064 Although this function doesn't operate with DirEntry, it is
3163 // not touched by the adjustment to virtual URLs, as it only works on
3164 // already opened files and the name doesn't matter there.
3166 if ( rPar.Count() != 3 )
3168 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3170 else
3172 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3173 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3174 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3175 if ( !pSbStrm )
3177 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3178 return;
3180 sal_Int16 nRet;
3181 if ( rPar.Get(2)->GetInteger() == 1 )
3183 nRet = static_cast<sal_Int16>(pSbStrm->GetMode());
3185 else
3187 nRet = 0; // System file handle not supported
3189 rPar.Get(0)->PutInteger( nRet );
3192 void SbRtl_Loc(StarBASIC *, SbxArray & rPar, bool)
3194 // No changes for UCB
3195 if ( rPar.Count() != 2 )
3197 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3199 else
3201 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3202 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3203 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3204 if ( !pSbStrm )
3206 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3207 return;
3209 SvStream* pSvStrm = pSbStrm->GetStrm();
3210 std::size_t nPos;
3211 if( pSbStrm->IsRandom())
3213 short nBlockLen = pSbStrm->GetBlockLen();
3214 nPos = nBlockLen ? (pSvStrm->Tell() / nBlockLen) : 0;
3215 nPos++; // block positions starting at 1
3217 else if ( pSbStrm->IsText() )
3219 nPos = pSbStrm->GetLine();
3221 else if( pSbStrm->IsBinary() )
3223 nPos = pSvStrm->Tell();
3225 else if ( pSbStrm->IsSeq() )
3227 nPos = ( pSvStrm->Tell()+1 ) / 128;
3229 else
3231 nPos = pSvStrm->Tell();
3233 rPar.Get(0)->PutLong( static_cast<sal_Int32>(nPos) );
3237 void SbRtl_Lof(StarBASIC *, SbxArray & rPar, bool)
3239 // No changes for UCB
3240 if ( rPar.Count() != 2 )
3242 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3244 else
3246 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3247 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3248 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3249 if ( !pSbStrm )
3251 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3252 return;
3254 SvStream* pSvStrm = pSbStrm->GetStrm();
3255 sal_uInt64 const nLen = pSvStrm->TellEnd();
3256 rPar.Get(0)->PutLong( static_cast<sal_Int32>(nLen) );
3261 void SbRtl_Seek(StarBASIC *, SbxArray & rPar, bool)
3263 // No changes for UCB
3264 int nArgs = static_cast<int>(rPar.Count());
3265 if ( nArgs < 2 || nArgs > 3 )
3267 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3268 return;
3270 sal_Int16 nChannel = rPar.Get(1)->GetInteger();
3271 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3272 SbiStream* pSbStrm = pIO->GetStream( nChannel );
3273 if ( !pSbStrm )
3275 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
3276 return;
3278 SvStream* pStrm = pSbStrm->GetStrm();
3280 if ( nArgs == 2 ) // Seek-Function
3282 sal_uInt64 nPos = pStrm->Tell();
3283 if( pSbStrm->IsRandom() )
3285 nPos = nPos / pSbStrm->GetBlockLen();
3287 nPos++; // Basic counts from 1
3288 rPar.Get(0)->PutLong( static_cast<sal_Int32>(nPos) );
3290 else // Seek-Statement
3292 sal_Int32 nPos = rPar.Get(2)->GetLong();
3293 if ( nPos < 1 )
3295 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3296 return;
3298 nPos--; // Basic counts from 1, SvStreams count from 0
3299 pSbStrm->SetExpandOnWriteTo( 0 );
3300 if ( pSbStrm->IsRandom() )
3302 nPos *= pSbStrm->GetBlockLen();
3304 pStrm->Seek( static_cast<sal_uInt64>(nPos) );
3305 pSbStrm->SetExpandOnWriteTo( nPos );
3309 void SbRtl_Format(StarBASIC *, SbxArray & rPar, bool)
3311 sal_uInt16 nArgCount = rPar.Count();
3312 if ( nArgCount < 2 || nArgCount > 3 )
3314 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3316 else
3318 OUString aResult;
3319 if( nArgCount == 2 )
3321 rPar.Get(1)->Format( aResult );
3323 else
3325 OUString aFmt( rPar.Get(2)->GetOUString() );
3326 rPar.Get(1)->Format( aResult, &aFmt );
3328 rPar.Get(0)->PutString( aResult );
3332 // https://msdn.microsoft.com/en-us/vba/language-reference-vba/articles/formatnumber-function
3333 void SbRtl_FormatNumber(StarBASIC*, SbxArray& rPar, bool)
3335 const sal_uInt16 nArgCount = rPar.Count();
3336 if (nArgCount < 2 || nArgCount > 6)
3338 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3339 return;
3342 // The UI locale never changes -> we can use static value here
3343 static const LocaleDataWrapper localeData(Application::GetSettings().GetUILanguageTag());
3344 sal_Int16 nNumDigitsAfterDecimal = -1;
3345 if (nArgCount > 2 && !rPar.Get(2)->IsEmpty())
3347 nNumDigitsAfterDecimal = rPar.Get(2)->GetInteger();
3348 if (nNumDigitsAfterDecimal < -1)
3350 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3351 return;
3353 else if (nNumDigitsAfterDecimal > 255)
3354 nNumDigitsAfterDecimal %= 256;
3356 if (nNumDigitsAfterDecimal == -1)
3357 nNumDigitsAfterDecimal = LocaleDataWrapper::getNumDigits();
3359 bool bIncludeLeadingDigit = LocaleDataWrapper::isNumLeadingZero();
3360 if (nArgCount > 3 && !rPar.Get(3)->IsEmpty())
3362 switch (rPar.Get(3)->GetInteger())
3364 case ooo::vba::VbTriState::vbFalse:
3365 bIncludeLeadingDigit = false;
3366 break;
3367 case ooo::vba::VbTriState::vbTrue:
3368 bIncludeLeadingDigit = true;
3369 break;
3370 case ooo::vba::VbTriState::vbUseDefault:
3371 // do nothing;
3372 break;
3373 default:
3374 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3375 return;
3379 bool bUseParensForNegativeNumbers = false;
3380 if (nArgCount > 4 && !rPar.Get(4)->IsEmpty())
3382 switch (rPar.Get(4)->GetInteger())
3384 case ooo::vba::VbTriState::vbFalse:
3385 case ooo::vba::VbTriState::vbUseDefault:
3386 // do nothing
3387 break;
3388 case ooo::vba::VbTriState::vbTrue:
3389 bUseParensForNegativeNumbers = true;
3390 break;
3391 default:
3392 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3393 return;
3397 bool bGroupDigits = false;
3398 if (nArgCount > 5 && !rPar.Get(5)->IsEmpty())
3400 switch (rPar.Get(5)->GetInteger())
3402 case ooo::vba::VbTriState::vbFalse:
3403 case ooo::vba::VbTriState::vbUseDefault:
3404 // do nothing
3405 break;
3406 case ooo::vba::VbTriState::vbTrue:
3407 bGroupDigits = true;
3408 break;
3409 default:
3410 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
3411 return;
3415 double fVal = rPar.Get(1)->GetDouble();
3416 const bool bNegative = fVal < 0;
3417 if (bNegative)
3418 fVal = fabs(fVal); // Always work with non-negatives, to easily handle leading zero
3420 static const sal_Unicode decSep = localeData.getNumDecimalSep().toChar();
3421 OUString aResult = rtl::math::doubleToUString(
3422 fVal, rtl_math_StringFormat_F, nNumDigitsAfterDecimal, decSep,
3423 bGroupDigits ? localeData.getDigitGrouping().getConstArray() : nullptr,
3424 localeData.getNumThousandSep().toChar());
3426 if (!bIncludeLeadingDigit && aResult.getLength() > 1 && aResult.startsWith("0"))
3427 aResult = aResult.copy(1);
3429 if (nNumDigitsAfterDecimal > 0)
3431 sal_Int32 nActualDigits = nNumDigitsAfterDecimal;
3432 const sal_Int32 nSepPos = aResult.indexOf(decSep);
3433 if (nSepPos == -1)
3434 nActualDigits = 0;
3435 else
3436 nActualDigits = aResult.getLength() - nSepPos - 1;
3438 // VBA allows up to 255 digits; rtl::math::doubleToUString outputs up to 15 digits
3439 // for ~small numbers, so pad them as appropriate.
3440 if (nActualDigits < nNumDigitsAfterDecimal)
3442 OUStringBuffer sBuf;
3443 comphelper::string::padToLength(sBuf, nNumDigitsAfterDecimal - nActualDigits, '0');
3444 aResult += sBuf;
3448 if (bNegative)
3450 if (bUseParensForNegativeNumbers)
3451 aResult = "(" + aResult + ")";
3452 else
3453 aResult = "-" + aResult;
3456 rPar.Get(0)->PutString(aResult);
3459 namespace {
3461 // note: BASIC does not use comphelper::random, because
3462 // Randomize(int) must be supported and should not affect non-BASIC random use
3463 struct RandomNumberGenerator
3465 std::mt19937 global_rng;
3467 RandomNumberGenerator()
3471 std::random_device rd;
3472 // initialises the state of the global random number generator
3473 // should only be called once.
3474 // (note, a few std::variate_generator<> (like normal) have their
3475 // own state which would need a reset as well to guarantee identical
3476 // sequence of numbers, e.g. via myrand.distribution().reset())
3477 global_rng.seed(rd() ^ time(nullptr));
3479 catch (std::runtime_error& e)
3481 SAL_WARN("basic", "Using std::random_device failed: " << e.what());
3482 global_rng.seed(time(nullptr));
3487 class theRandomNumberGenerator : public rtl::Static<RandomNumberGenerator, theRandomNumberGenerator> {};
3491 void SbRtl_Randomize(StarBASIC *, SbxArray & rPar, bool)
3493 if ( rPar.Count() > 2 )
3495 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3497 if( rPar.Count() == 2 )
3499 int nSeed = static_cast<int>(rPar.Get(1)->GetInteger());
3500 theRandomNumberGenerator::get().global_rng.seed(nSeed);
3502 // without parameter, no need to do anything - RNG is seeded at first use
3505 void SbRtl_Rnd(StarBASIC *, SbxArray & rPar, bool)
3507 if ( rPar.Count() > 2 )
3509 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3511 else
3513 std::uniform_real_distribution<double> dist(0.0, 1.0);
3514 double const tmp(dist(theRandomNumberGenerator::get().global_rng));
3515 rPar.Get(0)->PutDouble(tmp);
3520 // Syntax: Shell("Path",[ Window-Style,[ "Params", [ bSync = sal_False ]]])
3521 // WindowStyles (VBA compatible):
3522 // 2 == Minimized
3523 // 3 == Maximized
3524 // 10 == Full-Screen (text mode applications OS/2, WIN95, WNT)
3525 // HACK: The WindowStyle will be passed to
3526 // Application::StartApp in Creator. Format: "xxxx2"
3529 void SbRtl_Shell(StarBASIC *, SbxArray & rPar, bool)
3531 std::size_t nArgCount = rPar.Count();
3532 if ( nArgCount < 2 || nArgCount > 5 )
3534 rPar.Get(0)->PutLong(0);
3535 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3537 else
3539 oslProcessOption nOptions = osl_Process_SEARCHPATH | osl_Process_DETACHED;
3541 OUString aCmdLine = rPar.Get(1)->GetOUString();
3542 // attach additional parameters - everything must be parsed anyway
3543 if( nArgCount >= 4 )
3545 OUString tmp = rPar.Get(3)->GetOUString().trim();
3546 if (!tmp.isEmpty())
3548 aCmdLine += " " + tmp;
3551 else if( aCmdLine.isEmpty() )
3553 // avoid special treatment (empty list)
3554 aCmdLine += " ";
3556 sal_Int32 nLen = aCmdLine.getLength();
3558 // #55735 if there are parameters, they have to be separated
3559 // #72471 also separate the single parameters
3560 std::vector<OUString> aTokenVector;
3561 OUString aToken;
3562 sal_Int32 i = 0;
3563 sal_Unicode c;
3564 while( i < nLen )
3566 for ( ;; ++i )
3568 c = aCmdLine[ i ];
3569 if ( c != ' ' && c != '\t' )
3571 break;
3575 if( c == '\"' || c == '\'' )
3577 sal_Int32 iFoundPos = aCmdLine.indexOf( c, i + 1 );
3579 if( iFoundPos < 0 )
3581 aToken = aCmdLine.copy( i);
3582 i = nLen;
3584 else
3586 aToken = aCmdLine.copy( i + 1, (iFoundPos - i - 1) );
3587 i = iFoundPos + 1;
3590 else
3592 sal_Int32 iFoundSpacePos = aCmdLine.indexOf( ' ', i );
3593 sal_Int32 iFoundTabPos = aCmdLine.indexOf( '\t', i );
3594 sal_Int32 iFoundPos = iFoundSpacePos >= 0 ? iFoundTabPos >= 0 ? std::min( iFoundSpacePos, iFoundTabPos ) : iFoundSpacePos : -1;
3596 if( iFoundPos < 0 )
3598 aToken = aCmdLine.copy( i );
3599 i = nLen;
3601 else
3603 aToken = aCmdLine.copy( i, (iFoundPos - i) );
3604 i = iFoundPos;
3608 // insert into the list
3609 aTokenVector.push_back( aToken );
3611 // #55735 / #72471 end
3613 sal_Int16 nWinStyle = 0;
3614 if( nArgCount >= 3 )
3616 nWinStyle = rPar.Get(2)->GetInteger();
3617 switch( nWinStyle )
3619 case 2:
3620 nOptions |= osl_Process_MINIMIZED;
3621 break;
3622 case 3:
3623 nOptions |= osl_Process_MAXIMIZED;
3624 break;
3625 case 10:
3626 nOptions |= osl_Process_FULLSCREEN;
3627 break;
3630 bool bSync = false;
3631 if( nArgCount >= 5 )
3633 bSync = rPar.Get(4)->GetBool();
3635 if( bSync )
3637 nOptions |= osl_Process_WAIT;
3641 // #72471 work parameter(s) up
3642 std::vector<OUString>::const_iterator iter = aTokenVector.begin();
3643 OUString aOUStrProgURL = getFullPath( *iter );
3645 ++iter;
3647 sal_uInt16 nParamCount = sal::static_int_cast< sal_uInt16 >(aTokenVector.size() - 1 );
3648 std::unique_ptr<rtl_uString*[]> pParamList;
3649 if( nParamCount )
3651 pParamList.reset( new rtl_uString*[nParamCount]);
3652 for(int iVector = 0; iter != aTokenVector.end(); ++iVector, ++iter)
3654 const OUString& rParamStr = *iter;
3655 pParamList[iVector] = nullptr;
3656 rtl_uString_assign(&(pParamList[iVector]), rParamStr.pData);
3660 oslProcess pApp;
3661 bool bSucc = osl_executeProcess(
3662 aOUStrProgURL.pData,
3663 pParamList.get(),
3664 nParamCount,
3665 nOptions,
3666 nullptr,
3667 nullptr,
3668 nullptr, 0,
3669 &pApp ) == osl_Process_E_None;
3671 // 53521 only free process handle on success
3672 if (bSucc)
3674 osl_freeProcessHandle( pApp );
3677 for(int j = 0; j < nParamCount; ++j)
3679 rtl_uString_release(pParamList[j]);
3682 if( !bSucc )
3684 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND );
3686 else
3688 rPar.Get(0)->PutLong( 0 );
3693 void SbRtl_VarType(StarBASIC *, SbxArray & rPar, bool)
3695 if ( rPar.Count() != 2 )
3697 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3699 else
3701 SbxDataType eType = rPar.Get(1)->GetType();
3702 rPar.Get(0)->PutInteger( static_cast<sal_Int16>(eType) );
3706 // Exported function
3707 OUString getBasicTypeName( SbxDataType eType )
3709 static const char* pTypeNames[] =
3711 "Empty", // SbxEMPTY
3712 "Null", // SbxNULL
3713 "Integer", // SbxINTEGER
3714 "Long", // SbxLONG
3715 "Single", // SbxSINGLE
3716 "Double", // SbxDOUBLE
3717 "Currency", // SbxCURRENCY
3718 "Date", // SbxDATE
3719 "String", // SbxSTRING
3720 "Object", // SbxOBJECT
3721 "Error", // SbxERROR
3722 "Boolean", // SbxBOOL
3723 "Variant", // SbxVARIANT
3724 "DataObject", // SbxDATAOBJECT
3725 "Unknown Type",
3726 "Unknown Type",
3727 "Char", // SbxCHAR
3728 "Byte", // SbxBYTE
3729 "UShort", // SbxUSHORT
3730 "ULong", // SbxULONG
3731 "Long64", // SbxLONG64
3732 "ULong64", // SbxULONG64
3733 "Int", // SbxINT
3734 "UInt", // SbxUINT
3735 "Void", // SbxVOID
3736 "HResult", // SbxHRESULT
3737 "Pointer", // SbxPOINTER
3738 "DimArray", // SbxDIMARRAY
3739 "CArray", // SbxCARRAY
3740 "Userdef", // SbxUSERDEF
3741 "Lpstr", // SbxLPSTR
3742 "Lpwstr", // SbxLPWSTR
3743 "Unknown Type", // SbxCoreSTRING
3744 "WString", // SbxWSTRING
3745 "WChar", // SbxWCHAR
3746 "Int64", // SbxSALINT64
3747 "UInt64", // SbxSALUINT64
3748 "Decimal", // SbxDECIMAL
3751 size_t nPos = static_cast<size_t>(eType) & 0x0FFF;
3752 const size_t nTypeNameCount = SAL_N_ELEMENTS( pTypeNames );
3753 if ( nPos >= nTypeNameCount )
3755 nPos = nTypeNameCount - 1;
3757 return OUString::createFromAscii(pTypeNames[nPos]);
3760 static OUString getObjectTypeName( SbxVariable* pVar )
3762 OUString sRet( "Object" );
3763 if ( pVar )
3765 SbxBase* pBaseObj = pVar->GetObject();
3766 if( !pBaseObj )
3768 sRet = "Nothing";
3770 else
3772 SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pVar );
3773 if ( !pUnoObj )
3775 pUnoObj = dynamic_cast<SbUnoObject*>( pBaseObj );
3777 if ( pUnoObj )
3779 Any aObj = pUnoObj->getUnoAny();
3780 // For upstreaming unless we start to build oovbaapi by default
3781 // we need to get detect the vba-ness of the object in some
3782 // other way
3783 // note: Automation objects do not support XServiceInfo
3784 uno::Reference< XServiceInfo > xServInfo( aObj, uno::UNO_QUERY );
3785 if ( xServInfo.is() )
3787 // is this a VBA object ?
3788 Sequence< OUString > sServices = xServInfo->getSupportedServiceNames();
3789 if ( sServices.hasElements() )
3791 sRet = sServices[ 0 ];
3794 else
3796 uno::Reference< bridge::oleautomation::XAutomationObject > xAutoMation( aObj, uno::UNO_QUERY );
3797 if ( xAutoMation.is() )
3799 uno::Reference< script::XInvocation > xInv( aObj, uno::UNO_QUERY );
3800 if ( xInv.is() )
3804 xInv->getValue( "$GetTypeName" ) >>= sRet;
3806 catch(const Exception& )
3812 sal_Int32 nDot = sRet.lastIndexOf( '.' );
3813 if ( nDot != -1 && nDot < sRet.getLength() )
3815 sRet = sRet.copy( nDot + 1 );
3820 return sRet;
3823 void SbRtl_TypeName(StarBASIC *, SbxArray & rPar, bool)
3825 if ( rPar.Count() != 2 )
3827 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3829 else
3831 SbxDataType eType = rPar.Get(1)->GetType();
3832 bool bIsArray = ( ( eType & SbxARRAY ) != 0 );
3834 OUString aRetStr;
3835 if ( SbiRuntime::isVBAEnabled() && eType == SbxOBJECT )
3837 aRetStr = getObjectTypeName( rPar.Get(1) );
3839 else
3841 aRetStr = getBasicTypeName( eType );
3843 if( bIsArray )
3845 aRetStr += "()";
3847 rPar.Get(0)->PutString( aRetStr );
3851 void SbRtl_Len(StarBASIC *, SbxArray & rPar, bool)
3853 if ( rPar.Count() != 2 )
3855 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3857 else
3859 const OUString& rStr = rPar.Get(1)->GetOUString();
3860 rPar.Get(0)->PutLong( rStr.getLength() );
3864 void SbRtl_DDEInitiate(StarBASIC *, SbxArray & rPar, bool)
3866 int nArgs = static_cast<int>(rPar.Count());
3867 if ( nArgs != 3 )
3869 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3870 return;
3872 const OUString& rApp = rPar.Get(1)->GetOUString();
3873 const OUString& rTopic = rPar.Get(2)->GetOUString();
3875 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3876 size_t nChannel;
3877 ErrCode nDdeErr = pDDE->Initiate( rApp, rTopic, nChannel );
3878 if( nDdeErr )
3880 StarBASIC::Error( nDdeErr );
3882 else
3884 rPar.Get(0)->PutInteger( static_cast<sal_Int16>(nChannel) );
3888 void SbRtl_DDETerminate(StarBASIC *, SbxArray & rPar, bool)
3890 rPar.Get(0)->PutEmpty();
3891 int nArgs = static_cast<int>(rPar.Count());
3892 if ( nArgs != 2 )
3894 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3895 return;
3897 size_t nChannel = rPar.Get(1)->GetInteger();
3898 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3899 ErrCode nDdeErr = pDDE->Terminate( nChannel );
3900 if( nDdeErr )
3902 StarBASIC::Error( nDdeErr );
3906 void SbRtl_DDETerminateAll(StarBASIC *, SbxArray & rPar, bool)
3908 rPar.Get(0)->PutEmpty();
3909 int nArgs = static_cast<int>(rPar.Count());
3910 if ( nArgs != 1 )
3912 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3913 return;
3916 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3917 ErrCode nDdeErr = pDDE->TerminateAll();
3918 if( nDdeErr )
3920 StarBASIC::Error( nDdeErr );
3924 void SbRtl_DDERequest(StarBASIC *, SbxArray & rPar, bool)
3926 int nArgs = static_cast<int>(rPar.Count());
3927 if ( nArgs != 3 )
3929 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3930 return;
3932 size_t nChannel = rPar.Get(1)->GetInteger();
3933 const OUString& rItem = rPar.Get(2)->GetOUString();
3934 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3935 OUString aResult;
3936 ErrCode nDdeErr = pDDE->Request( nChannel, rItem, aResult );
3937 if( nDdeErr )
3939 StarBASIC::Error( nDdeErr );
3941 else
3943 rPar.Get(0)->PutString( aResult );
3947 void SbRtl_DDEExecute(StarBASIC *, SbxArray & rPar, bool)
3949 rPar.Get(0)->PutEmpty();
3950 int nArgs = static_cast<int>(rPar.Count());
3951 if ( nArgs != 3 )
3953 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3954 return;
3956 size_t nChannel = rPar.Get(1)->GetInteger();
3957 const OUString& rCommand = rPar.Get(2)->GetOUString();
3958 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3959 ErrCode nDdeErr = pDDE->Execute( nChannel, rCommand );
3960 if( nDdeErr )
3962 StarBASIC::Error( nDdeErr );
3966 void SbRtl_DDEPoke(StarBASIC *, SbxArray & rPar, bool)
3968 rPar.Get(0)->PutEmpty();
3969 int nArgs = static_cast<int>(rPar.Count());
3970 if ( nArgs != 4 )
3972 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3973 return;
3975 size_t nChannel = rPar.Get(1)->GetInteger();
3976 const OUString& rItem = rPar.Get(2)->GetOUString();
3977 const OUString& rData = rPar.Get(3)->GetOUString();
3978 SbiDdeControl* pDDE = GetSbData()->pInst->GetDdeControl();
3979 ErrCode nDdeErr = pDDE->Poke( nChannel, rItem, rData );
3980 if( nDdeErr )
3982 StarBASIC::Error( nDdeErr );
3987 void SbRtl_FreeFile(StarBASIC *, SbxArray & rPar, bool)
3989 if ( rPar.Count() != 1 )
3991 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
3992 return;
3994 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
3995 short nChannel = 1;
3996 while( nChannel < CHANNELS )
3998 SbiStream* pStrm = pIO->GetStream( nChannel );
3999 if( !pStrm )
4001 rPar.Get(0)->PutInteger( nChannel );
4002 return;
4004 nChannel++;
4006 StarBASIC::Error( ERRCODE_BASIC_TOO_MANY_FILES );
4009 void SbRtl_LBound(StarBASIC *, SbxArray & rPar, bool)
4011 sal_uInt16 nParCount = rPar.Count();
4012 if ( nParCount != 3 && nParCount != 2 )
4014 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4015 return;
4017 SbxBase* pParObj = rPar.Get(1)->GetObject();
4018 SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
4019 if( pArr )
4021 sal_Int32 nLower, nUpper;
4022 short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
4023 if( !pArr->GetDim32( nDim, nLower, nUpper ) )
4024 StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
4025 else
4026 rPar.Get(0)->PutLong( nLower );
4028 else
4029 StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
4032 void SbRtl_UBound(StarBASIC *, SbxArray & rPar, bool)
4034 sal_uInt16 nParCount = rPar.Count();
4035 if ( nParCount != 3 && nParCount != 2 )
4037 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4038 return;
4041 SbxBase* pParObj = rPar.Get(1)->GetObject();
4042 SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
4043 if( pArr )
4045 sal_Int32 nLower, nUpper;
4046 short nDim = (nParCount == 3) ? static_cast<short>(rPar.Get(2)->GetInteger()) : 1;
4047 if( !pArr->GetDim32( nDim, nLower, nUpper ) )
4048 StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
4049 else
4050 rPar.Get(0)->PutLong( nUpper );
4052 else
4053 StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
4056 void SbRtl_RGB(StarBASIC *, SbxArray & rPar, bool)
4058 if ( rPar.Count() != 4 )
4060 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4061 return;
4064 sal_Int32 nRed = rPar.Get(1)->GetInteger() & 0xFF;
4065 sal_Int32 nGreen = rPar.Get(2)->GetInteger() & 0xFF;
4066 sal_Int32 nBlue = rPar.Get(3)->GetInteger() & 0xFF;
4067 sal_Int32 nRGB;
4069 SbiInstance* pInst = GetSbData()->pInst;
4070 bool bCompatibility = ( pInst && pInst->IsCompatibility() );
4071 if( bCompatibility )
4073 nRGB = (nBlue << 16) | (nGreen << 8) | nRed;
4075 else
4077 nRGB = (nRed << 16) | (nGreen << 8) | nBlue;
4079 rPar.Get(0)->PutLong( nRGB );
4082 void SbRtl_QBColor(StarBASIC *, SbxArray & rPar, bool)
4084 static const sal_Int32 pRGB[] =
4086 0x000000,
4087 0x800000,
4088 0x008000,
4089 0x808000,
4090 0x000080,
4091 0x800080,
4092 0x008080,
4093 0xC0C0C0,
4094 0x808080,
4095 0xFF0000,
4096 0x00FF00,
4097 0xFFFF00,
4098 0x0000FF,
4099 0xFF00FF,
4100 0x00FFFF,
4101 0xFFFFFF,
4104 if ( rPar.Count() != 2 )
4106 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4107 return;
4110 sal_Int16 nCol = rPar.Get(1)->GetInteger();
4111 if( nCol < 0 || nCol > 15 )
4113 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4114 return;
4116 sal_Int32 nRGB = pRGB[ nCol ];
4117 rPar.Get(0)->PutLong( nRGB );
4120 // StrConv(string, conversion, LCID)
4121 void SbRtl_StrConv(StarBASIC *, SbxArray & rPar, bool)
4123 std::size_t nArgCount = rPar.Count()-1;
4124 if( nArgCount < 2 || nArgCount > 3 )
4126 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4127 return;
4130 OUString aOldStr = rPar.Get(1)->GetOUString();
4131 sal_Int32 nConversion = rPar.Get(2)->GetLong();
4133 LanguageType nLanguage = LANGUAGE_SYSTEM;
4135 sal_Int32 nOldLen = aOldStr.getLength();
4136 if( nOldLen == 0 )
4138 // null string,return
4139 rPar.Get(0)->PutString(aOldStr);
4140 return;
4143 TransliterationFlags nType = TransliterationFlags::NONE;
4144 if ( (nConversion & 0x03) == 3 ) // vbProperCase
4146 const CharClass& rCharClass = GetCharClass();
4147 aOldStr = rCharClass.titlecase( aOldStr.toAsciiLowerCase(), 0, nOldLen );
4149 else if ( (nConversion & 0x01) == 1 ) // vbUpperCase
4151 nType |= TransliterationFlags::LOWERCASE_UPPERCASE;
4153 else if ( (nConversion & 0x02) == 2 ) // vbLowerCase
4155 nType |= TransliterationFlags::UPPERCASE_LOWERCASE;
4157 if ( (nConversion & 0x04) == 4 ) // vbWide
4159 nType |= TransliterationFlags::HALFWIDTH_FULLWIDTH;
4161 else if ( (nConversion & 0x08) == 8 ) // vbNarrow
4163 nType |= TransliterationFlags::FULLWIDTH_HALFWIDTH;
4165 if ( (nConversion & 0x10) == 16) // vbKatakana
4167 nType |= TransliterationFlags::HIRAGANA_KATAKANA;
4169 else if ( (nConversion & 0x20) == 32 ) // vbHiragana
4171 nType |= TransliterationFlags::KATAKANA_HIRAGANA;
4173 OUString aNewStr( aOldStr );
4174 if( nType != TransliterationFlags::NONE )
4176 uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext();
4177 ::utl::TransliterationWrapper aTransliterationWrapper( xContext, nType );
4178 uno::Sequence<sal_Int32> aOffsets;
4179 aTransliterationWrapper.loadModuleIfNeeded( nLanguage );
4180 aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets );
4183 if ( (nConversion & 0x40) == 64 ) // vbUnicode
4185 // convert the string to byte string, preserving unicode (2 bytes per character)
4186 sal_Int32 nSize = aNewStr.getLength()*2;
4187 const sal_Unicode* pSrc = aNewStr.getStr();
4188 std::unique_ptr<sal_Char[]> pChar(new sal_Char[nSize+1]);
4189 for( sal_Int32 i=0; i < nSize; i++ )
4191 pChar[i] = static_cast< sal_Char >( (i%2) ? ((*pSrc) >> 8) & 0xff : (*pSrc) & 0xff );
4192 if( i%2 )
4194 pSrc++;
4197 pChar[nSize] = '\0';
4198 OString aOStr(pChar.get());
4200 // there is no concept about default codepage in unix. so it is incorrectly in unix
4201 OUString aOUStr = OStringToOUString(aOStr, osl_getThreadTextEncoding());
4202 rPar.Get(0)->PutString( aOUStr );
4203 return;
4205 else if ( (nConversion & 0x80) == 128 ) // vbFromUnicode
4207 // there is no concept about default codepage in unix. so it is incorrectly in unix
4208 OString aOStr = OUStringToOString(aNewStr,osl_getThreadTextEncoding());
4209 const sal_Char* pChar = aOStr.getStr();
4210 sal_Int32 nArraySize = aOStr.getLength();
4211 SbxDimArray* pArray = new SbxDimArray(SbxBYTE);
4212 bool bIncIndex = (IsBaseIndexOne() && SbiRuntime::isVBAEnabled() );
4213 if(nArraySize)
4215 if( bIncIndex )
4217 pArray->AddDim( 1, nArraySize );
4219 else
4221 pArray->AddDim( 0, nArraySize-1 );
4224 else
4226 pArray->unoAddDim( 0, -1 );
4229 for( sal_Int32 i=0; i< nArraySize; i++)
4231 SbxVariable* pNew = new SbxVariable( SbxBYTE );
4232 pNew->PutByte(*pChar);
4233 pChar++;
4234 pNew->SetFlag( SbxFlagBits::Write );
4235 short aIdx[1];
4236 aIdx[0] = i;
4237 if( bIncIndex )
4239 ++aIdx[0];
4241 pArray->Put(pNew, aIdx);
4244 SbxVariableRef refVar = rPar.Get(0);
4245 SbxFlagBits nFlags = refVar->GetFlags();
4246 refVar->ResetFlag( SbxFlagBits::Fixed );
4247 refVar->PutObject( pArray );
4248 refVar->SetFlags( nFlags );
4249 refVar->SetParameters( nullptr );
4250 return;
4252 rPar.Get(0)->PutString(aNewStr);
4256 void SbRtl_Beep(StarBASIC *, SbxArray & rPar, bool)
4258 if ( rPar.Count() != 1 )
4260 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4261 return;
4263 Sound::Beep();
4266 void SbRtl_Load(StarBASIC *, SbxArray & rPar, bool)
4268 if( rPar.Count() != 2 )
4270 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4271 return;
4275 SbxBase* pObj = rPar.Get(1)->GetObject();
4276 if ( pObj )
4278 if (SbUserFormModule* pModule = dynamic_cast<SbUserFormModule*>(pObj))
4280 pModule->Load();
4282 else if (SbxObject* pSbxObj = dynamic_cast<SbxObject*>(pObj))
4284 SbxVariable* pVar = pSbxObj->Find("Load", SbxClassType::Method);
4285 if( pVar )
4287 pVar->GetInteger();
4293 void SbRtl_Unload(StarBASIC *, SbxArray & rPar, bool)
4295 rPar.Get(0)->PutEmpty();
4296 if( rPar.Count() != 2 )
4298 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4299 return;
4303 SbxBase* pObj = rPar.Get(1)->GetObject();
4304 if ( pObj )
4306 if (SbUserFormModule* pFormModule = dynamic_cast<SbUserFormModule*>(pObj))
4308 pFormModule->Unload();
4310 else if (SbxObject *pSbxObj = dynamic_cast<SbxObject*>(pObj))
4312 SbxVariable* pVar = pSbxObj->Find("Unload", SbxClassType::Method);
4313 if( pVar )
4315 pVar->GetInteger();
4321 void SbRtl_LoadPicture(StarBASIC *, SbxArray & rPar, bool)
4323 if( rPar.Count() != 2 )
4325 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4326 return;
4329 OUString aFileURL = getFullPath( rPar.Get(1)->GetOUString() );
4330 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aFileURL, StreamMode::READ ));
4331 if( pStream )
4333 Bitmap aBmp;
4334 ReadDIB(aBmp, *pStream, true);
4335 Graphic aGraphic(aBmp);
4337 SbxObjectRef xRef = new SbStdPicture;
4338 static_cast<SbStdPicture*>(xRef.get())->SetGraphic( aGraphic );
4339 rPar.Get(0)->PutObject( xRef.get() );
4343 void SbRtl_SavePicture(StarBASIC *, SbxArray & rPar, bool)
4345 rPar.Get(0)->PutEmpty();
4346 if( rPar.Count() != 3 )
4348 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4349 return;
4352 SbxBase* pObj = rPar.Get(1)->GetObject();
4353 if (SbStdPicture *pPicture = dynamic_cast<SbStdPicture*>(pObj))
4355 SvFileStream aOStream( rPar.Get(2)->GetOUString(), StreamMode::WRITE | StreamMode::TRUNC );
4356 const Graphic& aGraphic = pPicture->GetGraphic();
4357 WriteGraphic( aOStream, aGraphic );
4361 void SbRtl_MsgBox(StarBASIC *, SbxArray & rPar, bool)
4363 sal_uInt16 nArgCount = rPar.Count();
4364 if( nArgCount < 2 || nArgCount > 6 )
4366 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4367 return;
4369 WinBits nType = 0; // MB_OK
4370 if( nArgCount >= 3 )
4371 nType = static_cast<WinBits>(rPar.Get(2)->GetInteger());
4372 WinBits nStyle = nType;
4373 nStyle &= 15; // delete bits 4-16
4374 if (nStyle > 5)
4375 nStyle = 0;
4377 enum BasicResponse
4379 Ok = 1,
4380 Cancel = 2,
4381 Abort = 3,
4382 Retry = 4,
4383 Ignore = 5,
4384 Yes = 6,
4385 No = 7
4388 OUString aMsg = rPar.Get(1)->GetOUString();
4389 OUString aTitle;
4390 if( nArgCount >= 4 )
4392 aTitle = rPar.Get(3)->GetOUString();
4394 else
4396 aTitle = Application::GetDisplayName();
4399 WinBits nDialogType = nType & (16+32+64);
4401 SolarMutexGuard aSolarGuard;
4402 vcl::Window* pParentWin = Application::GetDefDialogParent();
4403 weld::Widget* pParent = pParentWin ? pParentWin->GetFrameWeld() : nullptr;
4405 VclMessageType eType = VclMessageType::Other;
4407 switch (nDialogType)
4409 case 16:
4410 eType = VclMessageType::Error;
4411 break;
4412 case 32:
4413 eType = VclMessageType::Question;
4414 break;
4415 case 48:
4416 eType = VclMessageType::Warning;
4417 break;
4418 case 64:
4419 eType = VclMessageType::Info;
4420 break;
4423 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
4424 eType, VclButtonsType::NONE, aMsg));
4426 switch (nStyle)
4428 case 0: // MB_OK
4429 default:
4430 xBox->add_button(GetStandardText(StandardButtonType::OK), BasicResponse::Ok);
4431 break;
4432 case 1: // MB_OKCANCEL
4433 xBox->add_button(GetStandardText(StandardButtonType::OK), BasicResponse::Ok);
4434 xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4436 if (nType & 256 || nType & 512)
4437 xBox->set_default_response(BasicResponse::Cancel);
4438 else
4439 xBox->set_default_response(BasicResponse::Ok);
4441 break;
4442 case 2: // MB_ABORTRETRYIGNORE
4443 xBox->add_button(GetStandardText(StandardButtonType::Abort), BasicResponse::Abort);
4444 xBox->add_button(GetStandardText(StandardButtonType::Retry), BasicResponse::Retry);
4445 xBox->add_button(GetStandardText(StandardButtonType::Ignore), BasicResponse::Ignore);
4447 if (nType & 256)
4448 xBox->set_default_response(BasicResponse::Retry);
4449 else if (nType & 512)
4450 xBox->set_default_response(BasicResponse::Ignore);
4451 else
4452 xBox->set_default_response(BasicResponse::Cancel);
4454 break;
4455 case 3: // MB_YESNOCANCEL
4456 xBox->add_button(GetStandardText(StandardButtonType::Yes), BasicResponse::Yes);
4457 xBox->add_button(GetStandardText(StandardButtonType::No), BasicResponse::No);
4458 xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4460 if (nType & 256 || nType & 512)
4461 xBox->set_default_response(BasicResponse::Cancel);
4462 else
4463 xBox->set_default_response(BasicResponse::Yes);
4465 break;
4466 case 4: // MB_YESNO
4467 xBox->add_button(GetStandardText(StandardButtonType::Yes), BasicResponse::Yes);
4468 xBox->add_button(GetStandardText(StandardButtonType::No), BasicResponse::No);
4470 if (nType & 256 || nType & 512)
4471 xBox->set_default_response(BasicResponse::No);
4472 else
4473 xBox->set_default_response(BasicResponse::Yes);
4475 break;
4476 case 5: // MB_RETRYCANCEL
4477 xBox->add_button(GetStandardText(StandardButtonType::Retry), BasicResponse::Retry);
4478 xBox->add_button(GetStandardText(StandardButtonType::Cancel), BasicResponse::Cancel);
4480 if (nType & 256 || nType & 512)
4481 xBox->set_default_response(BasicResponse::Cancel);
4482 else
4483 xBox->set_default_response(BasicResponse::Retry);
4485 break;
4488 xBox->set_title(aTitle);
4489 sal_Int16 nRet = xBox->run();
4490 rPar.Get(0)->PutInteger(nRet);
4493 void SbRtl_SetAttr(StarBASIC *, SbxArray & rPar, bool)
4495 rPar.Get(0)->PutEmpty();
4496 if ( rPar.Count() == 3 )
4498 OUString aStr = rPar.Get(1)->GetOUString();
4499 SbAttributes nFlags = static_cast<SbAttributes>( rPar.Get(2)->GetInteger() );
4501 if( hasUno() )
4503 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
4504 if( xSFI.is() )
4508 bool bReadOnly = bool(nFlags & SbAttributes::READONLY);
4509 xSFI->setReadOnly( aStr, bReadOnly );
4510 bool bHidden = bool(nFlags & SbAttributes::HIDDEN);
4511 xSFI->setHidden( aStr, bHidden );
4513 catch(const Exception & )
4515 StarBASIC::Error( ERRCODE_IO_GENERAL );
4520 else
4522 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4526 void SbRtl_Reset(StarBASIC *, SbxArray &, bool)
4528 SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
4529 if (pIO)
4531 pIO->CloseAll();
4535 void SbRtl_DumpAllObjects(StarBASIC * pBasic, SbxArray & rPar, bool)
4537 sal_uInt16 nArgCount = rPar.Count();
4538 if( nArgCount < 2 || nArgCount > 3 )
4540 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4542 else if( !pBasic )
4544 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
4546 else
4548 SbxObject* p = pBasic;
4549 while( p->GetParent() )
4551 p = p->GetParent();
4553 SvFileStream aStrm( rPar.Get( 1 )->GetOUString(),
4554 StreamMode::WRITE | StreamMode::TRUNC );
4555 p->Dump( aStrm, rPar.Get( 2 )->GetBool() );
4556 aStrm.Close();
4557 if( aStrm.GetError() != ERRCODE_NONE )
4559 StarBASIC::Error( ERRCODE_BASIC_IO_ERROR );
4565 void SbRtl_FileExists(StarBASIC *, SbxArray & rPar, bool)
4567 if ( rPar.Count() == 2 )
4569 OUString aStr = rPar.Get(1)->GetOUString();
4570 bool bExists = false;
4572 if( hasUno() )
4574 const uno::Reference< ucb::XSimpleFileAccess3 >& xSFI = getFileAccess();
4575 if( xSFI.is() )
4579 bExists = xSFI->exists( aStr );
4581 catch(const Exception & )
4583 StarBASIC::Error( ERRCODE_IO_GENERAL );
4587 else
4589 DirectoryItem aItem;
4590 FileBase::RC nRet = DirectoryItem::get( getFullPath( aStr ), aItem );
4591 bExists = (nRet == FileBase::E_None);
4593 rPar.Get(0)->PutBool( bExists );
4595 else
4597 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4601 void SbRtl_Partition(StarBASIC *, SbxArray & rPar, bool)
4603 if ( rPar.Count() != 5 )
4605 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4606 return;
4609 sal_Int32 nNumber = rPar.Get(1)->GetLong();
4610 sal_Int32 nStart = rPar.Get(2)->GetLong();
4611 sal_Int32 nStop = rPar.Get(3)->GetLong();
4612 sal_Int32 nInterval = rPar.Get(4)->GetLong();
4614 if( nStart < 0 || nStop <= nStart || nInterval < 1 )
4616 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4617 return;
4620 // the Partition function inserts leading spaces before lowervalue and uppervalue
4621 // so that they both have the same number of characters as the string
4622 // representation of the value (Stop + 1). This ensures that if you use the output
4623 // of the Partition function with several values of Number, the resulting text
4624 // will be handled properly during any subsequent sort operation.
4626 // calculate the maximum number of characters before lowervalue and uppervalue
4627 OUString aBeforeStart = OUString::number( nStart - 1 );
4628 OUString aAfterStop = OUString::number( nStop + 1 );
4629 sal_Int32 nLen1 = aBeforeStart.getLength();
4630 sal_Int32 nLen2 = aAfterStop.getLength();
4631 sal_Int32 nLen = nLen1 >= nLen2 ? nLen1:nLen2;
4633 OUStringBuffer aRetStr( nLen * 2 + 1);
4634 OUString aLowerValue;
4635 OUString aUpperValue;
4636 if( nNumber < nStart )
4638 aUpperValue = aBeforeStart;
4640 else if( nNumber > nStop )
4642 aLowerValue = aAfterStop;
4644 else
4646 sal_Int32 nLowerValue = nNumber;
4647 sal_Int32 nUpperValue = nLowerValue;
4648 if( nInterval > 1 )
4650 nLowerValue = ((( nNumber - nStart ) / nInterval ) * nInterval ) + nStart;
4651 nUpperValue = nLowerValue + nInterval - 1;
4653 aLowerValue = OUString::number( nLowerValue );
4654 aUpperValue = OUString::number( nUpperValue );
4657 nLen1 = aLowerValue.getLength();
4658 nLen2 = aUpperValue.getLength();
4660 if( nLen > nLen1 )
4662 // appending the leading spaces for the lowervalue
4663 for ( sal_Int32 i= nLen - nLen1; i > 0; --i )
4665 aRetStr.append(" ");
4668 aRetStr.append( aLowerValue ).append(":");
4669 if( nLen > nLen2 )
4671 // appending the leading spaces for the uppervalue
4672 for ( sal_Int32 i= nLen - nLen2; i > 0; --i )
4674 aRetStr.append(" ");
4677 aRetStr.append( aUpperValue );
4678 rPar.Get(0)->PutString( aRetStr.makeStringAndClear());
4681 #endif
4683 static long GetDayDiff( const Date& rDate )
4685 Date aRefDate( 1,1,1900 );
4686 long nDiffDays;
4687 if ( aRefDate > rDate )
4689 nDiffDays = aRefDate - rDate;
4690 nDiffDays *= -1;
4692 else
4694 nDiffDays = rDate - aRefDate;
4696 nDiffDays += 2; // adjustment VisualBasic: 1.Jan.1900 == 2
4697 return nDiffDays;
4700 sal_Int16 implGetDateYear( double aDate )
4702 Date aRefDate( 1,1,1900 );
4703 long nDays = static_cast<long>(aDate);
4704 nDays -= 2; // standardize: 1.1.1900 => 0.0
4705 aRefDate.AddDays( nDays );
4706 sal_Int16 nRet = aRefDate.GetYear();
4707 return nRet;
4710 bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
4711 bool bUseTwoDigitYear, SbDateCorrection eCorr, double& rdRet )
4713 // XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and
4714 // 30..99 can not be input as they are 2-digit for 2000..2029 and
4715 // 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always
4716 // true). For VBA years > 9999 are invalid.
4717 // For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99
4718 // can not be input as they are 2-digit for 1900..1999, years<0 are
4719 // accepted. If bUseTwoDigitYear==false then all years are accepted, but
4720 // year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01).
4721 #if HAVE_FEATURE_SCRIPTING
4722 if ( (nYear < 0 || 9999 < nYear) && SbiRuntime::isVBAEnabled() )
4724 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4725 return false;
4727 else if ( nYear < 30 && SbiRuntime::isVBAEnabled() )
4729 nYear += 2000;
4731 else
4732 #endif
4734 if ( 0 <= nYear && nYear < 100 && (bUseTwoDigitYear
4735 #if HAVE_FEATURE_SCRIPTING
4736 || SbiRuntime::isVBAEnabled()
4737 #endif
4740 nYear += 1900;
4744 sal_Int32 nAddMonths = 0;
4745 sal_Int32 nAddDays = 0;
4746 // Always sanitize values to set date and to use for validity detection.
4747 if (nMonth < 1 || 12 < nMonth)
4749 sal_Int16 nM = ((nMonth < 1) ? (12 + (nMonth % 12)) : (nMonth % 12));
4750 nAddMonths = nMonth - nM;
4751 nMonth = nM;
4753 // Day 0 would already be normalized during Date::Normalize(), include
4754 // it in negative days, also to detect non-validity. The actual day of
4755 // month is 1+(nDay-1)
4756 if (nDay < 1)
4758 nAddDays = nDay - 1;
4759 nDay = 1;
4761 else if (nDay > 31)
4763 nAddDays = nDay - 31;
4764 nDay = 31;
4767 Date aCurDate( nDay, nMonth, nYear );
4769 /* TODO: we could enable the same rollover mechanism for StarBASIC to be
4770 * compatible with VBA (just with our wider supported date range), then
4771 * documentation would need to be adapted. As is, the DateSerial() runtime
4772 * function works as dumb as documented... (except that the resulting date
4773 * is checked for validity now and not just day<=31 and month<=12).
4774 * If change wanted then simply remove overriding RollOver here and adapt
4775 * documentation.*/
4776 #if HAVE_FEATURE_SCRIPTING
4777 if (eCorr == SbDateCorrection::RollOver && !SbiRuntime::isVBAEnabled())
4778 eCorr = SbDateCorrection::None;
4779 #endif
4781 if (nYear == 0 || (eCorr == SbDateCorrection::None && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
4783 #if HAVE_FEATURE_SCRIPTING
4784 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
4785 #endif
4786 return false;
4789 if (eCorr != SbDateCorrection::None)
4791 aCurDate.Normalize();
4792 if (nAddMonths)
4793 aCurDate.AddMonths( nAddMonths);
4794 if (nAddDays)
4795 aCurDate.AddDays( nAddDays);
4796 if (eCorr == SbDateCorrection::TruncateToMonth && aCurDate.GetMonth() != nMonth)
4798 if (aCurDate.GetYear() == SAL_MAX_INT16 && nMonth == 12)
4800 // Roll over and back not possible, hard max.
4801 aCurDate.SetMonth(12);
4802 aCurDate.SetDay(31);
4804 else
4806 aCurDate.SetMonth(nMonth);
4807 aCurDate.SetDay(1);
4808 aCurDate.AddMonths(1);
4809 aCurDate.AddDays(-1);
4814 long nDiffDays = GetDayDiff( aCurDate );
4815 rdRet = static_cast<double>(nDiffDays);
4816 return true;
4819 double implTimeSerial( sal_Int16 nHours, sal_Int16 nMinutes, sal_Int16 nSeconds )
4821 return
4822 static_cast<double>( nHours * ::tools::Time::secondPerHour +
4823 nMinutes * ::tools::Time::secondPerMinute +
4824 nSeconds)
4826 static_cast<double>( ::tools::Time::secondPerDay );
4829 bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
4830 sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond,
4831 double& rdRet )
4833 double dDate;
4834 if(!implDateSerial(nYear, nMonth, nDay, false/*bUseTwoDigitYear*/, SbDateCorrection::None, dDate))
4835 return false;
4836 rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond);
4837 return true;
4840 sal_Int16 implGetMinute( double dDate )
4842 double nFrac = dDate - floor( dDate );
4843 nFrac *= 86400.0;
4844 sal_Int32 nSeconds = static_cast<sal_Int32>(nFrac + 0.5);
4845 sal_Int16 nTemp = static_cast<sal_Int16>(nSeconds % 3600);
4846 sal_Int16 nMin = nTemp / 60;
4847 return nMin;
4850 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */