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