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