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