xref: /aoo41x/main/sal/rtl/source/bootstrap.cxx (revision 87d2adbc)
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