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