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