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