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