xref: /trunk/main/sal/rtl/source/bootstrap.cxx (revision 45c5a00162811bd03b87fcb4fe9996782f2b59ec)
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 
68 bool isPathnameUrl(rtl::OUString const & url) {
69     return url.matchIgnoreAsciiCaseAsciiL(
70         RTL_CONSTASCII_STRINGPARAM(VND_SUN_STAR_PATHNAME));
71 }
72 
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 
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         {}
129     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 
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 
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 
225 inline void getExecutableFile_Impl (rtl_uString ** ppFileURL)
226 {
227     osl_bootstrap_getExecutableFile_Impl (ppFileURL);
228 }
229 
230 //----------------------------------------------------------------------------
231 
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 
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 #ifdef MACOSX
276             // In an application bundle only Mach-O binaries may live in
277             // Contents/MacOS -- code signing rejects the bundle otherwise --
278             // so the installation itself (rc files, rdbs, the libraries) sits
279             // in Contents/program, exactly as on the other UNX platforms.
280             // Look the ini file up there; $ORIGIN is derived from it below,
281             // which anchors the whole bootstrap chain in the program dir.
282             OUString macOSDir (RTL_CONSTASCII_USTRINGPARAM("/Contents/MacOS/"));
283             sal_Int32 nMacOSDir = fileName.lastIndexOf(macOSDir);
284             if (nMacOSDir >= 0)
285             {
286                 OUString programName =
287                     fileName.replaceAt(nMacOSDir, macOSDir.getLength(),
288                         OUString(RTL_CONSTASCII_USTRINGPARAM("/Contents/program/")));
289                 // Fall back to the old location for anything that is not laid
290                 // out this way (a plain bundle, an mdimporter, ...).
291                 ::osl::DirectoryItem item;
292                 if (::osl::DirectoryItem::get(programName, item) == ::osl::DirectoryItem::E_None)
293                     fileName = programName;
294             }
295 #endif
296         }
297 
298         static OUString theFileName;
299         if(fileName.getLength())
300             theFileName = fileName;
301 
302         pStaticName = &theFileName;
303     }
304 
305     return *pStaticName;
306 }
307 
308 //----------------------------------------------------------------------------
309 
310 static inline bool path_exists( OUString const & path )
311 {
312     DirectoryItem dirItem;
313     return (DirectoryItem::E_None == DirectoryItem::get( path, dirItem ));
314 }
315 
316 //----------------------------------------------------------------------------
317 // #111772#
318 // ensure the given file url has no final slash
319 
320 inline void EnsureNoFinalSlash (rtl::OUString & url)
321 {
322     sal_Int32 i = url.getLength();
323     if (i > 0 && url[i - 1] == '/') {
324         url = url.copy(0, i - 1);
325     }
326 }
327 
328 //----------------------------------------------------------------------------
329 //----------------------------------------------------------------------------
330 
331 struct Bootstrap_Impl
332 {
333     sal_Int32 _nRefCount;
334     Bootstrap_Impl * _base_ini;
335 
336     NameValueList _nameValueList;
337     OUString      _iniName;
338 
339     explicit Bootstrap_Impl (OUString const & rIniName);
340     ~Bootstrap_Impl();
341 
342     static void * operator new (std::size_t n) SAL_THROW(())
343         { return rtl_allocateMemory (sal_uInt32(n)); }
344     static void operator delete (void * p , std::size_t) SAL_THROW(())
345         { rtl_freeMemory (p); }
346 
347     bool getValue(
348         rtl::OUString const & key, rtl_uString ** value,
349         rtl_uString * defaultValue, LookupMode mode, bool override,
350         ExpandRequestLink const * requestStack) const;
351     bool getDirectValue(
352         rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
353         ExpandRequestLink const * requestStack) const;
354     bool getAmbienceValue(
355         rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
356         ExpandRequestLink const * requestStack) const;
357     void expandValue(
358         rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
359         Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
360         ExpandRequestLink const * requestStack) const;
361 };
362 
363 //----------------------------------------------------------------------------
364 
365 Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
366     : _nRefCount( 0 ),
367       _base_ini( 0 ),
368       _iniName (rIniName)
369 {
370     OUString base_ini( getIniFileName_Impl() );
371     // normalize path
372     FileStatus status( FileStatusMask_FileURL );
373     DirectoryItem dirItem;
374     if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) &&
375         DirectoryItem::E_None == dirItem.getFileStatus( status ))
376     {
377         base_ini = status.getFileURL();
378         if (! rIniName.equals( base_ini ))
379         {
380             _base_ini = static_cast< Bootstrap_Impl * >(
381                 rtl_bootstrap_args_open( base_ini.pData ) );
382         }
383     }
384 
385 #if OSL_DEBUG_LEVEL > 1
386     OString sFile = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
387     OSL_TRACE(__FILE__" -- Bootstrap_Impl() - %s\n", sFile.getStr());
388 #endif /* OSL_DEBUG_LEVEL > 1 */
389 
390     oslFileHandle handle;
391     if (_iniName.getLength() &&
392         osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read))
393     {
394         rtl::ByteSequence seq;
395 
396         while (osl_File_E_None == osl_readLine(handle , (sal_Sequence **)&seq))
397         {
398             OString line( (const sal_Char *) seq.getConstArray(), seq.getLength() );
399             sal_Int32 nIndex = line.indexOf('=');
400             if (nIndex >= 1)
401             {
402                 struct rtl_bootstrap_NameValue nameValue;
403                 nameValue.sName = OStringToOUString(
404                     line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US );
405                 nameValue.sValue = OStringToOUString(
406                     line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 );
407 
408 #if OSL_DEBUG_LEVEL > 1
409                 OString name_tmp = OUStringToOString(nameValue.sName, RTL_TEXTENCODING_ASCII_US);
410                 OString value_tmp = OUStringToOString(nameValue.sValue, RTL_TEXTENCODING_UTF8);
411                 OSL_TRACE(
412                     __FILE__" -- pushing: name=%s value=%s\n",
413                     name_tmp.getStr(), value_tmp.getStr() );
414 #endif /* OSL_DEBUG_LEVEL > 1 */
415 
416                 _nameValueList.push_back(nameValue);
417             }
418         }
419         osl_closeFile(handle);
420     }
421 #if OSL_DEBUG_LEVEL > 1
422     else
423     {
424         OString file_tmp = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
425         OSL_TRACE( __FILE__" -- couldn't open file: %s", file_tmp.getStr() );
426     }
427 #endif /* OSL_DEBUG_LEVEL > 1 */
428 }
429 
430 //----------------------------------------------------------------------------
431 
432 Bootstrap_Impl::~Bootstrap_Impl()
433 {
434     if (_base_ini != 0)
435         rtl_bootstrap_args_close( _base_ini );
436 }
437 
438 //----------------------------------------------------------------------------
439 
440 namespace {
441 
442 Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(())
443 {
444     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
445     static Bootstrap_Impl * s_handle = 0;
446     if (s_handle == 0)
447     {
448         OUString iniName (getIniFileName_Impl());
449         s_handle = static_cast< Bootstrap_Impl * >(
450             rtl_bootstrap_args_open( iniName.pData ) );
451         if (s_handle == 0)
452         {
453             Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
454             ++that->_nRefCount;
455             s_handle = that;
456         }
457     }
458     return s_handle;
459 }
460 
461 struct FundamentalIniData {
462     rtlBootstrapHandle ini;
463 
464     FundamentalIniData() {
465         OUString uri;
466         ini =
467             ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
468               getValue(
469                   rtl::OUString(
470                       RTL_CONSTASCII_USTRINGPARAM("URE_BOOTSTRAP")),
471                   &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) &&
472              resolvePathnameUrl(&uri))
473             ? rtl_bootstrap_args_open(uri.pData) : NULL;
474     }
475 
476     ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
477 
478 private:
479     FundamentalIniData(FundamentalIniData &); // not defined
480     void operator =(FundamentalIniData &); // not defined
481 };
482 
483 struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
484 {};
485 
486 }
487 
488 bool Bootstrap_Impl::getValue(
489     rtl::OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
490     LookupMode mode, bool override, ExpandRequestLink const * requestStack)
491     const
492 {
493     if (mode == LOOKUP_MODE_NORMAL &&
494         key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URE_BOOTSTRAP")))
495     {
496         mode = LOOKUP_MODE_URE_BOOTSTRAP;
497     }
498     if (override && getDirectValue(key, value, mode, requestStack)) {
499         return true;
500     }
501     if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_OS"))) {
502         rtl_uString_assign(
503             value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(THIS_OS)).pData);
504         return true;
505     }
506     if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_ARCH"))) {
507         rtl_uString_assign(
508             value, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(THIS_ARCH)).pData);
509         return true;
510     }
511     if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_CPPU_ENV"))) {
512         rtl_uString_assign(
513             value,
514             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(MY_STRING(CPPU_ENV))).
515              pData));
516         return true;
517     }
518     if (key.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("ORIGIN"))) {
519         rtl_uString_assign(
520             value,
521             _iniName.copy(
522                 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
523         return true;
524     }
525     if (getAmbienceValue(key, value, mode, requestStack)) {
526         return true;
527     }
528     if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSUSERCONFIG"))) {
529         rtl::OUString v;
530         bool b = osl::Security().getConfigDir(v);
531         EnsureNoFinalSlash(v);
532         rtl_uString_assign(value, v.pData);
533         return b;
534     }
535     if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSUSERHOME"))) {
536         rtl::OUString v;
537         bool b = osl::Security().getHomeDir(v);
538         EnsureNoFinalSlash(v);
539         rtl_uString_assign(value, v.pData);
540         return b;
541     }
542     if (key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("SYSBINDIR"))) {
543         getExecutableDirectory_Impl(value);
544         return true;
545     }
546     if (_base_ini != NULL &&
547         _base_ini->getDirectValue(key, value, mode, requestStack))
548     {
549         return true;
550     }
551     if (!override && getDirectValue(key, value, mode, requestStack)) {
552         return true;
553     }
554     if (mode == LOOKUP_MODE_NORMAL) {
555         FundamentalIniData const & d = FundamentalIni::get();
556         Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
557         if (b != NULL && b != this &&
558             b->getDirectValue(key, value, mode, requestStack))
559         {
560             return true;
561         }
562     }
563     if (defaultValue != NULL) {
564         rtl_uString_assign(value, defaultValue);
565         return true;
566     }
567     rtl_uString_new(value);
568     return false;
569 }
570 
571 bool Bootstrap_Impl::getDirectValue(
572     rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
573     ExpandRequestLink const * requestStack) const
574 {
575     rtl::OUString v;
576     if (find(_nameValueList, key, &v)) {
577         expandValue(value, v, mode, this, key, requestStack);
578         return true;
579     } else {
580         return false;
581     }
582 }
583 
584 bool Bootstrap_Impl::getAmbienceValue(
585     rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
586     ExpandRequestLink const * requestStack) const
587 {
588     rtl::OUString v;
589     bool f;
590     {
591         osl::MutexGuard g(osl::Mutex::getGlobalMutex());
592         f = find(rtl_bootstrap_set_list::get(), key, &v);
593     }
594     if (f || getFromCommandLineArgs(key, &v) ||
595         osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
596     {
597         expandValue(value, v, mode, NULL, key, requestStack);
598         return true;
599     } else {
600         return false;
601     }
602 }
603 
604 void Bootstrap_Impl::expandValue(
605     rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
606     Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
607     ExpandRequestLink const * requestStack) const
608 {
609     rtl_uString_assign(
610         value,
611         (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
612          text :
613          recursivelyExpandMacros(
614              this, text,
615              (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
616               LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
617              requestFile, requestKey, requestStack)).pData);
618 }
619 
620 //----------------------------------------------------------------------------
621 //----------------------------------------------------------------------------
622 
623 namespace {
624 
625 struct bootstrap_map {
626     typedef std::hash_map< const rtl::OUString, Bootstrap_Impl*, rtl::OUStringHash > t;
627 
628     // get and release must only be called properly synchronized via some mutex
629     // (e.g., osl::Mutex::getGlobalMutex()):
630 
631     static t * get() {
632         if (m_map == NULL) {
633             m_map = new t;
634         }
635         return m_map;
636     }
637 
638     static void release() {
639         if (m_map != NULL && m_map->empty()) {
640             delete m_map;
641             m_map = NULL;
642         }
643     }
644 
645 private:
646     bootstrap_map(); // not defined
647 
648     static t * m_map;
649 };
650 
651 bootstrap_map::t * bootstrap_map::m_map = NULL;
652 
653 }
654 
655 //----------------------------------------------------------------------------
656 
657 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
658     rtl_uString * pIniName
659 ) SAL_THROW_EXTERN_C()
660 {
661     OUString iniName( pIniName );
662 
663     // normalize path
664     FileStatus status( FileStatusMask_FileURL );
665     DirectoryItem dirItem;
666     if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
667         DirectoryItem::E_None != dirItem.getFileStatus( status ))
668     {
669         return 0;
670     }
671     iniName = status.getFileURL();
672 
673     Bootstrap_Impl * that;
674     osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
675     bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
676     bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
677     if (iFind == p_bootstrap_map->end())
678     {
679         bootstrap_map::release();
680         guard.clear();
681         that = new Bootstrap_Impl( iniName );
682         guard.reset();
683         p_bootstrap_map = bootstrap_map::get();
684         iFind = p_bootstrap_map->find( iniName );
685         if (iFind == p_bootstrap_map->end())
686         {
687             ++that->_nRefCount;
688             ::std::pair< bootstrap_map::t::iterator, bool > insertion(
689                 p_bootstrap_map->insert(
690                     bootstrap_map::t::value_type( iniName, that ) ) );
691             OSL_ASSERT( insertion.second );
692         }
693         else
694         {
695             Bootstrap_Impl * obsolete = that;
696             that = iFind->second;
697             ++that->_nRefCount;
698             guard.clear();
699             delete obsolete;
700         }
701     }
702     else
703     {
704         that = iFind->second;
705         ++that->_nRefCount;
706     }
707     return static_cast< rtlBootstrapHandle >( that );
708 }
709 
710 //----------------------------------------------------------------------------
711 
712 void SAL_CALL rtl_bootstrap_args_close (
713     rtlBootstrapHandle handle
714 ) SAL_THROW_EXTERN_C()
715 {
716     if (handle == 0)
717         return;
718     Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
719 
720     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
721     bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
722     OSL_ASSERT(
723         p_bootstrap_map->find( that->_iniName )->second == that );
724     --that->_nRefCount;
725     if (that->_nRefCount == 0)
726     {
727         ::std::size_t nLeaking = 8; // only hold up to 8 files statically
728 
729 #if OSL_DEBUG_LEVEL == 1 // nonpro
730         nLeaking = 0;
731 #elif OSL_DEBUG_LEVEL > 1 // debug
732         nLeaking = 1;
733 #endif /* OSL_DEBUG_LEVEL */
734 
735         if (p_bootstrap_map->size() > nLeaking)
736         {
737             ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
738             if (erased != 1) {
739                 OSL_ASSERT( false );
740             }
741             delete that;
742         }
743         bootstrap_map::release();
744     }
745 }
746 
747 //----------------------------------------------------------------------------
748 
749 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
750     rtlBootstrapHandle handle,
751     rtl_uString      * pName,
752     rtl_uString     ** ppValue,
753     rtl_uString      * pDefault
754 ) SAL_THROW_EXTERN_C()
755 {
756     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
757 
758     sal_Bool found = sal_False;
759     if(ppValue && pName)
760     {
761         if (handle == 0)
762             handle = get_static_bootstrap_handle();
763         found = static_cast< Bootstrap_Impl * >( handle )->getValue(
764             pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
765     }
766 
767     return found;
768 }
769 
770 //----------------------------------------------------------------------------
771 
772 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
773     rtlBootstrapHandle handle,
774     rtl_uString     ** ppIniName
775 ) SAL_THROW_EXTERN_C()
776 {
777     if(ppIniName)
778     {
779         if(handle)
780         {
781             Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
782             rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
783         }
784         else
785         {
786             const OUString & iniName = getIniFileName_Impl();
787             rtl_uString_assign(ppIniName, iniName.pData);
788         }
789     }
790 }
791 
792 //----------------------------------------------------------------------------
793 
794 void SAL_CALL rtl_bootstrap_setIniFileName (
795     rtl_uString * pName
796 ) SAL_THROW_EXTERN_C()
797 {
798     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
799     OUString & file = getIniFileName_Impl();
800     file = pName;
801 }
802 
803 //----------------------------------------------------------------------------
804 
805 sal_Bool SAL_CALL rtl_bootstrap_get (
806     rtl_uString  * pName,
807     rtl_uString ** ppValue,
808     rtl_uString  * pDefault
809 ) SAL_THROW_EXTERN_C()
810 {
811     return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
812 }
813 
814 //----------------------------------------------------------------------------
815 
816 void SAL_CALL rtl_bootstrap_set (
817     rtl_uString * pName,
818     rtl_uString * pValue
819 ) SAL_THROW_EXTERN_C()
820 {
821     const OUString name( pName );
822     const OUString value( pValue );
823 
824     osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
825 
826     NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
827     NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
828     NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
829     for ( ; iPos != iEnd; ++iPos )
830     {
831         if (iPos->sName.equals( name ))
832         {
833             iPos->sValue = value;
834             return;
835         }
836     }
837 
838 #if OSL_DEBUG_LEVEL > 1
839     OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
840     OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) );
841     OSL_TRACE(
842         "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
843         cstr_name.getStr(), cstr_value.getStr() );
844 #endif /* OSL_DEBUG_LEVEL > 1 */
845 
846     r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
847 }
848 
849 //----------------------------------------------------------------------------
850 
851 void SAL_CALL rtl_bootstrap_expandMacros_from_handle (
852     rtlBootstrapHandle handle,
853     rtl_uString     ** macro
854 ) SAL_THROW_EXTERN_C()
855 {
856     if (handle == NULL) {
857         handle = get_static_bootstrap_handle();
858     }
859     OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
860                                      * reinterpret_cast< OUString const * >( macro ),
861                                      LOOKUP_MODE_NORMAL, NULL ) );
862     rtl_uString_assign( macro, expanded.pData );
863 }
864 
865 //----------------------------------------------------------------------------
866 
867 void SAL_CALL rtl_bootstrap_expandMacros(
868     rtl_uString ** macro )
869     SAL_THROW_EXTERN_C()
870 {
871     rtl_bootstrap_expandMacros_from_handle(NULL, macro);
872 }
873 
874 void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
875     SAL_THROW_EXTERN_C()
876 {
877     OSL_ASSERT(value != NULL);
878     rtl::OUStringBuffer b;
879     for (sal_Int32 i = 0; i < value->length; ++i) {
880         sal_Unicode c = value->buffer[i];
881         if (c == '$' || c == '\\') {
882             b.append(sal_Unicode('\\'));
883         }
884         b.append(c);
885     }
886     rtl_uString_assign(encoded, b.makeStringAndClear().pData);
887 }
888 
889 namespace {
890 
891 int hex(sal_Unicode c) {
892     return
893         c >= '0' && c <= '9' ? c - '0' :
894         c >= 'A' && c <= 'F' ? c - 'A' + 10 :
895         c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
896 }
897 
898 sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) {
899     OSL_ASSERT(
900         pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
901     sal_Unicode c = text[(*pos)++];
902     if (c == '\\') {
903         int n1, n2, n3, n4;
904         if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
905             ((n1 = hex(text[*pos + 1])) >= 0) &&
906             ((n2 = hex(text[*pos + 2])) >= 0) &&
907             ((n3 = hex(text[*pos + 3])) >= 0) &&
908             ((n4 = hex(text[*pos + 4])) >= 0))
909         {
910             *pos += 5;
911             *escaped = true;
912             return static_cast< sal_Unicode >(
913                 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
914         } else if (*pos < text.getLength()) {
915             *escaped = true;
916             return text[(*pos)++];
917         }
918     }
919     *escaped = false;
920     return c;
921 }
922 
923 rtl::OUString lookup(
924     Bootstrap_Impl const * file, LookupMode mode, bool override,
925     rtl::OUString const & key, ExpandRequestLink const * requestStack)
926 {
927     rtl::OUString v;
928     (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
929         key, &v.pData, NULL, mode, override, requestStack);
930     return v;
931 }
932 
933 rtl::OUString expandMacros(
934     Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
935     ExpandRequestLink const * requestStack)
936 {
937     rtl::OUStringBuffer buf;
938     for (sal_Int32 i = 0; i < text.getLength();) {
939         bool escaped;
940         sal_Unicode c = read(text, &i, &escaped);
941         if (escaped || c != '$') {
942             buf.append(c);
943         } else {
944             if (i < text.getLength() && text[i] == '{') {
945                 ++i;
946                 sal_Int32 p = i;
947                 sal_Int32 nesting = 0;
948                 rtl::OUString seg[3];
949                 int n = 0;
950                 while (i < text.getLength()) {
951                     sal_Int32 j = i;
952                     c = read(text, &i, &escaped);
953                     if (!escaped) {
954                         switch (c) {
955                         case '{':
956                             ++nesting;
957                             break;
958                         case '}':
959                             if (nesting == 0) {
960                                 seg[n++] = text.copy(p, j - p);
961                                 goto done;
962                             } else {
963                                 --nesting;
964                             }
965                             break;
966                         case ':':
967                             if (nesting == 0 && n < 2) {
968                                 seg[n++] = text.copy(p, j - p);
969                                 p = i;
970                             }
971                             break;
972                         }
973                     }
974                 }
975             done:
976                 for (int j = 0; j < n; ++j) {
977                     seg[j] = expandMacros(file, seg[j], mode, requestStack);
978                 }
979                 if (n == 3 && seg[1].getLength() == 0) {
980                     // For backward compatibility, treat ${file::key} the same
981                     // as just ${file:key}:
982                     seg[1] = seg[2];
983                     n = 2;
984                 }
985                 if (n == 1) {
986                     buf.append(lookup(file, mode, false, seg[0], requestStack));
987                 } else if (n == 2) {
988                     if (seg[0].equalsAsciiL(
989                             RTL_CONSTASCII_STRINGPARAM(".link")))
990                     {
991                         osl::File f(seg[1]);
992                         rtl::ByteSequence seq;
993                         rtl::OUString line;
994                         rtl::OUString url;
995                         // Silently ignore any errors (is that good?):
996                         if (f.open(OpenFlag_Read) == osl::FileBase::E_None &&
997                             f.readLine(seq) == osl::FileBase::E_None &&
998                             rtl_convertStringToUString(
999                                 &line.pData,
1000                                 reinterpret_cast< char const * >(
1001                                     seq.getConstArray()),
1002                                 seq.getLength(), RTL_TEXTENCODING_UTF8,
1003                                 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
1004                                  RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
1005                                  RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) &&
1006                             (osl::File::getFileURLFromSystemPath(line, url) ==
1007                              osl::FileBase::E_None))
1008                         {
1009                             try {
1010                                 buf.append(
1011                                     rtl::Uri::convertRelToAbs(seg[1], url));
1012                             } catch (rtl::MalformedUriException &) {}
1013                         }
1014                     } else {
1015                         buf.append(
1016                             lookup(
1017                                 static_cast< Bootstrap_Impl * >(
1018                                     rtl::Bootstrap(seg[0]).getHandle()),
1019                                 mode, false, seg[1], requestStack));
1020                     }
1021                 } else if (seg[0].equalsAsciiL(
1022                                RTL_CONSTASCII_STRINGPARAM(".override")))
1023                 {
1024                     rtl::Bootstrap b(seg[1]);
1025                     Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(
1026                         b.getHandle());
1027                     buf.append(
1028                         lookup(f, mode, f != NULL, seg[2], requestStack));
1029                 } else {
1030                     // Going through osl::Profile, this code erroneously does
1031                     // not recursively expand macros in the resulting
1032                     // replacement text (and if it did, it would fail to detect
1033                     // cycles that pass through here):
1034                     buf.append(
1035                         rtl::OStringToOUString(
1036                             osl::Profile(seg[0]).readString(
1037                                 rtl::OUStringToOString(
1038                                     seg[1], RTL_TEXTENCODING_UTF8),
1039                                 rtl::OUStringToOString(
1040                                     seg[2], RTL_TEXTENCODING_UTF8),
1041                                 rtl::OString()),
1042                             RTL_TEXTENCODING_UTF8));
1043                 }
1044             } else {
1045                 rtl::OUStringBuffer kbuf;
1046                 for (; i < text.getLength();) {
1047                     sal_Int32 j = i;
1048                     c = read(text, &j, &escaped);
1049                     if (!escaped &&
1050                         (c == ' ' || c == '$' || c == '-' || c == '/' ||
1051                          c == ';' || c == '\\'))
1052                     {
1053                         break;
1054                     }
1055                     kbuf.append(c);
1056                     i = j;
1057                 }
1058                 buf.append(
1059                     lookup(
1060                         file, mode, false, kbuf.makeStringAndClear(),
1061                         requestStack));
1062             }
1063         }
1064     }
1065     return buf.makeStringAndClear();
1066 }
1067 
1068 }
1069