merge the formfield patch from ooo-build
[ooovba.git] / sal / rtl / source / bootstrap.cxx
blob4081dc89bdfd7a077ff26e36de9267168be426ca
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: bootstrap.cxx,v $
10 * $Revision: 1.43.20.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sal.hxx"
34 #include "rtl/bootstrap.h"
35 #include "rtl/bootstrap.hxx"
36 #include <osl/diagnose.h>
37 #include <osl/module.h>
38 #include <osl/process.h>
39 #include <osl/file.hxx>
40 #include <osl/mutex.hxx>
41 #include <osl/profile.hxx>
42 #include <osl/security.hxx>
43 #include <rtl/alloc.h>
44 #include <rtl/string.hxx>
45 #include <rtl/ustrbuf.hxx>
46 #include <rtl/ustring.hxx>
47 #include <rtl/byteseq.hxx>
48 #include <rtl/instance.hxx>
49 #include <rtl/malformeduriexception.hxx>
50 #include <rtl/uri.hxx>
51 #include "rtl/allocator.hxx"
53 #include "macro.hxx"
55 #include <hash_map>
56 #include <list>
58 #define MY_STRING_(x) # x
59 #define MY_STRING(x) MY_STRING_(x)
61 //----------------------------------------------------------------------------
63 using osl::DirectoryItem;
64 using osl::FileStatus;
66 using rtl::OString;
67 using rtl::OUString;
68 using rtl::OUStringToOString;
70 struct Bootstrap_Impl;
72 namespace {
74 static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
76 bool isPathnameUrl(rtl::OUString const & url) {
77 return url.matchIgnoreAsciiCaseAsciiL(
78 RTL_CONSTASCII_STRINGPARAM(VND_SUN_STAR_PATHNAME));
81 bool resolvePathnameUrl(rtl::OUString * url) {
82 OSL_ASSERT(url != NULL);
83 if (!isPathnameUrl(*url) ||
84 (osl::FileBase::getFileURLFromSystemPath(
85 url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
86 osl::FileBase::E_None))
88 return true;
89 } else {
90 *url = rtl::OUString();
91 return false;
95 enum LookupMode {
96 LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
97 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
99 struct ExpandRequestLink {
100 ExpandRequestLink const * next;
101 Bootstrap_Impl const * file;
102 rtl::OUString key;
105 rtl::OUString expandMacros(
106 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
107 ExpandRequestLink const * requestStack);
109 rtl::OUString recursivelyExpandMacros(
110 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
111 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
112 ExpandRequestLink const * requestStack)
114 for (; requestStack != NULL; requestStack = requestStack->next) {
115 if (requestStack->file == requestFile &&
116 requestStack->key == requestKey)
118 return rtl::OUString(
119 RTL_CONSTASCII_USTRINGPARAM("***RECURSION DETECTED***"));
122 ExpandRequestLink link = { requestStack, requestFile, requestKey };
123 return expandMacros(file, text, mode, &link);
128 //----------------------------------------------------------------------------
130 struct rtl_bootstrap_NameValue
132 OUString sName;
133 OUString sValue;
135 inline rtl_bootstrap_NameValue() SAL_THROW( () )
137 inline rtl_bootstrap_NameValue(
138 OUString const & name, OUString const & value ) SAL_THROW( () )
139 : sName( name ),
140 sValue( value )
144 typedef std::list<
145 rtl_bootstrap_NameValue,
146 rtl::Allocator< rtl_bootstrap_NameValue >
147 > NameValueList;
149 bool find(
150 NameValueList const & list, rtl::OUString const & key,
151 rtl::OUString * value)
153 OSL_ASSERT(value != NULL);
154 for (NameValueList::const_iterator i(list.begin()); i != list.end(); ++i) {
155 if (i->sName == key) {
156 *value = i->sValue;
157 return true;
160 return false;
163 namespace {
164 struct rtl_bootstrap_set_list :
165 public rtl::Static< NameValueList, rtl_bootstrap_set_list > {};
168 //----------------------------------------------------------------------------
170 static sal_Bool getFromCommandLineArgs(
171 rtl::OUString const & key, rtl::OUString * value )
173 OSL_ASSERT(value != NULL);
174 static NameValueList *pNameValueList = 0;
175 if( ! pNameValueList )
177 static NameValueList nameValueList;
179 sal_Int32 nArgCount = osl_getCommandArgCount();
180 for(sal_Int32 i = 0; i < nArgCount; ++ i)
182 rtl_uString *pArg = 0;
183 osl_getCommandArg( i, &pArg );
184 if( ('-' == pArg->buffer[0] || '/' == pArg->buffer[0] ) &&
185 'e' == pArg->buffer[1] &&
186 'n' == pArg->buffer[2] &&
187 'v' == pArg->buffer[3] &&
188 ':' == pArg->buffer[4] )
190 sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
191 if( nIndex >= 0 )
194 rtl_bootstrap_NameValue nameValue;
195 nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 );
196 nameValue.sValue = OUString( &(pArg->buffer[nIndex+1]) );
197 if( i == nArgCount-1 &&
198 nameValue.sValue.getLength() &&
199 nameValue.sValue[nameValue.sValue.getLength()-1] == 13 )
201 // avoid the 13 linefeed for the last argument,
202 // when the executable is started from a script,
203 // that was edited on windows
204 nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
206 nameValueList.push_back( nameValue );
209 rtl_uString_release( pArg );
211 pNameValueList = &nameValueList;
214 sal_Bool found = sal_False;
216 for( NameValueList::iterator ii = pNameValueList->begin() ;
217 ii != pNameValueList->end() ;
218 ++ii )
220 if( (*ii).sName.equals(key) )
222 *value = (*ii).sValue;
223 found = sal_True;
224 break;
228 return found;
231 //----------------------------------------------------------------------------
233 extern "C" oslProcessError SAL_CALL osl_bootstrap_getExecutableFile_Impl (
234 rtl_uString ** ppFileURL) SAL_THROW_EXTERN_C();
236 inline void getExecutableFile_Impl (rtl_uString ** ppFileURL)
238 osl_bootstrap_getExecutableFile_Impl (ppFileURL);
241 //----------------------------------------------------------------------------
243 static void getExecutableDirectory_Impl (rtl_uString ** ppDirURL)
245 OUString fileName;
246 getExecutableFile_Impl (&(fileName.pData));
248 sal_Int32 nDirEnd = fileName.lastIndexOf('/');
249 OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
251 rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
254 //----------------------------------------------------------------------------
256 static OUString & getIniFileName_Impl()
258 static OUString *pStaticName = 0;
259 if( ! pStaticName )
261 OUString fileName;
263 if(getFromCommandLineArgs(
264 OUString(RTL_CONSTASCII_USTRINGPARAM("INIFILENAME")), &fileName))
266 resolvePathnameUrl(&fileName);
268 else
270 getExecutableFile_Impl (&(fileName.pData));
272 // get rid of a potential executable extension
273 OUString progExt (RTL_CONSTASCII_USTRINGPARAM(".bin"));
274 if(fileName.getLength() > progExt.getLength()
275 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
276 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
278 progExt = OUString::createFromAscii(".exe");
279 if(fileName.getLength() > progExt.getLength()
280 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
281 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
283 // append config file suffix
284 fileName += OUString(RTL_CONSTASCII_USTRINGPARAM(SAL_CONFIGFILE("")));
287 OUString workDir;
288 osl_getProcessWorkingDir(&workDir.pData);
289 osl::FileBase::getAbsoluteFileURL(workDir, fileName, fileName);
291 static OUString theFileName;
292 if(fileName.getLength())
293 theFileName = fileName;
295 pStaticName = &theFileName;
298 return *pStaticName;
301 //----------------------------------------------------------------------------
303 static inline bool path_exists( OUString const & path )
305 DirectoryItem dirItem;
306 return (DirectoryItem::E_None == DirectoryItem::get( path, dirItem ));
309 //----------------------------------------------------------------------------
310 // #111772#
311 // ensure the given file url has no final slash
313 inline void EnsureNoFinalSlash (rtl::OUString & url)
315 sal_Int32 i = url.getLength();
316 if (i > 0 && url[i - 1] == '/') {
317 url = url.copy(0, i - 1);
321 //----------------------------------------------------------------------------
322 //----------------------------------------------------------------------------
324 struct Bootstrap_Impl
326 sal_Int32 _nRefCount;
327 Bootstrap_Impl * _base_ini;
329 NameValueList _nameValueList;
330 OUString _iniName;
332 explicit Bootstrap_Impl (OUString const & rIniName);
333 ~Bootstrap_Impl();
335 static void * operator new (std::size_t n) SAL_THROW(())
336 { return rtl_allocateMemory (sal_uInt32(n)); }
337 static void operator delete (void * p , std::size_t) SAL_THROW(())
338 { rtl_freeMemory (p); }
340 bool getValue(
341 rtl::OUString const & key, rtl_uString ** value,
342 rtl_uString * defaultValue, LookupMode mode, bool override,
343 ExpandRequestLink const * requestStack) const;
344 bool getDirectValue(
345 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
346 ExpandRequestLink const * requestStack) const;
347 bool getAmbienceValue(
348 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
349 ExpandRequestLink const * requestStack) const;
350 void expandValue(
351 rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
352 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
353 ExpandRequestLink const * requestStack) const;
356 //----------------------------------------------------------------------------
358 Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
359 : _nRefCount( 0 ),
360 _base_ini( 0 ),
361 _iniName (rIniName)
363 OUString base_ini( getIniFileName_Impl() );
364 // normalize path
365 FileStatus status( FileStatusMask_FileURL );
366 DirectoryItem dirItem;
367 if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) &&
368 DirectoryItem::E_None == dirItem.getFileStatus( status ))
370 base_ini = status.getFileURL();
371 if (! rIniName.equals( base_ini ))
373 _base_ini = static_cast< Bootstrap_Impl * >(
374 rtl_bootstrap_args_open( base_ini.pData ) );
378 #if OSL_DEBUG_LEVEL > 1
379 OString sFile = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
380 OSL_TRACE(__FILE__" -- Bootstrap_Impl() - %s\n", sFile.getStr());
381 #endif /* OSL_DEBUG_LEVEL > 1 */
383 oslFileHandle handle;
384 if (_iniName.getLength() &&
385 osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read))
387 rtl::ByteSequence seq;
389 while (osl_File_E_None == osl_readLine(handle , (sal_Sequence **)&seq))
391 OString line( (const sal_Char *) seq.getConstArray(), seq.getLength() );
392 sal_Int32 nIndex = line.indexOf('=');
393 if (nIndex >= 1)
395 struct rtl_bootstrap_NameValue nameValue;
396 nameValue.sName = OStringToOUString(
397 line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US );
398 nameValue.sValue = OStringToOUString(
399 line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 );
401 #if OSL_DEBUG_LEVEL > 1
402 OString name_tmp = OUStringToOString(nameValue.sName, RTL_TEXTENCODING_ASCII_US);
403 OString value_tmp = OUStringToOString(nameValue.sValue, RTL_TEXTENCODING_UTF8);
404 OSL_TRACE(
405 __FILE__" -- pushing: name=%s value=%s\n",
406 name_tmp.getStr(), value_tmp.getStr() );
407 #endif /* OSL_DEBUG_LEVEL > 1 */
409 _nameValueList.push_back(nameValue);
412 osl_closeFile(handle);
414 #if OSL_DEBUG_LEVEL > 1
415 else
417 OString file_tmp = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
418 OSL_TRACE( __FILE__" -- couldn't open file: %s", file_tmp.getStr() );
420 #endif /* OSL_DEBUG_LEVEL > 1 */
423 //----------------------------------------------------------------------------
425 Bootstrap_Impl::~Bootstrap_Impl()
427 if (_base_ini != 0)
428 rtl_bootstrap_args_close( _base_ini );
431 //----------------------------------------------------------------------------
433 namespace {
435 Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(())
437 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
438 static Bootstrap_Impl * s_handle = 0;
439 if (s_handle == 0)
441 OUString iniName (getIniFileName_Impl());
442 s_handle = static_cast< Bootstrap_Impl * >(
443 rtl_bootstrap_args_open( iniName.pData ) );
444 if (s_handle == 0)
446 Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
447 ++that->_nRefCount;
448 s_handle = that;
451 return s_handle;
454 struct FundamentalIniData {
455 rtlBootstrapHandle ini;
457 FundamentalIniData() {
458 OUString uri;
459 ini =
460 ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
461 getValue(
462 rtl::OUString(
463 RTL_CONSTASCII_USTRINGPARAM("URE_BOOTSTRAP")),
464 &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) &&
465 resolvePathnameUrl(&uri))
466 ? rtl_bootstrap_args_open(uri.pData) : NULL;
469 ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
471 private:
472 FundamentalIniData(FundamentalIniData &); // not defined
473 void operator =(FundamentalIniData &); // not defined
476 struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
481 bool Bootstrap_Impl::getValue(
482 rtl::OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
483 LookupMode mode, bool override, ExpandRequestLink const * requestStack)
484 const
486 if (mode == LOOKUP_MODE_NORMAL &&
487 key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URE_BOOTSTRAP")))
489 mode = LOOKUP_MODE_URE_BOOTSTRAP;
491 if (override && getDirectValue(key, value, mode, requestStack)) {
492 return true;
494 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_OS"))) {
495 rtl_uString_assign(
496 value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(THIS_OS)).pData);
497 return true;
499 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_ARCH"))) {
500 rtl_uString_assign(
501 value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(THIS_ARCH)).pData);
502 return true;
504 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_CPPU_ENV"))) {
505 rtl_uString_assign(
506 value,
507 (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(MY_STRING(CPPU_ENV))).
508 pData));
509 return true;
511 if (key.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("ORIGIN"))) {
512 rtl_uString_assign(
513 value,
514 _iniName.copy(
515 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
516 return true;
518 if (getAmbienceValue(key, value, mode, requestStack)) {
519 return true;
521 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSUSERCONFIG"))) {
522 rtl::OUString v;
523 bool b = osl::Security().getConfigDir(v);
524 EnsureNoFinalSlash(v);
525 rtl_uString_assign(value, v.pData);
526 return b;
528 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSUSERHOME"))) {
529 rtl::OUString v;
530 bool b = osl::Security().getHomeDir(v);
531 EnsureNoFinalSlash(v);
532 rtl_uString_assign(value, v.pData);
533 return b;
535 if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSBINDIR"))) {
536 getExecutableDirectory_Impl(value);
537 return true;
539 if (_base_ini != NULL &&
540 _base_ini->getDirectValue(key, value, mode, requestStack))
542 return true;
544 if (!override && getDirectValue(key, value, mode, requestStack)) {
545 return true;
547 if (mode == LOOKUP_MODE_NORMAL) {
548 FundamentalIniData const & d = FundamentalIni::get();
549 Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
550 if (b != NULL && b != this &&
551 b->getDirectValue(key, value, mode, requestStack))
553 return true;
556 if (defaultValue != NULL) {
557 rtl_uString_assign(value, defaultValue);
558 return true;
560 rtl_uString_new(value);
561 return false;
564 bool Bootstrap_Impl::getDirectValue(
565 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
566 ExpandRequestLink const * requestStack) const
568 rtl::OUString v;
569 if (find(_nameValueList, key, &v)) {
570 expandValue(value, v, mode, this, key, requestStack);
571 return true;
572 } else {
573 return false;
577 bool Bootstrap_Impl::getAmbienceValue(
578 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
579 ExpandRequestLink const * requestStack) const
581 rtl::OUString v;
582 bool f;
584 osl::MutexGuard g(osl::Mutex::getGlobalMutex());
585 f = find(rtl_bootstrap_set_list::get(), key, &v);
587 if (f || getFromCommandLineArgs(key, &v) ||
588 osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
590 expandValue(value, v, mode, NULL, key, requestStack);
591 return true;
592 } else {
593 return false;
597 void Bootstrap_Impl::expandValue(
598 rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
599 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
600 ExpandRequestLink const * requestStack) const
602 rtl_uString_assign(
603 value,
604 (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
605 text :
606 recursivelyExpandMacros(
607 this, text,
608 (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
609 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
610 requestFile, requestKey, requestStack)).pData);
613 //----------------------------------------------------------------------------
614 //----------------------------------------------------------------------------
616 namespace {
618 struct bootstrap_map {
619 // map<> may be preferred here, but hash_map<> is implemented fully inline,
620 // thus there is no need to link against the stlport:
621 typedef std::hash_map<
622 rtl::OUString, Bootstrap_Impl *,
623 rtl::OUStringHash, std::equal_to< rtl::OUString >,
624 rtl::Allocator< OUString > > t;
626 // get and release must only be called properly synchronized via some mutex
627 // (e.g., osl::Mutex::getGlobalMutex()):
629 static t * get() {
630 if (m_map == NULL) {
631 m_map = new t;
633 return m_map;
636 static void release() {
637 if (m_map != NULL && m_map->empty()) {
638 delete m_map;
639 m_map = NULL;
643 private:
644 bootstrap_map(); // not defined
646 static t * m_map;
649 bootstrap_map::t * bootstrap_map::m_map = NULL;
653 //----------------------------------------------------------------------------
655 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
656 rtl_uString * pIniName
657 ) SAL_THROW_EXTERN_C()
659 OUString workDir;
660 OUString iniName( pIniName );
662 osl_getProcessWorkingDir( &workDir.pData );
663 osl::FileBase::getAbsoluteFileURL( workDir, iniName, iniName );
665 // normalize path
666 FileStatus status( FileStatusMask_FileURL );
667 DirectoryItem dirItem;
668 if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
669 DirectoryItem::E_None != dirItem.getFileStatus( status ))
671 return 0;
673 iniName = status.getFileURL();
675 Bootstrap_Impl * that;
676 osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
677 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
678 bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
679 if (iFind == p_bootstrap_map->end())
681 bootstrap_map::release();
682 guard.clear();
683 that = new Bootstrap_Impl( iniName );
684 guard.reset();
685 p_bootstrap_map = bootstrap_map::get();
686 iFind = p_bootstrap_map->find( iniName );
687 if (iFind == p_bootstrap_map->end())
689 ++that->_nRefCount;
690 ::std::pair< bootstrap_map::t::iterator, bool > insertion(
691 p_bootstrap_map->insert(
692 bootstrap_map::t::value_type( iniName, that ) ) );
693 OSL_ASSERT( insertion.second );
695 else
697 Bootstrap_Impl * obsolete = that;
698 that = iFind->second;
699 ++that->_nRefCount;
700 guard.clear();
701 delete obsolete;
704 else
706 that = iFind->second;
707 ++that->_nRefCount;
709 return static_cast< rtlBootstrapHandle >( that );
712 //----------------------------------------------------------------------------
714 void SAL_CALL rtl_bootstrap_args_close (
715 rtlBootstrapHandle handle
716 ) SAL_THROW_EXTERN_C()
718 if (handle == 0)
719 return;
720 Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
722 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
723 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
724 OSL_ASSERT(
725 p_bootstrap_map->find( that->_iniName )->second == that );
726 --that->_nRefCount;
727 if (that->_nRefCount == 0)
729 ::std::size_t nLeaking = 8; // only hold up to 8 files statically
731 #if OSL_DEBUG_LEVEL == 1 // nonpro
732 nLeaking = 0;
733 #elif OSL_DEBUG_LEVEL > 1 // debug
734 nLeaking = 1;
735 #endif /* OSL_DEBUG_LEVEL */
737 if (p_bootstrap_map->size() > nLeaking)
739 ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
740 if (erased != 1) {
741 OSL_ASSERT( false );
743 delete that;
745 bootstrap_map::release();
749 //----------------------------------------------------------------------------
751 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
752 rtlBootstrapHandle handle,
753 rtl_uString * pName,
754 rtl_uString ** ppValue,
755 rtl_uString * pDefault
756 ) SAL_THROW_EXTERN_C()
758 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
760 sal_Bool found = sal_False;
761 if(ppValue && pName)
763 if (handle == 0)
764 handle = get_static_bootstrap_handle();
765 found = static_cast< Bootstrap_Impl * >( handle )->getValue(
766 pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
769 return found;
772 //----------------------------------------------------------------------------
774 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
775 rtlBootstrapHandle handle,
776 rtl_uString ** ppIniName
777 ) SAL_THROW_EXTERN_C()
779 if(ppIniName)
781 if(handle)
783 Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
784 rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
786 else
788 const OUString & iniName = getIniFileName_Impl();
789 rtl_uString_assign(ppIniName, iniName.pData);
794 //----------------------------------------------------------------------------
796 void SAL_CALL rtl_bootstrap_setIniFileName (
797 rtl_uString * pName
798 ) SAL_THROW_EXTERN_C()
800 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
801 OUString & file = getIniFileName_Impl();
802 file = pName;
805 //----------------------------------------------------------------------------
807 sal_Bool SAL_CALL rtl_bootstrap_get (
808 rtl_uString * pName,
809 rtl_uString ** ppValue,
810 rtl_uString * pDefault
811 ) SAL_THROW_EXTERN_C()
813 return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
816 //----------------------------------------------------------------------------
818 void SAL_CALL rtl_bootstrap_set (
819 rtl_uString * pName,
820 rtl_uString * pValue
821 ) SAL_THROW_EXTERN_C()
823 OUString const & name = *reinterpret_cast< OUString const * >( &pName );
824 OUString const & value = *reinterpret_cast< OUString const * >( &pValue );
826 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
828 NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
829 NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
830 NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
831 for ( ; iPos != iEnd; ++iPos )
833 if (iPos->sName.equals( name ))
835 iPos->sValue = value;
836 return;
840 #if OSL_DEBUG_LEVEL > 1
841 OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
842 OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) );
843 OSL_TRACE(
844 "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
845 cstr_name.getStr(), cstr_value.getStr() );
846 #endif /* OSL_DEBUG_LEVEL > 1 */
848 r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
851 //----------------------------------------------------------------------------
853 void SAL_CALL rtl_bootstrap_expandMacros_from_handle (
854 rtlBootstrapHandle handle,
855 rtl_uString ** macro
856 ) SAL_THROW_EXTERN_C()
858 if (handle == NULL) {
859 handle = get_static_bootstrap_handle();
861 OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
862 * reinterpret_cast< OUString const * >( macro ),
863 LOOKUP_MODE_NORMAL, NULL ) );
864 rtl_uString_assign( macro, expanded.pData );
867 //----------------------------------------------------------------------------
869 void SAL_CALL rtl_bootstrap_expandMacros(
870 rtl_uString ** macro )
871 SAL_THROW_EXTERN_C()
873 rtl_bootstrap_expandMacros_from_handle(NULL, macro);
876 void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
877 SAL_THROW_EXTERN_C()
879 OSL_ASSERT(value != NULL);
880 rtl::OUStringBuffer b;
881 for (sal_Int32 i = 0; i < value->length; ++i) {
882 sal_Unicode c = value->buffer[i];
883 if (c == '$' || c == '\\') {
884 b.append(sal_Unicode('\\'));
886 b.append(c);
888 rtl_uString_assign(encoded, b.makeStringAndClear().pData);
891 namespace {
893 int hex(sal_Unicode c) {
894 return
895 c >= '0' && c <= '9' ? c - '0' :
896 c >= 'A' && c <= 'F' ? c - 'A' + 10 :
897 c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
900 sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) {
901 OSL_ASSERT(
902 pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
903 sal_Unicode c = text[(*pos)++];
904 if (c == '\\') {
905 int n1, n2, n3, n4;
906 if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
907 ((n1 = hex(text[*pos + 1])) >= 0) &&
908 ((n2 = hex(text[*pos + 2])) >= 0) &&
909 ((n3 = hex(text[*pos + 3])) >= 0) &&
910 ((n4 = hex(text[*pos + 4])) >= 0))
912 *pos += 5;
913 *escaped = true;
914 return static_cast< sal_Unicode >(
915 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
916 } else if (*pos < text.getLength()) {
917 *escaped = true;
918 return text[(*pos)++];
921 *escaped = false;
922 return c;
925 rtl::OUString lookup(
926 Bootstrap_Impl const * file, LookupMode mode, bool override,
927 rtl::OUString const & key, ExpandRequestLink const * requestStack)
929 rtl::OUString v;
930 (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
931 key, &v.pData, NULL, mode, override, requestStack);
932 return v;
935 rtl::OUString expandMacros(
936 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
937 ExpandRequestLink const * requestStack)
939 rtl::OUStringBuffer buf;
940 for (sal_Int32 i = 0; i < text.getLength();) {
941 bool escaped;
942 sal_Unicode c = read(text, &i, &escaped);
943 if (escaped || c != '$') {
944 buf.append(c);
945 } else {
946 if (i < text.getLength() && text[i] == '{') {
947 ++i;
948 sal_Int32 p = i;
949 sal_Int32 nesting = 0;
950 rtl::OUString seg[3];
951 int n = 0;
952 while (i < text.getLength()) {
953 sal_Int32 j = i;
954 c = read(text, &i, &escaped);
955 if (!escaped) {
956 switch (c) {
957 case '{':
958 ++nesting;
959 break;
960 case '}':
961 if (nesting == 0) {
962 seg[n++] = text.copy(p, j - p);
963 goto done;
964 } else {
965 --nesting;
967 break;
968 case ':':
969 if (nesting == 0 && n < 2) {
970 seg[n++] = text.copy(p, j - p);
971 p = i;
973 break;
977 done:
978 for (int j = 0; j < n; ++j) {
979 seg[j] = expandMacros(file, seg[j], mode, requestStack);
981 if (n == 3 && seg[1].getLength() == 0) {
982 // For backward compatibility, treat ${file::key} the same
983 // as just ${file:key}:
984 seg[1] = seg[2];
985 n = 2;
987 if (n == 1) {
988 buf.append(lookup(file, mode, false, seg[0], requestStack));
989 } else if (n == 2) {
990 if (seg[0].equalsAsciiL(
991 RTL_CONSTASCII_STRINGPARAM(".link")))
993 osl::File f(seg[1]);
994 rtl::ByteSequence seq;
995 rtl::OUString line;
996 rtl::OUString url;
997 // Silently ignore any errors (is that good?):
998 if (f.open(OpenFlag_Read) == osl::FileBase::E_None &&
999 f.readLine(seq) == osl::FileBase::E_None &&
1000 rtl_convertStringToUString(
1001 &line.pData,
1002 reinterpret_cast< char const * >(
1003 seq.getConstArray()),
1004 seq.getLength(), RTL_TEXTENCODING_UTF8,
1005 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
1006 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
1007 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) &&
1008 (osl::File::getFileURLFromSystemPath(line, url) ==
1009 osl::FileBase::E_None))
1011 try {
1012 buf.append(
1013 rtl::Uri::convertRelToAbs(seg[1], url));
1014 } catch (rtl::MalformedUriException &) {}
1016 } else {
1017 buf.append(
1018 lookup(
1019 static_cast< Bootstrap_Impl * >(
1020 rtl::Bootstrap(seg[0]).getHandle()),
1021 mode, false, seg[1], requestStack));
1023 } else if (seg[0].equalsAsciiL(
1024 RTL_CONSTASCII_STRINGPARAM(".override")))
1026 rtl::Bootstrap b(seg[1]);
1027 Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(
1028 b.getHandle());
1029 buf.append(
1030 lookup(f, mode, f != NULL, seg[2], requestStack));
1031 } else {
1032 // Going through osl::Profile, this code erroneously does
1033 // not recursively expand macros in the resulting
1034 // replacement text (and if it did, it would fail to detect
1035 // cycles that pass through here):
1036 buf.append(
1037 rtl::OStringToOUString(
1038 osl::Profile(seg[0]).readString(
1039 rtl::OUStringToOString(
1040 seg[1], RTL_TEXTENCODING_UTF8),
1041 rtl::OUStringToOString(
1042 seg[2], RTL_TEXTENCODING_UTF8),
1043 rtl::OString()),
1044 RTL_TEXTENCODING_UTF8));
1046 } else {
1047 rtl::OUStringBuffer kbuf;
1048 for (; i < text.getLength();) {
1049 sal_Int32 j = i;
1050 c = read(text, &j, &escaped);
1051 if (!escaped &&
1052 (c == ' ' || c == '$' || c == '-' || c == '/' ||
1053 c == ';' || c == '\\'))
1055 break;
1057 kbuf.append(c);
1058 i = j;
1060 buf.append(
1061 lookup(
1062 file, mode, false, kbuf.makeStringAndClear(),
1063 requestStack));
1067 return buf.makeStringAndClear();