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