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