1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
65 #include <o3tl/char16_t2wchar_t.hxx>
67 using namespace comphelper
;
69 using namespace com::sun::star
;
70 using namespace com::sun::star::lang
;
71 using namespace com::sun::star::uno
;
74 #include <sbstdobj.hxx>
75 #include <rtlproto.hxx>
78 #include "ddectrl.hxx"
79 #include <sbintern.hxx>
80 #include <basic/vbahelper.hxx>
88 #include <sbobjmod.hxx>
100 #if HAVE_FEATURE_SCRIPTING
102 static void FilterWhiteSpace( OUString
& rStr
)
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'))
120 rStr
= aRet
.makeStringAndClear();
123 static long GetDayDiff( const Date
& rDate
);
125 static const CharClass
& GetCharClass()
127 static CharClass
aCharClass( Application::GetSettings().GetLanguageTag() );
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
)
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
);
157 // TODO: -> SbiGlobals
158 static uno::Reference
< ucb::XSimpleFileAccess3
> const & getFileAccess()
160 static uno::Reference
< ucb::XSimpleFileAccess3
> xSFI
= ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
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
);
175 StarBASIC::Error( ERRCODE_BASIC_CANNOT_LOAD
);
178 // Convenience: enter BASIC as parent
179 p
->SetParent( pBasic
);
180 rPar
.Get( 0 )->PutObject( p
.get() );
186 void SbRtl_Error(StarBASIC
* pBasic
, SbxArray
& rPar
, bool)
189 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
193 ErrCode nErr
= ERRCODE_NONE
;
195 if( rPar
.Count() == 1 )
197 nErr
= StarBASIC::GetErrBasic();
198 aErrorMsg
= StarBASIC::GetErrorMsg();
202 nCode
= rPar
.Get( 1 )->GetLong();
205 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
209 nErr
= StarBASIC::GetSfxFromVBError( static_cast<sal_uInt16
>(nCode
) );
213 bool bVBA
= SbiRuntime::isVBAEnabled();
215 if( bVBA
&& !aErrorMsg
.isEmpty())
217 tmpErrMsg
= aErrorMsg
;
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
);
241 void SbRtl_Sin(StarBASIC
*, SbxArray
& rPar
, bool)
243 if ( rPar
.Count() < 2 )
244 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
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
);
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
);
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
);
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
);
299 SbxVariableRef pArg
= rPar
.Get( 1 );
300 OUString
aStr( pArg
->GetOUString() );
303 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
304 rPar
.Get(0)->PutEmpty();
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
);
322 SbxVariableRef pArg
= rPar
.Get( 1 );
325 if( !bChrW
&& SbiRuntime::isVBAEnabled() )
327 sal_Char c
= static_cast<sal_Char
>(pArg
->GetByte());
328 aStr
= OUString(&c
, 1, osl_getThreadTextEncoding());
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
);
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*/ );
360 extern "C" void invalidParameterHandler(
361 wchar_t const * expression
, wchar_t const * function
, wchar_t const * file
, unsigned int line
,
366 "invalid parameter during _wgetdcwd; \"" << (expression
? o3tl::toU(expression
) : u
"???")
367 << "\" (" << (function
? o3tl::toU(function
) : u
"???") << ") at "
368 << (file
? o3tl::toU(file
) : u
"???") << ":" << line
);
375 void SbRtl_CurDir(StarBASIC
* pBasic
, SbxArray
& rPar
, bool 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.
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
);
395 auto c
= rtl::toAsciiUpperCase(aDrive
[0]);
396 if ( !rtl::isAsciiUpperCase( c
) )
398 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
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"
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
);
412 rPar
.Get(0)->PutString( o3tl::toU(pBuffer
) );
416 StarBASIC::Error( ERRCODE_BASIC_NO_DEVICE
);
421 const int PATH_INCR
= 250;
423 int nSize
= PATH_INCR
;
424 std::unique_ptr
<char[]> pMem
;
427 pMem
.reset(new char[nSize
]);
430 StarBASIC::Error( ERRCODE_BASIC_NO_MEMORY
);
433 if( getcwd( pMem
.get(), nSize
-1 ) != nullptr )
435 rPar
.Get(0)->PutString( OUString::createFromAscii(pMem
.get()) );
438 if( errno
!= ERANGE
)
440 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
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() );
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();
484 OUString aSourceFullPath
= getFullPath( aSource
);
485 if( !xSFI
->exists( aSourceFullPath
) )
487 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
491 OUString aDestFullPath
= getFullPath( aDest
);
492 if( xSFI
->exists( aDestFullPath
) )
494 StarBASIC::Error( ERRCODE_BASIC_FILE_EXISTS
);
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();
527 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
532 xSFI
->copy( getFullPath( aSource
), getFullPath( aDest
) );
534 catch(const Exception
& )
536 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
542 FileBase::RC nRet
= File::copy( getFullPath( aSource
), getFullPath( aDest
) );
543 if( nRet
!= FileBase::E_None
)
545 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
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();
562 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
565 OUString aFullPath
= getFullPath( aFileSpec
);
566 if( !xSFI
->exists( aFullPath
) || xSFI
->isFolder( aFullPath
) )
568 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
573 xSFI
->kill( aFullPath
);
575 catch(const Exception
& )
577 StarBASIC::Error( ERRCODE_IO_GENERAL
);
583 File::remove( getFullPath( aFileSpec
) );
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
) ;
623 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
628 xSFI
->createFolder( getFullPath( aPath
) );
630 catch(const Exception
& )
632 StarBASIC::Error( ERRCODE_IO_GENERAL
);
638 Directory::create( getFullPath( aPath
) );
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
)
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
);
667 Directory
aDir( aDirPath
);
669 if( nRet
!= FileBase::E_None
)
671 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
677 DirectoryItem aItem2
;
678 nRet
= aDir
.getNextItem( aItem2
);
679 if( nRet
!= FileBase::E_None
)
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");
691 OUString aPath
= aFileStatus2
.getFileURL();
694 FileStatus::Type aType2
= aFileStatus2
.getFileType();
695 bool bFolder2
= isFolder( aType2
);
698 implRemoveDirRecursive( aPath
);
702 File::remove( aPath
);
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();
719 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
724 if( !xSFI
->isFolder( aPath
) )
726 StarBASIC::Error( ERRCODE_BASIC_PATH_NOT_FOUND
);
729 SbiInstance
* pInst
= GetSbData()->pInst
;
730 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
733 Sequence
< OUString
> aContent
= xSFI
->getFolderContents( aPath
, true );
734 if( aContent
.hasElements() )
736 StarBASIC::Error( ERRCODE_BASIC_ACCESS_ERROR
);
741 xSFI
->kill( getFullPath( aPath
) );
743 catch(const Exception
& )
745 StarBASIC::Error( ERRCODE_IO_GENERAL
);
751 implRemoveDirRecursive( getFullPath( aPath
) );
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
);
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
);
787 SbxVariableRef pArg
= rPar
.Get( 1 );
788 OUString
aStr( pArg
->GetOUString() );
792 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
797 nLen
= xSFI
->getSize( getFullPath( aStr
) );
799 catch(const Exception
& )
801 StarBASIC::Error( ERRCODE_IO_GENERAL
);
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
);
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();
845 SbxVariableRef pVar
= new SbxVariable(SbxVARIANT
);
846 *rPar
.Get(0) = *pVar
;
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;
861 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
864 sal_Int32 nStartPos
= 1;
865 sal_Int32 nFirstStringPos
= 1;
867 if ( nArgCount
>= 3 )
869 nStartPos
= rPar
.Get(1)->GetLong();
872 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
878 SbiInstance
* pInst
= GetSbData()->pInst
;
880 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
883 SbiRuntime
* pRT
= pInst
->pRun
;
884 bTextMode
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
890 if ( nArgCount
== 4 )
892 bTextMode
= rPar
.Get(4)->GetInteger();
895 const OUString
& rToken
= rPar
.Get(nFirstStringPos
+1)->GetOUString();
897 // #97545 Always find empty string
898 if( rToken
.isEmpty() )
906 const OUString
& rStr1
= rPar
.Get(nFirstStringPos
)->GetOUString();
907 nPos
= rStr1
.indexOf( rToken
, nStartPos
- 1 ) + 1;
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;
932 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
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
);
950 SbiInstance
* pInst
= GetSbData()->pInst
;
952 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
955 SbiRuntime
* pRT
= pInst
->pRun
;
956 bTextMode
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
962 if ( nArgCount
== 4 )
964 bTextMode
= rPar
.Get(4)->GetInteger();
966 sal_Int32 nStrLen
= aStr1
.getLength();
967 if( nStartPos
== -1 )
973 if( nStartPos
<= nStrLen
)
975 sal_Int32 nTokenLen
= aToken
.getLength();
978 // Always find empty string
981 else if( nStrLen
> 0 )
985 nPos
= aStr1
.lastIndexOf( aToken
, nStartPos
) + 1;
989 aStr1
= aStr1
.toAsciiUpperCase();
990 aToken
= aToken
.toAsciiUpperCase();
992 nPos
= aStr1
.lastIndexOf( aToken
, nStartPos
) + 1;
996 rPar
.Get(0)->PutLong( nPos
);
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
);
1014 SbxVariableRef pArg
= rPar
.Get( 1 );
1015 double aDouble
= pArg
->GetDouble();
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
);
1032 SbxVariableRef pArg
= rPar
.Get( 1 );
1033 double aDouble
= pArg
->GetDouble();
1034 if ( aDouble
>= 0.0 )
1035 aDouble
= floor( aDouble
);
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
);
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
);
1066 OUString
aStr( rPar
.Get(1)->GetOUString() );
1067 sal_Int32 nResultLen
= rPar
.Get(2)->GetLong();
1068 if( 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
);
1090 double aArg
= rPar
.Get(1)->GetDouble();
1093 double d
= log( aArg
);
1094 checkArithmeticOverflow( d
);
1095 rPar
.Get( 0 )->PutDouble( d
);
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
);
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
);
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 )
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
);
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;
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
);
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
;
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
);
1192 nStartPos
, aReplaceStr
.getStr(), std::min(nReplaceLen
, nReplaceStrLen
));
1194 rPar
.Get(1)->PutString( aResultStr
.makeStringAndClear() );
1198 OUString aResultStr
;
1199 if (nStartPos
> aArgStr
.getLength())
1203 else if(nArgCount
== 2)
1205 aResultStr
= aArgStr
.copy( nStartPos
);
1211 if(nStartPos
+ nLen
> aArgStr
.getLength())
1213 nLen
= aArgStr
.getLength() - nStartPos
;
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
);
1233 SbxVariableRef pArg
= rPar
.Get( 1 );
1234 if ( pArg
->IsInteger() )
1236 snprintf( aBuffer
, sizeof(aBuffer
), "%o", pArg
->GetInteger() );
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
);
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();
1270 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1275 sal_Int32 lCount
= -1;
1278 if( rPar
.Get(5)->GetType() != SbxEMPTY
)
1280 lCount
= rPar
.Get(5)->GetLong();
1284 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1289 SbiInstance
* pInst
= GetSbData()->pInst
;
1291 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
1292 if( bCompatibility
)
1294 SbiRuntime
* pRT
= pInst
->pRun
;
1295 bTextMode
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
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
);
1318 aSrcStr
= aSrcStr
.toAsciiUpperCase();
1319 aFindStr
= aFindStr
.toAsciiUpperCase();
1321 nPos
= aSrcStr
.indexOf( aFindStr
, nPos
);
1324 aExpStr
= aExpStr
.replaceAt( nPos
, nFindStrLen
, aReplaceStr
);
1325 nPos
= nPos
+ nReplaceStrLen
;
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
);
1346 const OUString
& rStr
= rPar
.Get(1)->GetOUString();
1347 int nResultLen
= rPar
.Get(2)->GetLong();
1348 if( 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
);
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
);
1389 double aDouble
= rPar
.Get(1)->GetDouble();
1390 sal_Int16 nResult
= 0;
1395 else if ( aDouble
< 0 )
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
);
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
);
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
);
1439 double aDouble
= rPar
.Get(1)->GetDouble();
1442 rPar
.Get(0)->PutDouble( sqrt( aDouble
));
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
);
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;
1487 if( pBuf
[0] != ' ' )
1492 sal_Int32 iNext
= iZeroSearch
+ 1;
1493 if( pBuf
[iZeroSearch
] == '0' && nLen
> iNext
&& pBuf
[iNext
] == '.' )
1497 aStrNew
+= aStr
.copy(iZeroSearch
);
1501 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();
1520 const OUString
& rStr1
= rPar
.Get(1)->GetOUString();
1521 const OUString
& rStr2
= rPar
.Get(2)->GetOUString();
1523 SbiInstance
* pInst
= GetSbData()->pInst
;
1525 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
1526 if( bCompatibility
)
1528 SbiRuntime
* pRT
= pInst
->pRun
;
1529 bTextCompare
= pRT
&& pRT
->IsImageFlag( SbiImageFlags::COMPARETEXT
);
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;
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
);
1564 aResult
= rStr1
.compareTo( rStr2
);
1569 else if ( aResult
> 0)
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
);
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());
1597 const OUString
& rStr
= rPar
.Get(2)->GetOUString();
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
);
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
);
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
);
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
)
1652 if ( rPar
.Count() < 2 )
1654 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
1658 double nResult
= 0.0;
1661 OUString
aStr( rPar
.Get(1)->GetOUString() );
1663 FilterWhiteSpace( aStr
);
1664 if ( aStr
.getLength() > 1 && aStr
[0] == '&' )
1667 char aChar
= static_cast<char>(aStr
[1]);
1668 if ( aChar
== 'h' || aChar
== 'H' )
1672 else if ( aChar
== 'o' || aChar
== 'O' )
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
);
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. */
1696 else if ( nParseEnd
!= aStr
.getLength() )
1697 StarBASIC::Error( ERRCODE_BASIC_CONVERSION
);
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() );
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() );
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
);
1740 void SbxDateFromUNODate( SbxValue
*pVal
, const css::util::Date
& aUnoDate
)
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
);
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
);
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
);
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;
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
);
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
);
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
);
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;
1841 void SbxDateFromUNODateTime( SbxValue
*pVal
, const css::util::DateTime
& aUnoDT
)
1844 if( implDateTimeSerial( aUnoDT
.Year
, aUnoDT
.Month
, aUnoDT
.Day
,
1845 aUnoDT
.Hours
, aUnoDT
.Minutes
, aUnoDT
.Seconds
,
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
);
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
);
1873 Any
aAny (sbxToUnoValue(rPar
.Get(1), cppu::UnoType
<css::util::DateTime
>::get()));
1874 css::util::DateTime aUnoDT
;
1876 SbxDateFromUNODateTime(rPar
.Get(0), aUnoDT
);
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
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
);
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();
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;
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)
1933 // YYYYMMDD YYYYYMMDD YYMMDD
1934 // YYYY-MM-DD YYYYY-MM-DD
1935 if (nLen
!= 6 && (nLen
< 8 || 11 < nLen
))
1938 bool bUseTwoDigitYear
= false;
1939 OUString aYearStr
, aMonthStr
, aDayStr
;
1940 if (nLen
== 6 || nLen
== 8 || nLen
== 9)
1943 if (!comphelper::string::isdigitAsciiString(aStr
))
1946 const sal_Int32 nMonthPos
= (nLen
== 8 ? 4 : (nLen
== 6 ? 2 : 5));
1948 bUseTwoDigitYear
= true;
1949 aYearStr
= aStr
.copy( 0, nMonthPos
);
1950 aMonthStr
= aStr
.copy( nMonthPos
, 2 );
1951 aDayStr
= aStr
.copy( nMonthPos
+ 2, 2 );
1956 const sal_Int32 nMonthSep
= (nLen
== 11 ? 5 : 4);
1957 if (aStr
.indexOf('-') != nMonthSep
)
1959 if (aStr
.indexOf('-', nMonthSep
+ 1) != nMonthSep
+ 3)
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
))
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
))
1977 rPar
.Get(0)->PutDate( dDate
);
1983 SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER
);
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
);
1998 sal_Int16 nYear
= rPar
.Get(1)->GetInteger();
1999 sal_Int16 nMonth
= rPar
.Get(2)->GetInteger();
2000 sal_Int16 nDay
= rPar
.Get(3)->GetInteger();
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
);
2016 sal_Int16 nHour
= rPar
.Get(1)->GetInteger();
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
);
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
);
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();
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
);
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
)
2077 if ( fResult
> 0.0 )
2079 fResult
= floor( fResult
);
2083 fResult
= ceil( fResult
);
2086 rPar
.Get(0)->PutDate( fResult
);
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
);
2103 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2104 if( GetSbData()->pInst
)
2105 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2109 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, n
, n
);
2112 sal_uInt32 nIndex
= 0;
2114 bool bSuccess
= pFormatter
->IsNumberFormat( rPar
.Get(1)->GetOUString(),
2116 SvNumFormatType nType
= pFormatter
->GetType(nIndex
);
2117 if(bSuccess
&& (nType
==SvNumFormatType::TIME
||nType
==SvNumFormatType::DATETIME
))
2119 if ( nType
== SvNumFormatType::DATETIME
)
2122 fResult
= fmod( fResult
, 1 );
2124 rPar
.Get(0)->PutDate( fResult
);
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
);
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
);
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
);
2166 sal_Int32 nSeconds
= static_cast<sal_Int32
>(nFrac
+ 0.5);
2167 sal_Int16 nHour
= static_cast<sal_Int16
>(nSeconds
/ 3600);
2171 void SbRtl_Hour(StarBASIC
*, SbxArray
& rPar
, bool)
2173 if ( rPar
.Count() < 2 )
2175 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
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
);
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
);
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
);
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
);
2226 void SbRtl_Second(StarBASIC
*, SbxArray
& rPar
, bool)
2228 if ( rPar
.Count() < 2 )
2230 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2234 double nArg
= rPar
.Get(1)->GetDate();
2235 sal_Int16 nSecond
= implGetSecond( nArg
);
2236 rPar
.Get(0)->PutInteger( nSecond
);
2242 DateTime
aDateTime( DateTime::SYSTEM
);
2243 double aSerial
= static_cast<double>(GetDayDiff( aDateTime
));
2244 long nSeconds
= aDateTime
.GetHour();
2246 nSeconds
+= aDateTime
.GetMin() * 60;
2247 nSeconds
+= aDateTime
.GetSec();
2248 double nDays
= static_cast<double>(nSeconds
) / (24.0*3600.0);
2255 void SbRtl_Now(StarBASIC
*, SbxArray
& rPar
, bool)
2257 rPar
.Get(0)->PutDate( Now_Impl() );
2262 void SbRtl_Time(StarBASIC
*, SbxArray
& rPar
, bool bWrite
)
2266 tools::Time
aTime( tools::Time::SYSTEM
);
2267 SbxVariable
* pMeth
= rPar
.Get( 0 );
2269 if( pMeth
->IsFixed() )
2273 snprintf( buf
, sizeof(buf
), "%02d:%02d:%02d",
2274 aTime
.GetHour(), aTime
.GetMin(), aTime
.GetSec() );
2275 aRes
= OUString::createFromAscii( buf
);
2279 // Time: system dependent
2280 long nSeconds
=aTime
.GetHour();
2282 nSeconds
+= aTime
.GetMin() * 60;
2283 nSeconds
+= aTime
.GetSec();
2284 double nDays
= static_cast<double>(nSeconds
) * ( 1.0 / (24.0*3600.0) );
2287 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2289 if( GetSbData()->pInst
)
2291 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2292 nIndex
= GetSbData()->pInst
->GetStdTimeIdx();
2296 sal_uInt32 n
; // Dummy
2297 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, nIndex
, n
);
2300 pFormatter
->GetOutputString( nDays
, nIndex
, aRes
, &pCol
);
2302 pMeth
->PutString( aRes
);
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();
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
)
2325 Date
aToday( Date::SYSTEM
);
2326 double nDays
= static_cast<double>(GetDayDiff( aToday
));
2327 SbxVariable
* pMeth
= rPar
.Get( 0 );
2328 if( pMeth
->IsString() )
2333 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
2335 if( GetSbData()->pInst
)
2337 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
2338 nIndex
= GetSbData()->pInst
->GetStdDateIdx();
2343 pFormatter
= SbiInstance::PrepareNumberFormatter( nIndex
, n
, n
);
2346 pFormatter
->GetOutputString( nDays
, nIndex
, aRes
, &pCol
);
2347 pMeth
->PutString( aRes
);
2351 pMeth
->PutDate( nDays
);
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
);
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
);
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
);
2400 // #46134 only string is converted, all other types result in sal_False
2401 SbxVariableRef xArg
= rPar
.Get( 1 );
2402 SbxDataType eType
= xArg
->GetType();
2405 if( eType
== SbxDATE
)
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
);
2434 SbxVariable
* pVar
= nullptr;
2435 if( SbiRuntime::isVBAEnabled() )
2437 pVar
= getDefaultProp( rPar
.Get(1) );
2441 pVar
->Broadcast( SfxHintId::BasicDataWanted
);
2442 rPar
.Get( 0 )->PutBool( pVar
->IsEmpty() );
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
);
2459 SbxVariable
* pVar
=rPar
.Get( 1 );
2460 SbUnoObject
* pObj
= dynamic_cast<SbUnoObject
*>( pVar
);
2463 if ( SbxBase
* pBaseObj
= (pVar
->IsObject() ? pVar
->GetObject() : nullptr) )
2465 pObj
= dynamic_cast<SbUnoObject
*>( pBaseObj
);
2468 uno::Reference
< script::XErrorQuery
> xError
;
2471 xError
.set( pObj
->getUnoAny(), uno::UNO_QUERY
);
2475 rPar
.Get( 0 )->PutBool( xError
->hasError() );
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
);
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();
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
);
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
);
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
);
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
);
2561 // Wildcards in path?
2562 if( nLastDelim
>= 0 && nLastDelim
> nLastWild
)
2569 OUString aPathStr
= getFullPath( aFileParam
);
2570 if( nLastDelim
!= aFileParam
.getLength() - 1 )
2572 rRTLData
.sFullNameToBeChecked
= aPathStr
;
2577 OUString aPureFileName
;
2578 if( nLastDelim
< 0 )
2580 aPureFileName
= aFileParam
;
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
);
2601 static bool implCheckWildcard(const OUString
& rName
, SbiRTLData
const& rRTLData
)
2605 if (rRTLData
.pWildCard
)
2607 bMatch
= rRTLData
.pWildCard
->Matches(rName
);
2613 static bool isRootDir( const OUString
& aDirURLStr
)
2615 INetURLObject
aDirURLObj( aDirURLStr
);
2618 // Check if it's a root directory
2619 sal_Int32 nCount
= aDirURLObj
.getSegmentCount();
2621 // No segment means Unix root directory "file:///"
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] == ':' )
2638 // More than one segments can never be root
2639 // so bRoot remains false
2644 void SbRtl_Dir(StarBASIC
*, SbxArray
& rPar
, bool)
2648 sal_uInt16 nParCount
= rPar
.Count();
2651 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
2655 SbiRTLData
& rRTLData
= GetSbData()->pInst
->GetRTLData();
2659 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
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
;
2676 INetURLObject
aFileURL( aFileURLStr
);
2677 aNameOnlyStr
= aFileURL
.getName( INetURLObject::LAST_SEGMENT
,
2678 true, INetURLObject::DecodeMechanism::WithCharset
);
2680 rPar
.Get(0)->PutString( aNameOnlyStr
);
2686 OUString aDirURLStr
;
2687 bool bFolder
= xSFI
->isFolder( aFileURLStr
);
2691 aDirURLStr
= aFileURLStr
;
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());
2706 rRTLData
.nDirFlags
= SbAttributes::NONE
;
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 ".."
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() );
2742 if (rRTLData
.nCurDirPos
< 0)
2744 if (rRTLData
.nCurDirPos
== -2)
2748 else if (rRTLData
.nCurDirPos
== -1)
2752 rRTLData
.nCurDirPos
++;
2754 else if (rRTLData
.nCurDirPos
>= rRTLData
.aDirSeq
.getLength())
2756 rRTLData
.aDirSeq
.realloc(0);
2763 = rRTLData
.aDirSeq
.getConstArray()[rRTLData
.nCurDirPos
++];
2765 if( bCompatibility
)
2769 bool bFolder
= xSFI
->isFolder( aFile
);
2781 bool bFolder
= xSFI
->isFolder( aFile
);
2789 INetURLObject
aURL( aFile
);
2790 aPath
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true,
2791 INetURLObject::DecodeMechanism::WithCharset
);
2794 bool bMatch
= implCheckWildcard(aPath
, rRTLData
);
2802 rPar
.Get(0)->PutString( aPath
);
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());
2822 rRTLData
.nDirFlags
= SbAttributes::NONE
;
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() );
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 ".."
2848 rRTLData
.nCurDirPos
= -2;
2856 bool bFolderFlag
= bool(rRTLData
.nDirFlags
& SbAttributes::DIRECTORY
);
2859 if (rRTLData
.nCurDirPos
< 0)
2861 if (rRTLData
.nCurDirPos
== -2)
2865 else if (rRTLData
.nCurDirPos
== -1)
2869 rRTLData
.nCurDirPos
++;
2873 DirectoryItem aItem
;
2874 FileBase::RC nRet
= rRTLData
.pDir
->getNextItem(aItem
);
2875 if( nRet
!= FileBase::E_None
)
2877 rRTLData
.pDir
.reset();
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");
2891 // Only directories?
2894 FileStatus::Type aType
= aFileStatus
.getFileType();
2895 bool bFolder
= isFolder( aType
);
2902 aPath
= aFileStatus
.getFileName();
2905 bool bMatch
= implCheckWildcard(aPath
, rRTLData
);
2913 rPar
.Get(0)->PutString( aPath
);
2919 void SbRtl_GetAttr(StarBASIC
* pBasic
, SbxArray
& rPar
, bool 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.
2931 if( SbiRuntime::isVBAEnabled() )
2933 OUString aPathURL
= getFullPath( rPar
.Get(1)->GetOUString() );
2935 FileBase::getSystemPathFromFileURL( aPathURL
, aPath
);
2936 DWORD nRealFlags
= GetFileAttributesW (o3tl::toW(aPath
.getStr()));
2937 if (nRealFlags
!= 0xffffffff)
2939 if (nRealFlags
== FILE_ATTRIBUTE_NORMAL
)
2943 nFlags
= static_cast<sal_Int16
>(nRealFlags
);
2947 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
2949 rPar
.Get(0)->PutInteger( nFlags
);
2957 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
2962 OUString aPath
= getFullPath( rPar
.Get(1)->GetOUString() );
2963 bool bExists
= false;
2964 try { bExists
= xSFI
->exists( aPath
); }
2965 catch(const Exception
& ) {}
2968 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
2972 bool bReadOnly
= xSFI
->isReadOnly( aPath
);
2973 bool bHidden
= xSFI
->isHidden( aPath
);
2974 bool bDirectory
= xSFI
->isFolder( aPath
);
2977 nFlags
|= sal_uInt16(SbAttributes::READONLY
);
2981 nFlags
|= sal_uInt16(SbAttributes::HIDDEN
);
2985 nFlags
|= sal_uInt16(SbAttributes::DIRECTORY
);
2988 catch(const Exception
& )
2990 StarBASIC::Error( ERRCODE_IO_GENERAL
);
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
);
3007 nFlags
|= sal_uInt16(SbAttributes::READONLY
);
3011 nFlags
|= sal_uInt16(SbAttributes::DIRECTORY
);
3014 rPar
.Get(0)->PutInteger( nFlags
);
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
);
3031 OUString aPath
= rPar
.Get(1)->GetOUString();
3032 tools::Time
aTime( tools::Time::EMPTY
);
3033 Date
aDate( Date::EMPTY
);
3036 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
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
);
3053 bool bSuccess
= false;
3056 DirectoryItem aItem
;
3057 if (DirectoryItem::get( getFullPath( aPath
), aItem
) != FileBase::E_None
)
3060 FileStatus
aFileStatus( osl_FileStatus_Mask_ModifyTime
);
3061 if (aItem
.getFileStatus( aFileStatus
) != FileBase::E_None
)
3064 TimeValue aTimeVal
= aFileStatus
.getModifyTime();
3066 if (!osl_getDateTimeFromTimeValue( &aTimeVal
, &aDT
))
3067 // Strictly spoken this is not an i/o error but some other failure.
3070 aTime
= tools::Time( aDT
.Hours
, aDT
.Minutes
, aDT
.Seconds
, aDT
.NanoSeconds
);
3071 aDate
= Date( aDT
.Day
, aDT
.Month
, aDT
.Year
);
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.
3086 if (aDate
.IsEmpty())
3088 aRes
= "0000-00-00 00:00:00";
3092 double fSerial
= static_cast<double>(GetDayDiff( aDate
));
3093 long nSeconds
= aTime
.GetHour();
3095 nSeconds
+= aTime
.GetMin() * 60;
3096 nSeconds
+= aTime
.GetSec();
3097 double nDays
= static_cast<double>(nSeconds
) / (24.0*3600.0);
3102 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
3104 if( GetSbData()->pInst
)
3106 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
3107 nIndex
= GetSbData()->pInst
->GetStdDateTimeIdx();
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
);
3131 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3132 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3133 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3136 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3140 SvStream
* pSvStrm
= pSbStrm
->GetStrm();
3141 if ( pSbStrm
->IsText() )
3144 (*pSvStrm
).ReadChar( cBla
); // can we read another character?
3145 beof
= pSvStrm
->eof();
3148 pSvStrm
->SeekRel( -1 );
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
);
3172 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3173 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3174 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3177 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3181 if ( rPar
.Get(2)->GetInteger() == 1 )
3183 nRet
= static_cast<sal_Int16
>(pSbStrm
->GetMode());
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
);
3201 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3202 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3203 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3206 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
3209 SvStream
* pSvStrm
= pSbStrm
->GetStrm();
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;
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
);
3246 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3247 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3248 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3251 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
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
);
3270 sal_Int16 nChannel
= rPar
.Get(1)->GetInteger();
3271 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3272 SbiStream
* pSbStrm
= pIO
->GetStream( nChannel
);
3275 StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL
);
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();
3295 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
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
);
3319 if( nArgCount
== 2 )
3321 rPar
.Get(1)->Format( aResult
);
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
);
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
);
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;
3367 case ooo::vba::VbTriState::vbTrue
:
3368 bIncludeLeadingDigit
= true;
3370 case ooo::vba::VbTriState::vbUseDefault
:
3374 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
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
:
3388 case ooo::vba::VbTriState::vbTrue
:
3389 bUseParensForNegativeNumbers
= true;
3392 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
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
:
3406 case ooo::vba::VbTriState::vbTrue
:
3407 bGroupDigits
= true;
3410 StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT
);
3415 double fVal
= rPar
.Get(1)->GetDouble();
3416 const bool bNegative
= fVal
< 0;
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
);
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');
3450 if (bUseParensForNegativeNumbers
)
3451 aResult
= "(" + aResult
+ ")";
3453 aResult
= "-" + aResult
;
3456 rPar
.Get(0)->PutString(aResult
);
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
);
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):
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
);
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();
3548 aCmdLine
+= " " + tmp
;
3551 else if( aCmdLine
.isEmpty() )
3553 // avoid special treatment (empty list)
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
;
3569 if ( c
!= ' ' && c
!= '\t' )
3575 if( c
== '\"' || c
== '\'' )
3577 sal_Int32 iFoundPos
= aCmdLine
.indexOf( c
, i
+ 1 );
3581 aToken
= aCmdLine
.copy( i
);
3586 aToken
= aCmdLine
.copy( i
+ 1, (iFoundPos
- i
- 1) );
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;
3598 aToken
= aCmdLine
.copy( i
);
3603 aToken
= aCmdLine
.copy( i
, (iFoundPos
- i
) );
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();
3620 nOptions
|= osl_Process_MINIMIZED
;
3623 nOptions
|= osl_Process_MAXIMIZED
;
3626 nOptions
|= osl_Process_FULLSCREEN
;
3631 if( nArgCount
>= 5 )
3633 bSync
= rPar
.Get(4)->GetBool();
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
);
3647 sal_uInt16 nParamCount
= sal::static_int_cast
< sal_uInt16
>(aTokenVector
.size() - 1 );
3648 std::unique_ptr
<rtl_uString
*[]> pParamList
;
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
);
3661 bool bSucc
= osl_executeProcess(
3662 aOUStrProgURL
.pData
,
3669 &pApp
) == osl_Process_E_None
;
3671 // 53521 only free process handle on success
3674 osl_freeProcessHandle( pApp
);
3677 for(int j
= 0; j
< nParamCount
; ++j
)
3679 rtl_uString_release(pParamList
[j
]);
3684 StarBASIC::Error( ERRCODE_BASIC_FILE_NOT_FOUND
);
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
);
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
3713 "Integer", // SbxINTEGER
3715 "Single", // SbxSINGLE
3716 "Double", // SbxDOUBLE
3717 "Currency", // SbxCURRENCY
3719 "String", // SbxSTRING
3720 "Object", // SbxOBJECT
3721 "Error", // SbxERROR
3722 "Boolean", // SbxBOOL
3723 "Variant", // SbxVARIANT
3724 "DataObject", // SbxDATAOBJECT
3729 "UShort", // SbxUSHORT
3730 "ULong", // SbxULONG
3731 "Long64", // SbxLONG64
3732 "ULong64", // SbxULONG64
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" );
3765 SbxBase
* pBaseObj
= pVar
->GetObject();
3772 SbUnoObject
* pUnoObj
= dynamic_cast<SbUnoObject
*>( pVar
);
3775 pUnoObj
= dynamic_cast<SbUnoObject
*>( pBaseObj
);
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
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 ];
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
);
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 );
3823 void SbRtl_TypeName(StarBASIC
*, SbxArray
& rPar
, bool)
3825 if ( rPar
.Count() != 2 )
3827 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3831 SbxDataType eType
= rPar
.Get(1)->GetType();
3832 bool bIsArray
= ( ( eType
& SbxARRAY
) != 0 );
3835 if ( SbiRuntime::isVBAEnabled() && eType
== SbxOBJECT
)
3837 aRetStr
= getObjectTypeName( rPar
.Get(1) );
3841 aRetStr
= getBasicTypeName( eType
);
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
);
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());
3869 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3872 const OUString
& rApp
= rPar
.Get(1)->GetOUString();
3873 const OUString
& rTopic
= rPar
.Get(2)->GetOUString();
3875 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3877 ErrCode nDdeErr
= pDDE
->Initiate( rApp
, rTopic
, nChannel
);
3880 StarBASIC::Error( nDdeErr
);
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());
3894 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3897 size_t nChannel
= rPar
.Get(1)->GetInteger();
3898 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3899 ErrCode nDdeErr
= pDDE
->Terminate( nChannel
);
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());
3912 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3916 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3917 ErrCode nDdeErr
= pDDE
->TerminateAll();
3920 StarBASIC::Error( nDdeErr
);
3924 void SbRtl_DDERequest(StarBASIC
*, SbxArray
& rPar
, bool)
3926 int nArgs
= static_cast<int>(rPar
.Count());
3929 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3932 size_t nChannel
= rPar
.Get(1)->GetInteger();
3933 const OUString
& rItem
= rPar
.Get(2)->GetOUString();
3934 SbiDdeControl
* pDDE
= GetSbData()->pInst
->GetDdeControl();
3936 ErrCode nDdeErr
= pDDE
->Request( nChannel
, rItem
, aResult
);
3939 StarBASIC::Error( nDdeErr
);
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());
3953 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
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
);
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());
3972 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
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
);
3982 StarBASIC::Error( nDdeErr
);
3987 void SbRtl_FreeFile(StarBASIC
*, SbxArray
& rPar
, bool)
3989 if ( rPar
.Count() != 1 )
3991 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
3994 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
3996 while( nChannel
< CHANNELS
)
3998 SbiStream
* pStrm
= pIO
->GetStream( nChannel
);
4001 rPar
.Get(0)->PutInteger( 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
);
4017 SbxBase
* pParObj
= rPar
.Get(1)->GetObject();
4018 SbxDimArray
* pArr
= dynamic_cast<SbxDimArray
*>( pParObj
);
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
);
4026 rPar
.Get(0)->PutLong( nLower
);
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
);
4041 SbxBase
* pParObj
= rPar
.Get(1)->GetObject();
4042 SbxDimArray
* pArr
= dynamic_cast<SbxDimArray
*>( pParObj
);
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
);
4050 rPar
.Get(0)->PutLong( nUpper
);
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
);
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;
4069 SbiInstance
* pInst
= GetSbData()->pInst
;
4070 bool bCompatibility
= ( pInst
&& pInst
->IsCompatibility() );
4071 if( bCompatibility
)
4073 nRGB
= (nBlue
<< 16) | (nGreen
<< 8) | nRed
;
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
[] =
4104 if ( rPar
.Count() != 2 )
4106 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4110 sal_Int16 nCol
= rPar
.Get(1)->GetInteger();
4111 if( nCol
< 0 || nCol
> 15 )
4113 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
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
);
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();
4138 // null string,return
4139 rPar
.Get(0)->PutString(aOldStr
);
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 );
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
);
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() );
4217 pArray
->AddDim( 1, nArraySize
);
4221 pArray
->AddDim( 0, nArraySize
-1 );
4226 pArray
->unoAddDim( 0, -1 );
4229 for( sal_Int32 i
=0; i
< nArraySize
; i
++)
4231 SbxVariable
* pNew
= new SbxVariable( SbxBYTE
);
4232 pNew
->PutByte(*pChar
);
4234 pNew
->SetFlag( SbxFlagBits::Write
);
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 );
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
);
4266 void SbRtl_Load(StarBASIC
*, SbxArray
& rPar
, bool)
4268 if( rPar
.Count() != 2 )
4270 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4275 SbxBase
* pObj
= rPar
.Get(1)->GetObject();
4278 if (SbUserFormModule
* pModule
= dynamic_cast<SbUserFormModule
*>(pObj
))
4282 else if (SbxObject
* pSbxObj
= dynamic_cast<SbxObject
*>(pObj
))
4284 SbxVariable
* pVar
= pSbxObj
->Find("Load", SbxClassType::Method
);
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
);
4303 SbxBase
* pObj
= rPar
.Get(1)->GetObject();
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
);
4321 void SbRtl_LoadPicture(StarBASIC
*, SbxArray
& rPar
, bool)
4323 if( rPar
.Count() != 2 )
4325 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4329 OUString aFileURL
= getFullPath( rPar
.Get(1)->GetOUString() );
4330 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream( aFileURL
, StreamMode::READ
));
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
);
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
);
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
4388 OUString aMsg
= rPar
.Get(1)->GetOUString();
4390 if( nArgCount
>= 4 )
4392 aTitle
= rPar
.Get(3)->GetOUString();
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
)
4410 eType
= VclMessageType::Error
;
4413 eType
= VclMessageType::Question
;
4416 eType
= VclMessageType::Warning
;
4419 eType
= VclMessageType::Info
;
4423 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pParent
,
4424 eType
, VclButtonsType::NONE
, aMsg
));
4430 xBox
->add_button(GetStandardText(StandardButtonType::OK
), BasicResponse::Ok
);
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
);
4439 xBox
->set_default_response(BasicResponse::Ok
);
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
);
4448 xBox
->set_default_response(BasicResponse::Retry
);
4449 else if (nType
& 512)
4450 xBox
->set_default_response(BasicResponse::Ignore
);
4452 xBox
->set_default_response(BasicResponse::Cancel
);
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
);
4463 xBox
->set_default_response(BasicResponse::Yes
);
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
);
4473 xBox
->set_default_response(BasicResponse::Yes
);
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
);
4483 xBox
->set_default_response(BasicResponse::Retry
);
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() );
4503 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
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
);
4522 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4526 void SbRtl_Reset(StarBASIC
*, SbxArray
&, bool)
4528 SbiIoSystem
* pIO
= GetSbData()->pInst
->GetIoSystem();
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
);
4544 StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR
);
4548 SbxObject
* p
= pBasic
;
4549 while( p
->GetParent() )
4553 SvFileStream
aStrm( rPar
.Get( 1 )->GetOUString(),
4554 StreamMode::WRITE
| StreamMode::TRUNC
);
4555 p
->Dump( aStrm
, rPar
.Get( 2 )->GetBool() );
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;
4574 const uno::Reference
< ucb::XSimpleFileAccess3
>& xSFI
= getFileAccess();
4579 bExists
= xSFI
->exists( aStr
);
4581 catch(const Exception
& )
4583 StarBASIC::Error( ERRCODE_IO_GENERAL
);
4589 DirectoryItem aItem
;
4590 FileBase::RC nRet
= DirectoryItem::get( getFullPath( aStr
), aItem
);
4591 bExists
= (nRet
== FileBase::E_None
);
4593 rPar
.Get(0)->PutBool( bExists
);
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
);
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
);
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
;
4646 sal_Int32 nLowerValue
= nNumber
;
4647 sal_Int32 nUpperValue
= nLowerValue
;
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();
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(":");
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());
4683 static long GetDayDiff( const Date
& rDate
)
4685 Date
aRefDate( 1,1,1900 );
4687 if ( aRefDate
> rDate
)
4689 nDiffDays
= aRefDate
- rDate
;
4694 nDiffDays
= rDate
- aRefDate
;
4696 nDiffDays
+= 2; // adjustment VisualBasic: 1.Jan.1900 == 2
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();
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
);
4727 else if ( nYear
< 30 && SbiRuntime::isVBAEnabled() )
4734 if ( 0 <= nYear
&& nYear
< 100 && (bUseTwoDigitYear
4735 #if HAVE_FEATURE_SCRIPTING
4736 || SbiRuntime::isVBAEnabled()
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
;
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)
4758 nAddDays
= nDay
- 1;
4763 nAddDays
= 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
4776 #if HAVE_FEATURE_SCRIPTING
4777 if (eCorr
== SbDateCorrection::RollOver
&& !SbiRuntime::isVBAEnabled())
4778 eCorr
= SbDateCorrection::None
;
4781 if (nYear
== 0 || (eCorr
== SbDateCorrection::None
&& (nAddMonths
|| nAddDays
|| !aCurDate
.IsValidDate())))
4783 #if HAVE_FEATURE_SCRIPTING
4784 StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT
);
4789 if (eCorr
!= SbDateCorrection::None
)
4791 aCurDate
.Normalize();
4793 aCurDate
.AddMonths( nAddMonths
);
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);
4806 aCurDate
.SetMonth(nMonth
);
4808 aCurDate
.AddMonths(1);
4809 aCurDate
.AddDays(-1);
4814 long nDiffDays
= GetDayDiff( aCurDate
);
4815 rdRet
= static_cast<double>(nDiffDays
);
4819 double implTimeSerial( sal_Int16 nHours
, sal_Int16 nMinutes
, sal_Int16 nSeconds
)
4822 static_cast<double>( nHours
* ::tools::Time::secondPerHour
+
4823 nMinutes
* ::tools::Time::secondPerMinute
+
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
,
4834 if(!implDateSerial(nYear
, nMonth
, nDay
, false/*bUseTwoDigitYear*/, SbDateCorrection::None
, dDate
))
4836 rdRet
+= dDate
+ implTimeSerial(nHour
, nMinute
, nSecond
);
4840 sal_Int16
implGetMinute( double dDate
)
4842 double nFrac
= dDate
- floor( dDate
);
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;
4850 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */