1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sal.hxx" 30 31 #include "rtl/bootstrap.h" 32 #include "rtl/bootstrap.hxx" 33 #include <osl/diagnose.h> 34 #include <osl/module.h> 35 #include <osl/process.h> 36 #include <osl/file.hxx> 37 #include <osl/mutex.hxx> 38 #include <osl/profile.hxx> 39 #include <osl/security.hxx> 40 #include <rtl/alloc.h> 41 #include <rtl/string.hxx> 42 #include <rtl/ustrbuf.hxx> 43 #include <rtl/ustring.hxx> 44 #include <rtl/byteseq.hxx> 45 #include <rtl/instance.hxx> 46 #include <rtl/malformeduriexception.hxx> 47 #include <rtl/uri.hxx> 48 #include "rtl/allocator.hxx" 49 50 #include "macro.hxx" 51 52 #include <hash_map> 53 #include <list> 54 55 #define MY_STRING_(x) # x 56 #define MY_STRING(x) MY_STRING_(x) 57 58 //---------------------------------------------------------------------------- 59 60 using osl::DirectoryItem; 61 using osl::FileStatus; 62 63 using rtl::OString; 64 using rtl::OUString; 65 using rtl::OUStringToOString; 66 67 struct Bootstrap_Impl; 68 69 namespace { 70 71 static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:"; 72 73 bool isPathnameUrl(rtl::OUString const & url) { 74 return url.matchIgnoreAsciiCaseAsciiL( 75 RTL_CONSTASCII_STRINGPARAM(VND_SUN_STAR_PATHNAME)); 76 } 77 78 bool resolvePathnameUrl(rtl::OUString * url) { 79 OSL_ASSERT(url != NULL); 80 if (!isPathnameUrl(*url) || 81 (osl::FileBase::getFileURLFromSystemPath( 82 url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) == 83 osl::FileBase::E_None)) 84 { 85 return true; 86 } else { 87 *url = rtl::OUString(); 88 return false; 89 } 90 } 91 92 enum LookupMode { 93 LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP, 94 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION }; 95 96 struct ExpandRequestLink { 97 ExpandRequestLink const * next; 98 Bootstrap_Impl const * file; 99 rtl::OUString key; 100 }; 101 102 rtl::OUString expandMacros( 103 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode, 104 ExpandRequestLink const * requestStack); 105 106 rtl::OUString recursivelyExpandMacros( 107 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode, 108 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey, 109 ExpandRequestLink const * requestStack) 110 { 111 for (; requestStack != NULL; requestStack = requestStack->next) { 112 if (requestStack->file == requestFile && 113 requestStack->key == requestKey) 114 { 115 return rtl::OUString( 116 RTL_CONSTASCII_USTRINGPARAM("***RECURSION DETECTED***")); 117 } 118 } 119 ExpandRequestLink link = { requestStack, requestFile, requestKey }; 120 return expandMacros(file, text, mode, &link); 121 } 122 123 } 124 125 //---------------------------------------------------------------------------- 126 127 struct rtl_bootstrap_NameValue 128 { 129 OUString sName; 130 OUString sValue; 131 132 inline rtl_bootstrap_NameValue() SAL_THROW( () ) 133 {} 134 inline rtl_bootstrap_NameValue( 135 OUString const & name, OUString const & value ) SAL_THROW( () ) 136 : sName( name ), 137 sValue( value ) 138 {} 139 }; 140 141 typedef std::list< 142 rtl_bootstrap_NameValue, 143 rtl::Allocator< rtl_bootstrap_NameValue > 144 > NameValueList; 145 146 bool find( 147 NameValueList const & list, rtl::OUString const & key, 148 rtl::OUString * value) 149 { 150 OSL_ASSERT(value != NULL); 151 for (NameValueList::const_iterator i(list.begin()); i != list.end(); ++i) { 152 if (i->sName == key) { 153 *value = i->sValue; 154 return true; 155 } 156 } 157 return false; 158 } 159 160 namespace { 161 struct rtl_bootstrap_set_list : 162 public rtl::Static< NameValueList, rtl_bootstrap_set_list > {}; 163 } 164 165 //---------------------------------------------------------------------------- 166 167 static sal_Bool getFromCommandLineArgs( 168 rtl::OUString const & key, rtl::OUString * value ) 169 { 170 OSL_ASSERT(value != NULL); 171 static NameValueList *pNameValueList = 0; 172 if( ! pNameValueList ) 173 { 174 static NameValueList nameValueList; 175 176 sal_Int32 nArgCount = osl_getCommandArgCount(); 177 for(sal_Int32 i = 0; i < nArgCount; ++ i) 178 { 179 rtl_uString *pArg = 0; 180 osl_getCommandArg( i, &pArg ); 181 if( ('-' == pArg->buffer[0] || '/' == pArg->buffer[0] ) && 182 'e' == pArg->buffer[1] && 183 'n' == pArg->buffer[2] && 184 'v' == pArg->buffer[3] && 185 ':' == pArg->buffer[4] ) 186 { 187 sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' ); 188 if( nIndex >= 0 ) 189 { 190 191 rtl_bootstrap_NameValue nameValue; 192 nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 ); 193 nameValue.sValue = OUString( &(pArg->buffer[nIndex+1]) ); 194 if( i == nArgCount-1 && 195 nameValue.sValue.getLength() && 196 nameValue.sValue[nameValue.sValue.getLength()-1] == 13 ) 197 { 198 // avoid the 13 linefeed for the last argument, 199 // when the executable is started from a script, 200 // that was edited on windows 201 nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1); 202 } 203 nameValueList.push_back( nameValue ); 204 } 205 } 206 rtl_uString_release( pArg ); 207 } 208 pNameValueList = &nameValueList; 209 } 210 211 sal_Bool found = sal_False; 212 213 for( NameValueList::iterator ii = pNameValueList->begin() ; 214 ii != pNameValueList->end() ; 215 ++ii ) 216 { 217 if( (*ii).sName.equals(key) ) 218 { 219 *value = (*ii).sValue; 220 found = sal_True; 221 break; 222 } 223 } 224 225 return found; 226 } 227 228 //---------------------------------------------------------------------------- 229 230 extern "C" oslProcessError SAL_CALL osl_bootstrap_getExecutableFile_Impl ( 231 rtl_uString ** ppFileURL) SAL_THROW_EXTERN_C(); 232 233 inline void getExecutableFile_Impl (rtl_uString ** ppFileURL) 234 { 235 osl_bootstrap_getExecutableFile_Impl (ppFileURL); 236 } 237 238 //---------------------------------------------------------------------------- 239 240 static void getExecutableDirectory_Impl (rtl_uString ** ppDirURL) 241 { 242 OUString fileName; 243 getExecutableFile_Impl (&(fileName.pData)); 244 245 sal_Int32 nDirEnd = fileName.lastIndexOf('/'); 246 OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory"); 247 248 rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd); 249 } 250 251 //---------------------------------------------------------------------------- 252 253 static OUString & getIniFileName_Impl() 254 { 255 static OUString *pStaticName = 0; 256 if( ! pStaticName ) 257 { 258 OUString fileName; 259 260 if(getFromCommandLineArgs( 261 OUString(RTL_CONSTASCII_USTRINGPARAM("INIFILENAME")), &fileName)) 262 { 263 resolvePathnameUrl(&fileName); 264 } 265 else 266 { 267 getExecutableFile_Impl (&(fileName.pData)); 268 269 // get rid of a potential executable extension 270 OUString progExt (RTL_CONSTASCII_USTRINGPARAM(".bin")); 271 if(fileName.getLength() > progExt.getLength() 272 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt)) 273 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength()); 274 275 progExt = OUString::createFromAscii(".exe"); 276 if(fileName.getLength() > progExt.getLength() 277 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt)) 278 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength()); 279 280 // append config file suffix 281 fileName += OUString(RTL_CONSTASCII_USTRINGPARAM(SAL_CONFIGFILE(""))); 282 } 283 284 static OUString theFileName; 285 if(fileName.getLength()) 286 theFileName = fileName; 287 288 pStaticName = &theFileName; 289 } 290 291 return *pStaticName; 292 } 293 294 //---------------------------------------------------------------------------- 295 296 static inline bool path_exists( OUString const & path ) 297 { 298 DirectoryItem dirItem; 299 return (DirectoryItem::E_None == DirectoryItem::get( path, dirItem )); 300 } 301 302 //---------------------------------------------------------------------------- 303 // #111772# 304 // ensure the given file url has no final slash 305 306 inline void EnsureNoFinalSlash (rtl::OUString & url) 307 { 308 sal_Int32 i = url.getLength(); 309 if (i > 0 && url[i - 1] == '/') { 310 url = url.copy(0, i - 1); 311 } 312 } 313 314 //---------------------------------------------------------------------------- 315 //---------------------------------------------------------------------------- 316 317 struct Bootstrap_Impl 318 { 319 sal_Int32 _nRefCount; 320 Bootstrap_Impl * _base_ini; 321 322 NameValueList _nameValueList; 323 OUString _iniName; 324 325 explicit Bootstrap_Impl (OUString const & rIniName); 326 ~Bootstrap_Impl(); 327 328 static void * operator new (std::size_t n) SAL_THROW(()) 329 { return rtl_allocateMemory (sal_uInt32(n)); } 330 static void operator delete (void * p , std::size_t) SAL_THROW(()) 331 { rtl_freeMemory (p); } 332 333 bool getValue( 334 rtl::OUString const & key, rtl_uString ** value, 335 rtl_uString * defaultValue, LookupMode mode, bool override, 336 ExpandRequestLink const * requestStack) const; 337 bool getDirectValue( 338 rtl::OUString const & key, rtl_uString ** value, LookupMode mode, 339 ExpandRequestLink const * requestStack) const; 340 bool getAmbienceValue( 341 rtl::OUString const & key, rtl_uString ** value, LookupMode mode, 342 ExpandRequestLink const * requestStack) const; 343 void expandValue( 344 rtl_uString ** value, rtl::OUString const & text, LookupMode mode, 345 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey, 346 ExpandRequestLink const * requestStack) const; 347 }; 348 349 //---------------------------------------------------------------------------- 350 351 Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName ) 352 : _nRefCount( 0 ), 353 _base_ini( 0 ), 354 _iniName (rIniName) 355 { 356 OUString base_ini( getIniFileName_Impl() ); 357 // normalize path 358 FileStatus status( FileStatusMask_FileURL ); 359 DirectoryItem dirItem; 360 if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) && 361 DirectoryItem::E_None == dirItem.getFileStatus( status )) 362 { 363 base_ini = status.getFileURL(); 364 if (! rIniName.equals( base_ini )) 365 { 366 _base_ini = static_cast< Bootstrap_Impl * >( 367 rtl_bootstrap_args_open( base_ini.pData ) ); 368 } 369 } 370 371 #if OSL_DEBUG_LEVEL > 1 372 OString sFile = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US); 373 OSL_TRACE(__FILE__" -- Bootstrap_Impl() - %s\n", sFile.getStr()); 374 #endif /* OSL_DEBUG_LEVEL > 1 */ 375 376 oslFileHandle handle; 377 if (_iniName.getLength() && 378 osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read)) 379 { 380 rtl::ByteSequence seq; 381 382 while (osl_File_E_None == osl_readLine(handle , (sal_Sequence **)&seq)) 383 { 384 OString line( (const sal_Char *) seq.getConstArray(), seq.getLength() ); 385 sal_Int32 nIndex = line.indexOf('='); 386 if (nIndex >= 1) 387 { 388 struct rtl_bootstrap_NameValue nameValue; 389 nameValue.sName = OStringToOUString( 390 line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US ); 391 nameValue.sValue = OStringToOUString( 392 line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 ); 393 394 #if OSL_DEBUG_LEVEL > 1 395 OString name_tmp = OUStringToOString(nameValue.sName, RTL_TEXTENCODING_ASCII_US); 396 OString value_tmp = OUStringToOString(nameValue.sValue, RTL_TEXTENCODING_UTF8); 397 OSL_TRACE( 398 __FILE__" -- pushing: name=%s value=%s\n", 399 name_tmp.getStr(), value_tmp.getStr() ); 400 #endif /* OSL_DEBUG_LEVEL > 1 */ 401 402 _nameValueList.push_back(nameValue); 403 } 404 } 405 osl_closeFile(handle); 406 } 407 #if OSL_DEBUG_LEVEL > 1 408 else 409 { 410 OString file_tmp = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US); 411 OSL_TRACE( __FILE__" -- couldn't open file: %s", file_tmp.getStr() ); 412 } 413 #endif /* OSL_DEBUG_LEVEL > 1 */ 414 } 415 416 //---------------------------------------------------------------------------- 417 418 Bootstrap_Impl::~Bootstrap_Impl() 419 { 420 if (_base_ini != 0) 421 rtl_bootstrap_args_close( _base_ini ); 422 } 423 424 //---------------------------------------------------------------------------- 425 426 namespace { 427 428 Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(()) 429 { 430 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() ); 431 static Bootstrap_Impl * s_handle = 0; 432 if (s_handle == 0) 433 { 434 OUString iniName (getIniFileName_Impl()); 435 s_handle = static_cast< Bootstrap_Impl * >( 436 rtl_bootstrap_args_open( iniName.pData ) ); 437 if (s_handle == 0) 438 { 439 Bootstrap_Impl * that = new Bootstrap_Impl( iniName ); 440 ++that->_nRefCount; 441 s_handle = that; 442 } 443 } 444 return s_handle; 445 } 446 447 struct FundamentalIniData { 448 rtlBootstrapHandle ini; 449 450 FundamentalIniData() { 451 OUString uri; 452 ini = 453 ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())-> 454 getValue( 455 rtl::OUString( 456 RTL_CONSTASCII_USTRINGPARAM("URE_BOOTSTRAP")), 457 &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) && 458 resolvePathnameUrl(&uri)) 459 ? rtl_bootstrap_args_open(uri.pData) : NULL; 460 } 461 462 ~FundamentalIniData() { rtl_bootstrap_args_close(ini); } 463 464 private: 465 FundamentalIniData(FundamentalIniData &); // not defined 466 void operator =(FundamentalIniData &); // not defined 467 }; 468 469 struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni > 470 {}; 471 472 } 473 474 bool Bootstrap_Impl::getValue( 475 rtl::OUString const & key, rtl_uString ** value, rtl_uString * defaultValue, 476 LookupMode mode, bool override, ExpandRequestLink const * requestStack) 477 const 478 { 479 if (mode == LOOKUP_MODE_NORMAL && 480 key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URE_BOOTSTRAP"))) 481 { 482 mode = LOOKUP_MODE_URE_BOOTSTRAP; 483 } 484 if (override && getDirectValue(key, value, mode, requestStack)) { 485 return true; 486 } 487 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_OS"))) { 488 rtl_uString_assign( 489 value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(THIS_OS)).pData); 490 return true; 491 } 492 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_ARCH"))) { 493 rtl_uString_assign( 494 value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(THIS_ARCH)).pData); 495 return true; 496 } 497 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_CPPU_ENV"))) { 498 rtl_uString_assign( 499 value, 500 (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(MY_STRING(CPPU_ENV))). 501 pData)); 502 return true; 503 } 504 if (key.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("ORIGIN"))) { 505 rtl_uString_assign( 506 value, 507 _iniName.copy( 508 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData); 509 return true; 510 } 511 if (getAmbienceValue(key, value, mode, requestStack)) { 512 return true; 513 } 514 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSUSERCONFIG"))) { 515 rtl::OUString v; 516 bool b = osl::Security().getConfigDir(v); 517 EnsureNoFinalSlash(v); 518 rtl_uString_assign(value, v.pData); 519 return b; 520 } 521 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSUSERHOME"))) { 522 rtl::OUString v; 523 bool b = osl::Security().getHomeDir(v); 524 EnsureNoFinalSlash(v); 525 rtl_uString_assign(value, v.pData); 526 return b; 527 } 528 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSBINDIR"))) { 529 getExecutableDirectory_Impl(value); 530 return true; 531 } 532 if (_base_ini != NULL && 533 _base_ini->getDirectValue(key, value, mode, requestStack)) 534 { 535 return true; 536 } 537 if (!override && getDirectValue(key, value, mode, requestStack)) { 538 return true; 539 } 540 if (mode == LOOKUP_MODE_NORMAL) { 541 FundamentalIniData const & d = FundamentalIni::get(); 542 Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini); 543 if (b != NULL && b != this && 544 b->getDirectValue(key, value, mode, requestStack)) 545 { 546 return true; 547 } 548 } 549 if (defaultValue != NULL) { 550 rtl_uString_assign(value, defaultValue); 551 return true; 552 } 553 rtl_uString_new(value); 554 return false; 555 } 556 557 bool Bootstrap_Impl::getDirectValue( 558 rtl::OUString const & key, rtl_uString ** value, LookupMode mode, 559 ExpandRequestLink const * requestStack) const 560 { 561 rtl::OUString v; 562 if (find(_nameValueList, key, &v)) { 563 expandValue(value, v, mode, this, key, requestStack); 564 return true; 565 } else { 566 return false; 567 } 568 } 569 570 bool Bootstrap_Impl::getAmbienceValue( 571 rtl::OUString const & key, rtl_uString ** value, LookupMode mode, 572 ExpandRequestLink const * requestStack) const 573 { 574 rtl::OUString v; 575 bool f; 576 { 577 osl::MutexGuard g(osl::Mutex::getGlobalMutex()); 578 f = find(rtl_bootstrap_set_list::get(), key, &v); 579 } 580 if (f || getFromCommandLineArgs(key, &v) || 581 osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None) 582 { 583 expandValue(value, v, mode, NULL, key, requestStack); 584 return true; 585 } else { 586 return false; 587 } 588 } 589 590 void Bootstrap_Impl::expandValue( 591 rtl_uString ** value, rtl::OUString const & text, LookupMode mode, 592 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey, 593 ExpandRequestLink const * requestStack) const 594 { 595 rtl_uString_assign( 596 value, 597 (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ? 598 text : 599 recursivelyExpandMacros( 600 this, text, 601 (mode == LOOKUP_MODE_URE_BOOTSTRAP ? 602 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode), 603 requestFile, requestKey, requestStack)).pData); 604 } 605 606 //---------------------------------------------------------------------------- 607 //---------------------------------------------------------------------------- 608 609 namespace { 610 611 struct bootstrap_map { 612 // map<> may be preferred here, but hash_map<> is implemented fully inline, 613 // thus there is no need to link against the stlport: 614 typedef std::hash_map< 615 rtl::OUString, Bootstrap_Impl *, 616 rtl::OUStringHash, std::equal_to< rtl::OUString >, 617 rtl::Allocator< OUString > > t; 618 619 // get and release must only be called properly synchronized via some mutex 620 // (e.g., osl::Mutex::getGlobalMutex()): 621 622 static t * get() { 623 if (m_map == NULL) { 624 m_map = new t; 625 } 626 return m_map; 627 } 628 629 static void release() { 630 if (m_map != NULL && m_map->empty()) { 631 delete m_map; 632 m_map = NULL; 633 } 634 } 635 636 private: 637 bootstrap_map(); // not defined 638 639 static t * m_map; 640 }; 641 642 bootstrap_map::t * bootstrap_map::m_map = NULL; 643 644 } 645 646 //---------------------------------------------------------------------------- 647 648 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open ( 649 rtl_uString * pIniName 650 ) SAL_THROW_EXTERN_C() 651 { 652 OUString iniName( pIniName ); 653 654 // normalize path 655 FileStatus status( FileStatusMask_FileURL ); 656 DirectoryItem dirItem; 657 if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) || 658 DirectoryItem::E_None != dirItem.getFileStatus( status )) 659 { 660 return 0; 661 } 662 iniName = status.getFileURL(); 663 664 Bootstrap_Impl * that; 665 osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() ); 666 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get(); 667 bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) ); 668 if (iFind == p_bootstrap_map->end()) 669 { 670 bootstrap_map::release(); 671 guard.clear(); 672 that = new Bootstrap_Impl( iniName ); 673 guard.reset(); 674 p_bootstrap_map = bootstrap_map::get(); 675 iFind = p_bootstrap_map->find( iniName ); 676 if (iFind == p_bootstrap_map->end()) 677 { 678 ++that->_nRefCount; 679 ::std::pair< bootstrap_map::t::iterator, bool > insertion( 680 p_bootstrap_map->insert( 681 bootstrap_map::t::value_type( iniName, that ) ) ); 682 OSL_ASSERT( insertion.second ); 683 } 684 else 685 { 686 Bootstrap_Impl * obsolete = that; 687 that = iFind->second; 688 ++that->_nRefCount; 689 guard.clear(); 690 delete obsolete; 691 } 692 } 693 else 694 { 695 that = iFind->second; 696 ++that->_nRefCount; 697 } 698 return static_cast< rtlBootstrapHandle >( that ); 699 } 700 701 //---------------------------------------------------------------------------- 702 703 void SAL_CALL rtl_bootstrap_args_close ( 704 rtlBootstrapHandle handle 705 ) SAL_THROW_EXTERN_C() 706 { 707 if (handle == 0) 708 return; 709 Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle ); 710 711 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() ); 712 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get(); 713 OSL_ASSERT( 714 p_bootstrap_map->find( that->_iniName )->second == that ); 715 --that->_nRefCount; 716 if (that->_nRefCount == 0) 717 { 718 ::std::size_t nLeaking = 8; // only hold up to 8 files statically 719 720 #if OSL_DEBUG_LEVEL == 1 // nonpro 721 nLeaking = 0; 722 #elif OSL_DEBUG_LEVEL > 1 // debug 723 nLeaking = 1; 724 #endif /* OSL_DEBUG_LEVEL */ 725 726 if (p_bootstrap_map->size() > nLeaking) 727 { 728 ::std::size_t erased = p_bootstrap_map->erase( that->_iniName ); 729 if (erased != 1) { 730 OSL_ASSERT( false ); 731 } 732 delete that; 733 } 734 bootstrap_map::release(); 735 } 736 } 737 738 //---------------------------------------------------------------------------- 739 740 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle( 741 rtlBootstrapHandle handle, 742 rtl_uString * pName, 743 rtl_uString ** ppValue, 744 rtl_uString * pDefault 745 ) SAL_THROW_EXTERN_C() 746 { 747 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() ); 748 749 sal_Bool found = sal_False; 750 if(ppValue && pName) 751 { 752 if (handle == 0) 753 handle = get_static_bootstrap_handle(); 754 found = static_cast< Bootstrap_Impl * >( handle )->getValue( 755 pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL ); 756 } 757 758 return found; 759 } 760 761 //---------------------------------------------------------------------------- 762 763 void SAL_CALL rtl_bootstrap_get_iniName_from_handle ( 764 rtlBootstrapHandle handle, 765 rtl_uString ** ppIniName 766 ) SAL_THROW_EXTERN_C() 767 { 768 if(ppIniName) 769 { 770 if(handle) 771 { 772 Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle); 773 rtl_uString_assign(ppIniName, pImpl->_iniName.pData); 774 } 775 else 776 { 777 const OUString & iniName = getIniFileName_Impl(); 778 rtl_uString_assign(ppIniName, iniName.pData); 779 } 780 } 781 } 782 783 //---------------------------------------------------------------------------- 784 785 void SAL_CALL rtl_bootstrap_setIniFileName ( 786 rtl_uString * pName 787 ) SAL_THROW_EXTERN_C() 788 { 789 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() ); 790 OUString & file = getIniFileName_Impl(); 791 file = pName; 792 } 793 794 //---------------------------------------------------------------------------- 795 796 sal_Bool SAL_CALL rtl_bootstrap_get ( 797 rtl_uString * pName, 798 rtl_uString ** ppValue, 799 rtl_uString * pDefault 800 ) SAL_THROW_EXTERN_C() 801 { 802 return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault); 803 } 804 805 //---------------------------------------------------------------------------- 806 807 void SAL_CALL rtl_bootstrap_set ( 808 rtl_uString * pName, 809 rtl_uString * pValue 810 ) SAL_THROW_EXTERN_C() 811 { 812 const OUString name( pName ); 813 const OUString value( pValue ); 814 815 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() ); 816 817 NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get(); 818 NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() ); 819 NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() ); 820 for ( ; iPos != iEnd; ++iPos ) 821 { 822 if (iPos->sName.equals( name )) 823 { 824 iPos->sValue = value; 825 return; 826 } 827 } 828 829 #if OSL_DEBUG_LEVEL > 1 830 OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) ); 831 OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) ); 832 OSL_TRACE( 833 "bootstrap.cxx: explicitly setting: name=%s value=%s\n", 834 cstr_name.getStr(), cstr_value.getStr() ); 835 #endif /* OSL_DEBUG_LEVEL > 1 */ 836 837 r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) ); 838 } 839 840 //---------------------------------------------------------------------------- 841 842 void SAL_CALL rtl_bootstrap_expandMacros_from_handle ( 843 rtlBootstrapHandle handle, 844 rtl_uString ** macro 845 ) SAL_THROW_EXTERN_C() 846 { 847 if (handle == NULL) { 848 handle = get_static_bootstrap_handle(); 849 } 850 OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ), 851 * reinterpret_cast< OUString const * >( macro ), 852 LOOKUP_MODE_NORMAL, NULL ) ); 853 rtl_uString_assign( macro, expanded.pData ); 854 } 855 856 //---------------------------------------------------------------------------- 857 858 void SAL_CALL rtl_bootstrap_expandMacros( 859 rtl_uString ** macro ) 860 SAL_THROW_EXTERN_C() 861 { 862 rtl_bootstrap_expandMacros_from_handle(NULL, macro); 863 } 864 865 void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded ) 866 SAL_THROW_EXTERN_C() 867 { 868 OSL_ASSERT(value != NULL); 869 rtl::OUStringBuffer b; 870 for (sal_Int32 i = 0; i < value->length; ++i) { 871 sal_Unicode c = value->buffer[i]; 872 if (c == '$' || c == '\\') { 873 b.append(sal_Unicode('\\')); 874 } 875 b.append(c); 876 } 877 rtl_uString_assign(encoded, b.makeStringAndClear().pData); 878 } 879 880 namespace { 881 882 int hex(sal_Unicode c) { 883 return 884 c >= '0' && c <= '9' ? c - '0' : 885 c >= 'A' && c <= 'F' ? c - 'A' + 10 : 886 c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1; 887 } 888 889 sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) { 890 OSL_ASSERT( 891 pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL); 892 sal_Unicode c = text[(*pos)++]; 893 if (c == '\\') { 894 int n1, n2, n3, n4; 895 if (*pos < text.getLength() - 4 && text[*pos] == 'u' && 896 ((n1 = hex(text[*pos + 1])) >= 0) && 897 ((n2 = hex(text[*pos + 2])) >= 0) && 898 ((n3 = hex(text[*pos + 3])) >= 0) && 899 ((n4 = hex(text[*pos + 4])) >= 0)) 900 { 901 *pos += 5; 902 *escaped = true; 903 return static_cast< sal_Unicode >( 904 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4); 905 } else if (*pos < text.getLength()) { 906 *escaped = true; 907 return text[(*pos)++]; 908 } 909 } 910 *escaped = false; 911 return c; 912 } 913 914 rtl::OUString lookup( 915 Bootstrap_Impl const * file, LookupMode mode, bool override, 916 rtl::OUString const & key, ExpandRequestLink const * requestStack) 917 { 918 rtl::OUString v; 919 (file == NULL ? get_static_bootstrap_handle() : file)->getValue( 920 key, &v.pData, NULL, mode, override, requestStack); 921 return v; 922 } 923 924 rtl::OUString expandMacros( 925 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode, 926 ExpandRequestLink const * requestStack) 927 { 928 rtl::OUStringBuffer buf; 929 for (sal_Int32 i = 0; i < text.getLength();) { 930 bool escaped; 931 sal_Unicode c = read(text, &i, &escaped); 932 if (escaped || c != '$') { 933 buf.append(c); 934 } else { 935 if (i < text.getLength() && text[i] == '{') { 936 ++i; 937 sal_Int32 p = i; 938 sal_Int32 nesting = 0; 939 rtl::OUString seg[3]; 940 int n = 0; 941 while (i < text.getLength()) { 942 sal_Int32 j = i; 943 c = read(text, &i, &escaped); 944 if (!escaped) { 945 switch (c) { 946 case '{': 947 ++nesting; 948 break; 949 case '}': 950 if (nesting == 0) { 951 seg[n++] = text.copy(p, j - p); 952 goto done; 953 } else { 954 --nesting; 955 } 956 break; 957 case ':': 958 if (nesting == 0 && n < 2) { 959 seg[n++] = text.copy(p, j - p); 960 p = i; 961 } 962 break; 963 } 964 } 965 } 966 done: 967 for (int j = 0; j < n; ++j) { 968 seg[j] = expandMacros(file, seg[j], mode, requestStack); 969 } 970 if (n == 3 && seg[1].getLength() == 0) { 971 // For backward compatibility, treat ${file::key} the same 972 // as just ${file:key}: 973 seg[1] = seg[2]; 974 n = 2; 975 } 976 if (n == 1) { 977 buf.append(lookup(file, mode, false, seg[0], requestStack)); 978 } else if (n == 2) { 979 if (seg[0].equalsAsciiL( 980 RTL_CONSTASCII_STRINGPARAM(".link"))) 981 { 982 osl::File f(seg[1]); 983 rtl::ByteSequence seq; 984 rtl::OUString line; 985 rtl::OUString url; 986 // Silently ignore any errors (is that good?): 987 if (f.open(OpenFlag_Read) == osl::FileBase::E_None && 988 f.readLine(seq) == osl::FileBase::E_None && 989 rtl_convertStringToUString( 990 &line.pData, 991 reinterpret_cast< char const * >( 992 seq.getConstArray()), 993 seq.getLength(), RTL_TEXTENCODING_UTF8, 994 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | 995 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | 996 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) && 997 (osl::File::getFileURLFromSystemPath(line, url) == 998 osl::FileBase::E_None)) 999 { 1000 try { 1001 buf.append( 1002 rtl::Uri::convertRelToAbs(seg[1], url)); 1003 } catch (rtl::MalformedUriException &) {} 1004 } 1005 } else { 1006 buf.append( 1007 lookup( 1008 static_cast< Bootstrap_Impl * >( 1009 rtl::Bootstrap(seg[0]).getHandle()), 1010 mode, false, seg[1], requestStack)); 1011 } 1012 } else if (seg[0].equalsAsciiL( 1013 RTL_CONSTASCII_STRINGPARAM(".override"))) 1014 { 1015 rtl::Bootstrap b(seg[1]); 1016 Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >( 1017 b.getHandle()); 1018 buf.append( 1019 lookup(f, mode, f != NULL, seg[2], requestStack)); 1020 } else { 1021 // Going through osl::Profile, this code erroneously does 1022 // not recursively expand macros in the resulting 1023 // replacement text (and if it did, it would fail to detect 1024 // cycles that pass through here): 1025 buf.append( 1026 rtl::OStringToOUString( 1027 osl::Profile(seg[0]).readString( 1028 rtl::OUStringToOString( 1029 seg[1], RTL_TEXTENCODING_UTF8), 1030 rtl::OUStringToOString( 1031 seg[2], RTL_TEXTENCODING_UTF8), 1032 rtl::OString()), 1033 RTL_TEXTENCODING_UTF8)); 1034 } 1035 } else { 1036 rtl::OUStringBuffer kbuf; 1037 for (; i < text.getLength();) { 1038 sal_Int32 j = i; 1039 c = read(text, &j, &escaped); 1040 if (!escaped && 1041 (c == ' ' || c == '$' || c == '-' || c == '/' || 1042 c == ';' || c == '\\')) 1043 { 1044 break; 1045 } 1046 kbuf.append(c); 1047 i = j; 1048 } 1049 buf.append( 1050 lookup( 1051 file, mode, false, kbuf.makeStringAndClear(), 1052 requestStack)); 1053 } 1054 } 1055 } 1056 return buf.makeStringAndClear(); 1057 } 1058 1059 } 1060