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