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