xref: /aoo41x/main/sal/rtl/source/bootstrap.cxx (revision b597708b)
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     typedef std::hash_map<
609         rtl::OUString, Bootstrap_Impl *,
610         rtl::OUStringHash, std::equal_to< rtl::OUString >,
611         rtl::Allocator< OUString > > t;
612 
613     // get and release must only be called properly synchronized via some mutex
614     // (e.g., osl::Mutex::getGlobalMutex()):
615 
616     static t * get() {
617         if (m_map == NULL) {
618             m_map = new t;
619         }
620         return m_map;
621     }
622 
623     static void release() {
624         if (m_map != NULL && m_map->empty()) {
625             delete m_map;
626             m_map = NULL;
627         }
628     }
629 
630 private:
631     bootstrap_map(); // not defined
632 
633     static t * m_map;
634 };
635 
636 bootstrap_map::t * bootstrap_map::m_map = NULL;
637 
638 }
639 
640 //----------------------------------------------------------------------------
641 
642 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
643 	rtl_uString * pIniName
644 ) SAL_THROW_EXTERN_C()
645 {
646 	OUString iniName( pIniName );
647 
648     // normalize path
649     FileStatus status( FileStatusMask_FileURL );
650     DirectoryItem dirItem;
651     if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
652         DirectoryItem::E_None != dirItem.getFileStatus( status ))
653     {
654         return 0;
655     }
656     iniName = status.getFileURL();
657 
658     Bootstrap_Impl * that;
659     osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
660     bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
661     bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
662     if (iFind == p_bootstrap_map->end())
663     {
664         bootstrap_map::release();
665         guard.clear();
666         that = new Bootstrap_Impl( iniName );
667         guard.reset();
668         p_bootstrap_map = bootstrap_map::get();
669         iFind = p_bootstrap_map->find( iniName );
670         if (iFind == p_bootstrap_map->end())
671         {
672             ++that->_nRefCount;
673             ::std::pair< bootstrap_map::t::iterator, bool > insertion(
674                 p_bootstrap_map->insert(
675                     bootstrap_map::t::value_type( iniName, that ) ) );
676             OSL_ASSERT( insertion.second );
677         }
678         else
679         {
680             Bootstrap_Impl * obsolete = that;
681             that = iFind->second;
682             ++that->_nRefCount;
683             guard.clear();
684             delete obsolete;
685         }
686     }
687     else
688     {
689         that = iFind->second;
690         ++that->_nRefCount;
691     }
692 	return static_cast< rtlBootstrapHandle >( that );
693 }
694 
695 //----------------------------------------------------------------------------
696 
697 void SAL_CALL rtl_bootstrap_args_close (
698 	rtlBootstrapHandle handle
699 ) SAL_THROW_EXTERN_C()
700 {
701     if (handle == 0)
702         return;
703     Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
704 
705     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
706     bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
707     OSL_ASSERT(
708         p_bootstrap_map->find( that->_iniName )->second == that );
709     --that->_nRefCount;
710     if (that->_nRefCount == 0)
711     {
712         ::std::size_t nLeaking = 8; // only hold up to 8 files statically
713 
714 #if OSL_DEBUG_LEVEL == 1 // nonpro
715         nLeaking = 0;
716 #elif OSL_DEBUG_LEVEL > 1 // debug
717         nLeaking = 1;
718 #endif /* OSL_DEBUG_LEVEL */
719 
720         if (p_bootstrap_map->size() > nLeaking)
721         {
722             ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
723             if (erased != 1) {
724                 OSL_ASSERT( false );
725             }
726             delete that;
727         }
728         bootstrap_map::release();
729     }
730 }
731 
732 //----------------------------------------------------------------------------
733 
734 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
735     rtlBootstrapHandle handle,
736 	rtl_uString      * pName,
737 	rtl_uString     ** ppValue,
738     rtl_uString      * pDefault
739 ) SAL_THROW_EXTERN_C()
740 {
741 	osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
742 
743 	sal_Bool found = sal_False;
744 	if(ppValue && pName)
745 	{
746 		if (handle == 0)
747             handle = get_static_bootstrap_handle();
748         found = static_cast< Bootstrap_Impl * >( handle )->getValue(
749             pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
750 	}
751 
752 	return found;
753 }
754 
755 //----------------------------------------------------------------------------
756 
757 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
758     rtlBootstrapHandle handle,
759 	rtl_uString     ** ppIniName
760 ) SAL_THROW_EXTERN_C()
761 {
762 	if(ppIniName)
763     {
764 		if(handle)
765         {
766 			Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
767 			rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
768         }
769 		else
770         {
771 			const OUString & iniName = getIniFileName_Impl();
772 			rtl_uString_assign(ppIniName, iniName.pData);
773 		}
774     }
775 }
776 
777 //----------------------------------------------------------------------------
778 
779 void SAL_CALL rtl_bootstrap_setIniFileName (
780 	rtl_uString * pName
781 ) SAL_THROW_EXTERN_C()
782 {
783 	osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
784 	OUString & file = getIniFileName_Impl();
785 	file = pName;
786 }
787 
788 //----------------------------------------------------------------------------
789 
790 sal_Bool SAL_CALL rtl_bootstrap_get (
791     rtl_uString  * pName,
792 	rtl_uString ** ppValue,
793 	rtl_uString  * pDefault
794 ) SAL_THROW_EXTERN_C()
795 {
796 	return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
797 }
798 
799 //----------------------------------------------------------------------------
800 
801 void SAL_CALL rtl_bootstrap_set (
802 	rtl_uString * pName,
803 	rtl_uString * pValue
804 ) SAL_THROW_EXTERN_C()
805 {
806     const OUString name( pName );
807     const OUString value( pValue );
808 
809     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
810 
811     NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
812     NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
813     NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
814     for ( ; iPos != iEnd; ++iPos )
815     {
816         if (iPos->sName.equals( name ))
817         {
818             iPos->sValue = value;
819             return;
820         }
821     }
822 
823 #if OSL_DEBUG_LEVEL > 1
824     OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
825     OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) );
826     OSL_TRACE(
827         "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
828         cstr_name.getStr(), cstr_value.getStr() );
829 #endif /* OSL_DEBUG_LEVEL > 1 */
830 
831     r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
832 }
833 
834 //----------------------------------------------------------------------------
835 
836 void SAL_CALL rtl_bootstrap_expandMacros_from_handle (
837     rtlBootstrapHandle handle,
838 	rtl_uString     ** macro
839 ) SAL_THROW_EXTERN_C()
840 {
841     if (handle == NULL) {
842         handle = get_static_bootstrap_handle();
843     }
844     OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
845 									 * reinterpret_cast< OUString const * >( macro ),
846                                      LOOKUP_MODE_NORMAL, NULL ) );
847     rtl_uString_assign( macro, expanded.pData );
848 }
849 
850 //----------------------------------------------------------------------------
851 
852 void SAL_CALL rtl_bootstrap_expandMacros(
853     rtl_uString ** macro )
854     SAL_THROW_EXTERN_C()
855 {
856     rtl_bootstrap_expandMacros_from_handle(NULL, macro);
857 }
858 
859 void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
860     SAL_THROW_EXTERN_C()
861 {
862     OSL_ASSERT(value != NULL);
863     rtl::OUStringBuffer b;
864     for (sal_Int32 i = 0; i < value->length; ++i) {
865         sal_Unicode c = value->buffer[i];
866         if (c == '$' || c == '\\') {
867             b.append(sal_Unicode('\\'));
868         }
869         b.append(c);
870     }
871     rtl_uString_assign(encoded, b.makeStringAndClear().pData);
872 }
873 
874 namespace {
875 
876 int hex(sal_Unicode c) {
877     return
878         c >= '0' && c <= '9' ? c - '0' :
879         c >= 'A' && c <= 'F' ? c - 'A' + 10 :
880         c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
881 }
882 
883 sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) {
884     OSL_ASSERT(
885         pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
886     sal_Unicode c = text[(*pos)++];
887     if (c == '\\') {
888         int n1, n2, n3, n4;
889         if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
890             ((n1 = hex(text[*pos + 1])) >= 0) &&
891             ((n2 = hex(text[*pos + 2])) >= 0) &&
892             ((n3 = hex(text[*pos + 3])) >= 0) &&
893             ((n4 = hex(text[*pos + 4])) >= 0))
894         {
895             *pos += 5;
896             *escaped = true;
897             return static_cast< sal_Unicode >(
898                 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
899         } else if (*pos < text.getLength()) {
900             *escaped = true;
901             return text[(*pos)++];
902         }
903     }
904     *escaped = false;
905     return c;
906 }
907 
908 rtl::OUString lookup(
909     Bootstrap_Impl const * file, LookupMode mode, bool override,
910     rtl::OUString const & key, ExpandRequestLink const * requestStack)
911 {
912     rtl::OUString v;
913     (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
914         key, &v.pData, NULL, mode, override, requestStack);
915     return v;
916 }
917 
918 rtl::OUString expandMacros(
919     Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
920     ExpandRequestLink const * requestStack)
921 {
922     rtl::OUStringBuffer buf;
923     for (sal_Int32 i = 0; i < text.getLength();) {
924         bool escaped;
925         sal_Unicode c = read(text, &i, &escaped);
926         if (escaped || c != '$') {
927             buf.append(c);
928         } else {
929             if (i < text.getLength() && text[i] == '{') {
930                 ++i;
931                 sal_Int32 p = i;
932                 sal_Int32 nesting = 0;
933                 rtl::OUString seg[3];
934                 int n = 0;
935                 while (i < text.getLength()) {
936                     sal_Int32 j = i;
937                     c = read(text, &i, &escaped);
938                     if (!escaped) {
939                         switch (c) {
940                         case '{':
941                             ++nesting;
942                             break;
943                         case '}':
944                             if (nesting == 0) {
945                                 seg[n++] = text.copy(p, j - p);
946                                 goto done;
947                             } else {
948                                 --nesting;
949                             }
950                             break;
951                         case ':':
952                             if (nesting == 0 && n < 2) {
953                                 seg[n++] = text.copy(p, j - p);
954                                 p = i;
955                             }
956                             break;
957                         }
958                     }
959                 }
960             done:
961                 for (int j = 0; j < n; ++j) {
962                     seg[j] = expandMacros(file, seg[j], mode, requestStack);
963                 }
964                 if (n == 3 && seg[1].getLength() == 0) {
965                     // For backward compatibility, treat ${file::key} the same
966                     // as just ${file:key}:
967                     seg[1] = seg[2];
968                     n = 2;
969                 }
970                 if (n == 1) {
971                     buf.append(lookup(file, mode, false, seg[0], requestStack));
972                 } else if (n == 2) {
973                     if (seg[0].equalsAsciiL(
974                             RTL_CONSTASCII_STRINGPARAM(".link")))
975                     {
976                         osl::File f(seg[1]);
977                         rtl::ByteSequence seq;
978                         rtl::OUString line;
979                         rtl::OUString url;
980                         // Silently ignore any errors (is that good?):
981                         if (f.open(OpenFlag_Read) == osl::FileBase::E_None &&
982                             f.readLine(seq) == osl::FileBase::E_None &&
983                             rtl_convertStringToUString(
984                                 &line.pData,
985                                 reinterpret_cast< char const * >(
986                                     seq.getConstArray()),
987                                 seq.getLength(), RTL_TEXTENCODING_UTF8,
988                                 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
989                                  RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
990                                  RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) &&
991                             (osl::File::getFileURLFromSystemPath(line, url) ==
992                              osl::FileBase::E_None))
993                         {
994                             try {
995                                 buf.append(
996                                     rtl::Uri::convertRelToAbs(seg[1], url));
997                             } catch (rtl::MalformedUriException &) {}
998                         }
999                     } else {
1000                         buf.append(
1001                             lookup(
1002                                 static_cast< Bootstrap_Impl * >(
1003                                     rtl::Bootstrap(seg[0]).getHandle()),
1004                                 mode, false, seg[1], requestStack));
1005                     }
1006                 } else if (seg[0].equalsAsciiL(
1007                                RTL_CONSTASCII_STRINGPARAM(".override")))
1008                 {
1009                     rtl::Bootstrap b(seg[1]);
1010                     Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(
1011                         b.getHandle());
1012                     buf.append(
1013                         lookup(f, mode, f != NULL, seg[2], requestStack));
1014                 } else {
1015                     // Going through osl::Profile, this code erroneously does
1016                     // not recursively expand macros in the resulting
1017                     // replacement text (and if it did, it would fail to detect
1018                     // cycles that pass through here):
1019                     buf.append(
1020                         rtl::OStringToOUString(
1021                             osl::Profile(seg[0]).readString(
1022                                 rtl::OUStringToOString(
1023                                     seg[1], RTL_TEXTENCODING_UTF8),
1024                                 rtl::OUStringToOString(
1025                                     seg[2], RTL_TEXTENCODING_UTF8),
1026                                 rtl::OString()),
1027                             RTL_TEXTENCODING_UTF8));
1028                 }
1029             } else {
1030                 rtl::OUStringBuffer kbuf;
1031                 for (; i < text.getLength();) {
1032                     sal_Int32 j = i;
1033                     c = read(text, &j, &escaped);
1034                     if (!escaped &&
1035                         (c == ' ' || c == '$' || c == '-' || c == '/' ||
1036                          c == ';' || c == '\\'))
1037                     {
1038                         break;
1039                     }
1040                     kbuf.append(c);
1041                     i = j;
1042                 }
1043                 buf.append(
1044                     lookup(
1045                         file, mode, false, kbuf.makeStringAndClear(),
1046                         requestStack));
1047             }
1048         }
1049     }
1050     return buf.makeStringAndClear();
1051 }
1052 
1053 }
1054