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