1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
21 #include <config_folders.h>
23 #include "rtl/bootstrap.h"
24 #include "rtl/bootstrap.hxx"
25 #include <osl/diagnose.h>
26 #include <osl/module.h>
27 #include <osl/process.h>
28 #include <osl/file.hxx>
29 #include <osl/mutex.hxx>
30 #include <osl/profile.hxx>
31 #include <osl/security.hxx>
32 #include <rtl/alloc.h>
33 #include <rtl/string.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <rtl/ustring.hxx>
36 #include <rtl/byteseq.hxx>
37 #include <rtl/instance.hxx>
38 #include <rtl/malformeduriexception.hxx>
39 #include <rtl/uri.hxx>
41 #include <boost/noncopyable.hpp>
42 #include <boost/unordered_map.hpp>
45 #include "getexecutablefile.hxx"
48 #include <osl/detail/android-bootstrap.h>
53 #import <Foundation/Foundation.h>
57 using osl::DirectoryItem
;
58 using osl::FileStatus
;
62 using rtl::OUStringToOString
;
64 struct Bootstrap_Impl
;
68 static char const VND_SUN_STAR_PATHNAME
[] = "vnd.sun.star.pathname:";
70 bool isPathnameUrl(rtl::OUString
const & url
) {
71 return url
.matchIgnoreAsciiCase(VND_SUN_STAR_PATHNAME
);
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
))
83 *url
= rtl::OUString();
89 LOOKUP_MODE_NORMAL
, LOOKUP_MODE_URE_BOOTSTRAP
,
90 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION
};
92 struct ExpandRequestLink
{
93 ExpandRequestLink
const * next
;
94 Bootstrap_Impl
const * file
;
98 rtl::OUString
expandMacros(
99 Bootstrap_Impl
const * file
, rtl::OUString
const & text
, LookupMode mode
,
100 ExpandRequestLink
const * requestStack
);
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
)
107 for (; requestStack
!= NULL
; requestStack
= requestStack
->next
) {
108 if (requestStack
->file
== requestFile
&&
109 requestStack
->key
== requestKey
)
111 return rtl::OUString("***RECURSION DETECTED***");
114 ExpandRequestLink link
= { requestStack
, requestFile
, requestKey
};
115 return expandMacros(file
, text
, mode
, &link
);
120 struct rtl_bootstrap_NameValue
125 inline rtl_bootstrap_NameValue() SAL_THROW( () )
127 inline rtl_bootstrap_NameValue(
128 OUString
const & name
, OUString
const & value
) SAL_THROW( () )
134 typedef std::list
<rtl_bootstrap_NameValue
> NameValueList
;
137 NameValueList
const & list
, rtl::OUString
const & key
,
138 rtl::OUString
* value
)
140 OSL_ASSERT(value
!= NULL
);
141 for (NameValueList::const_iterator
i(list
.begin()); i
!= list
.end(); ++i
) {
142 if (i
->sName
== key
) {
151 struct rtl_bootstrap_set_list
:
152 public rtl::Static
< NameValueList
, rtl_bootstrap_set_list
> {};
155 static bool getFromCommandLineArgs(
156 rtl::OUString
const & key
, rtl::OUString
* value
)
158 OSL_ASSERT(value
!= NULL
);
159 static NameValueList
*pNameValueList
= 0;
160 if( ! pNameValueList
)
162 static NameValueList nameValueList
;
164 sal_Int32 nArgCount
= osl_getCommandArgCount();
165 for(sal_Int32 i
= 0; i
< nArgCount
; ++ i
)
167 rtl_uString
*pArg
= 0;
168 osl_getCommandArg( i
, &pArg
);
169 if( ('-' == pArg
->buffer
[0] || '/' == pArg
->buffer
[0] ) &&
170 'e' == pArg
->buffer
[1] &&
171 'n' == pArg
->buffer
[2] &&
172 'v' == pArg
->buffer
[3] &&
173 ':' == pArg
->buffer
[4] )
175 sal_Int32 nIndex
= rtl_ustr_indexOfChar( pArg
->buffer
, '=' );
179 rtl_bootstrap_NameValue nameValue
;
180 nameValue
.sName
= OUString( &(pArg
->buffer
[5]), nIndex
- 5 );
181 nameValue
.sValue
= OUString( &(pArg
->buffer
[nIndex
+1]) );
182 if( i
== nArgCount
-1 &&
183 nameValue
.sValue
.getLength() &&
184 nameValue
.sValue
[nameValue
.sValue
.getLength()-1] == 13 )
186 // avoid the 13 linefeed for the last argument,
187 // when the executable is started from a script,
188 // that was edited on windows
189 nameValue
.sValue
= nameValue
.sValue
.copy(0,nameValue
.sValue
.getLength()-1);
191 nameValueList
.push_back( nameValue
);
194 rtl_uString_release( pArg
);
196 pNameValueList
= &nameValueList
;
201 for( NameValueList::iterator ii
= pNameValueList
->begin() ;
202 ii
!= pNameValueList
->end() ;
205 if( (*ii
).sName
.equals(key
) )
207 *value
= (*ii
).sValue
;
216 inline void getExecutableFile_Impl (rtl_uString
** ppFileURL
)
218 osl_bootstrap_getExecutableFile_Impl (ppFileURL
);
221 static void getExecutableDirectory_Impl (rtl_uString
** ppDirURL
)
224 getExecutableFile_Impl (&(fileName
.pData
));
226 sal_Int32 nDirEnd
= fileName
.lastIndexOf('/');
227 OSL_ENSURE(nDirEnd
>= 0, "Cannot locate executable directory");
229 rtl_uString_newFromStr_WithLength(ppDirURL
,fileName
.getStr(),nDirEnd
);
232 static OUString
& getIniFileName_Impl()
234 osl::MutexGuard
guard( osl::Mutex::getGlobalMutex() );
235 static OUString
*pStaticName
= 0;
241 // On iOS hardcode the inifile as "rc" in the .app
242 // directory. Apps are self-contained anyway, there is no
243 // possibility to have several "applications" in the same
244 // installation location with different inifiles.
245 const char *inifile
= [[@
"vnd.sun.star.pathname:" stringByAppendingString
: [[[NSBundle mainBundle
] bundlePath
] stringByAppendingPathComponent
: @
"rc"]] UTF8String
];
246 fileName
= rtl::OUString(inifile
, strlen(inifile
), RTL_TEXTENCODING_UTF8
);
247 resolvePathnameUrl(&fileName
);
248 #elif defined ANDROID
249 // Apps are self-contained on Android, too, can as well hardcode
250 // it as "rc" in the "/assets" directory, i.e. inside the app's
251 // .apk (zip) archive as the /assets/rc file.
252 fileName
= rtl::OUString("vnd.sun.star.pathname:/assets/rc");
253 resolvePathnameUrl(&fileName
);
255 if(getFromCommandLineArgs(
256 OUString("INIFILENAME"), &fileName
))
258 resolvePathnameUrl(&fileName
);
262 getExecutableFile_Impl (&(fileName
.pData
));
264 // get rid of a potential executable extension
265 OUString progExt
= ".bin";
266 if(fileName
.getLength() > progExt
.getLength()
267 && fileName
.copy(fileName
.getLength() - progExt
.getLength()).equalsIgnoreAsciiCase(progExt
))
268 fileName
= fileName
.copy(0, fileName
.getLength() - progExt
.getLength());
271 if(fileName
.getLength() > progExt
.getLength()
272 && fileName
.copy(fileName
.getLength() - progExt
.getLength()).equalsIgnoreAsciiCase(progExt
))
273 fileName
= fileName
.copy(0, fileName
.getLength() - progExt
.getLength());
275 // append config file suffix
276 fileName
+= OUString(SAL_CONFIGFILE(""));
278 #if HAVE_FEATURE_MACOSX_MACLIKE_APP_STRUCTURE
279 // We keep only executables in the MacOS folder, and all
280 // rc files in LIBO_ETC_FOLDER (typically "Resources").
281 sal_Int32 p
= fileName
.lastIndexOf( "/MacOS/" );
282 fileName
= fileName
.replaceAt( p
+1, strlen("MacOS"), LIBO_ETC_FOLDER
);
287 static OUString theFileName
;
288 if(fileName
.getLength())
289 theFileName
= fileName
;
291 pStaticName
= &theFileName
;
298 // ensure the given file url has no final slash
300 inline void EnsureNoFinalSlash (rtl::OUString
& url
)
302 sal_Int32 i
= url
.getLength();
303 if (i
> 0 && url
[i
- 1] == '/') {
304 url
= url
.copy(0, i
- 1);
308 struct Bootstrap_Impl
310 sal_Int32 _nRefCount
;
311 Bootstrap_Impl
* _base_ini
;
313 NameValueList _nameValueList
;
316 explicit Bootstrap_Impl (OUString
const & rIniName
);
319 static void * operator new (std::size_t n
) SAL_THROW(())
320 { return rtl_allocateMemory (sal_uInt32(n
)); }
321 static void operator delete (void * p
, std::size_t) SAL_THROW(())
322 { rtl_freeMemory (p
); }
325 rtl::OUString
const & key
, rtl_uString
** value
,
326 rtl_uString
* defaultValue
, LookupMode mode
, bool override
,
327 ExpandRequestLink
const * requestStack
) const;
329 rtl::OUString
const & key
, rtl_uString
** value
, LookupMode mode
,
330 ExpandRequestLink
const * requestStack
) const;
331 bool getAmbienceValue(
332 rtl::OUString
const & key
, rtl_uString
** value
, LookupMode mode
,
333 ExpandRequestLink
const * requestStack
) const;
335 rtl_uString
** value
, rtl::OUString
const & text
, LookupMode mode
,
336 Bootstrap_Impl
const * requestFile
, rtl::OUString
const & requestKey
,
337 ExpandRequestLink
const * requestStack
) const;
340 Bootstrap_Impl::Bootstrap_Impl( OUString
const & rIniName
)
345 OUString
base_ini( getIniFileName_Impl() );
347 FileStatus
status( osl_FileStatus_Mask_FileURL
);
348 DirectoryItem dirItem
;
349 if (DirectoryItem::E_None
== DirectoryItem::get( base_ini
, dirItem
) &&
350 DirectoryItem::E_None
== dirItem
.getFileStatus( status
))
352 base_ini
= status
.getFileURL();
353 if (! rIniName
.equals( base_ini
))
355 _base_ini
= static_cast< Bootstrap_Impl
* >(
356 rtl_bootstrap_args_open( base_ini
.pData
) );
360 #if OSL_DEBUG_LEVEL > 1
361 OString sFile
= OUStringToOString(_iniName
, RTL_TEXTENCODING_ASCII_US
);
362 OSL_TRACE("Bootstrap_Impl(): sFile=%s", sFile
.getStr());
363 #endif /* OSL_DEBUG_LEVEL > 1 */
365 oslFileHandle handle
;
366 if (!_iniName
.isEmpty() &&
367 osl_File_E_None
== osl_openFile(_iniName
.pData
, &handle
, osl_File_OpenFlag_Read
))
369 rtl::ByteSequence seq
;
371 while (osl_File_E_None
== osl_readLine(handle
, (sal_Sequence
**)&seq
))
373 OString
line( (const sal_Char
*) seq
.getConstArray(), seq
.getLength() );
374 sal_Int32 nIndex
= line
.indexOf('=');
377 struct rtl_bootstrap_NameValue nameValue
;
378 nameValue
.sName
= OStringToOUString(
379 line
.copy(0,nIndex
).trim(), RTL_TEXTENCODING_ASCII_US
);
380 nameValue
.sValue
= OStringToOUString(
381 line
.copy(nIndex
+1).trim(), RTL_TEXTENCODING_UTF8
);
383 #if OSL_DEBUG_LEVEL > 1
384 OString name_tmp
= OUStringToOString(nameValue
.sName
, RTL_TEXTENCODING_ASCII_US
);
385 OString value_tmp
= OUStringToOString(nameValue
.sValue
, RTL_TEXTENCODING_UTF8
);
387 "pushing: name=%s value=%s",
388 name_tmp
.getStr(), value_tmp
.getStr() );
389 #endif /* OSL_DEBUG_LEVEL > 1 */
391 _nameValueList
.push_back(nameValue
);
394 osl_closeFile(handle
);
396 #if OSL_DEBUG_LEVEL > 1
399 OString file_tmp
= OUStringToOString(_iniName
, RTL_TEXTENCODING_ASCII_US
);
400 OSL_TRACE( "couldn't open file: %s", file_tmp
.getStr() );
402 #endif /* OSL_DEBUG_LEVEL > 1 */
405 Bootstrap_Impl::~Bootstrap_Impl()
408 rtl_bootstrap_args_close( _base_ini
);
413 Bootstrap_Impl
* get_static_bootstrap_handle() SAL_THROW(())
415 osl::MutexGuard
guard( osl::Mutex::getGlobalMutex() );
416 static Bootstrap_Impl
* s_handle
= 0;
419 OUString
iniName (getIniFileName_Impl());
420 s_handle
= static_cast< Bootstrap_Impl
* >(
421 rtl_bootstrap_args_open( iniName
.pData
) );
424 Bootstrap_Impl
* that
= new Bootstrap_Impl( iniName
);
432 struct FundamentalIniData
: private boost::noncopyable
{
433 rtlBootstrapHandle ini
;
435 FundamentalIniData() {
438 ((static_cast< Bootstrap_Impl
* >(get_static_bootstrap_handle())->
440 rtl::OUString("URE_BOOTSTRAP"),
441 &uri
.pData
, 0, LOOKUP_MODE_NORMAL
, false, 0)) &&
442 resolvePathnameUrl(&uri
))
443 ? rtl_bootstrap_args_open(uri
.pData
) : NULL
;
446 ~FundamentalIniData() { rtl_bootstrap_args_close(ini
); }
449 struct FundamentalIni
: public rtl::Static
< FundamentalIniData
, FundamentalIni
>
454 bool Bootstrap_Impl::getValue(
455 rtl::OUString
const & key
, rtl_uString
** value
, rtl_uString
* defaultValue
,
456 LookupMode mode
, bool override
, ExpandRequestLink
const * requestStack
)
459 if (mode
== LOOKUP_MODE_NORMAL
&& key
== "URE_BOOTSTRAP") {
460 mode
= LOOKUP_MODE_URE_BOOTSTRAP
;
462 if (override
&& getDirectValue(key
, value
, mode
, requestStack
)) {
467 value
, rtl::OUString(RTL_OS
).pData
);
470 if (key
== "_ARCH") {
472 value
, rtl::OUString(RTL_ARCH
).pData
);
475 if (key
== "_CPPU_ENV") {
479 SAL_STRINGIFY(CPPU_ENV
)).
484 if (key
== "APP_DATA_DIR") {
485 const char *app_data_dir
= lo_get_app_data_dir();
487 value
, rtl::OUString(app_data_dir
, strlen(app_data_dir
), RTL_TEXTENCODING_UTF8
).pData
);
492 if (key
== "APP_DATA_DIR") {
493 const char *app_data_dir
= [[[[NSBundle mainBundle
] bundlePath
] stringByAddingPercentEscapesUsingEncoding
: NSUTF8StringEncoding
] UTF8String
];
495 value
, rtl::OUString(app_data_dir
, strlen(app_data_dir
), RTL_TEXTENCODING_UTF8
).pData
);
499 if (key
== "ORIGIN") {
503 0, std::max
<sal_Int32
>(0, _iniName
.lastIndexOf('/'))).pData
);
506 if (getAmbienceValue(key
, value
, mode
, requestStack
)) {
509 if (key
== "SYSUSERCONFIG") {
511 bool b
= osl::Security().getConfigDir(v
);
512 EnsureNoFinalSlash(v
);
513 rtl_uString_assign(value
, v
.pData
);
516 if (key
== "SYSUSERHOME") {
518 bool b
= osl::Security().getHomeDir(v
);
519 EnsureNoFinalSlash(v
);
520 rtl_uString_assign(value
, v
.pData
);
523 if (key
== "SYSBINDIR") {
524 getExecutableDirectory_Impl(value
);
527 if (_base_ini
!= NULL
&&
528 _base_ini
->getDirectValue(key
, value
, mode
, requestStack
))
532 if (!override
&& getDirectValue(key
, value
, mode
, requestStack
)) {
535 if (mode
== LOOKUP_MODE_NORMAL
) {
536 FundamentalIniData
const & d
= FundamentalIni::get();
537 Bootstrap_Impl
const * b
= static_cast<Bootstrap_Impl
const *>(d
.ini
);
538 if (b
!= NULL
&& b
!= this &&
539 b
->getDirectValue(key
, value
, mode
, requestStack
))
544 if (defaultValue
!= NULL
) {
545 rtl_uString_assign(value
, defaultValue
);
548 rtl_uString_new(value
);
552 bool Bootstrap_Impl::getDirectValue(
553 rtl::OUString
const & key
, rtl_uString
** value
, LookupMode mode
,
554 ExpandRequestLink
const * requestStack
) const
557 if (find(_nameValueList
, key
, &v
)) {
558 expandValue(value
, v
, mode
, this, key
, requestStack
);
565 bool Bootstrap_Impl::getAmbienceValue(
566 rtl::OUString
const & key
, rtl_uString
** value
, LookupMode mode
,
567 ExpandRequestLink
const * requestStack
) const
572 osl::MutexGuard
g(osl::Mutex::getGlobalMutex());
573 f
= find(rtl_bootstrap_set_list::get(), key
, &v
);
575 if (f
|| getFromCommandLineArgs(key
, &v
) ||
576 osl_getEnvironment(key
.pData
, &v
.pData
) == osl_Process_E_None
)
578 expandValue(value
, v
, mode
, NULL
, key
, requestStack
);
585 void Bootstrap_Impl::expandValue(
586 rtl_uString
** value
, rtl::OUString
const & text
, LookupMode mode
,
587 Bootstrap_Impl
const * requestFile
, rtl::OUString
const & requestKey
,
588 ExpandRequestLink
const * requestStack
) const
592 (mode
== LOOKUP_MODE_URE_BOOTSTRAP
&& isPathnameUrl(text
) ?
594 recursivelyExpandMacros(
596 (mode
== LOOKUP_MODE_URE_BOOTSTRAP
?
597 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION
: mode
),
598 requestFile
, requestKey
, requestStack
)).pData
);
603 struct bootstrap_map
: private boost::noncopyable
{
604 typedef boost::unordered_map
<
605 rtl::OUString
, Bootstrap_Impl
*,
606 rtl::OUStringHash
, std::equal_to
< rtl::OUString
> > t
;
608 // get and release must only be called properly synchronized via some mutex
609 // (e.g., osl::Mutex::getGlobalMutex()):
618 static void release() {
619 if (m_map
!= NULL
&& m_map
->empty()) {
629 bootstrap_map::t
* bootstrap_map::m_map
= NULL
;
633 rtlBootstrapHandle SAL_CALL
rtl_bootstrap_args_open (
634 rtl_uString
* pIniName
635 ) SAL_THROW_EXTERN_C()
637 OUString
iniName( pIniName
);
640 FileStatus
status( osl_FileStatus_Mask_FileURL
);
641 DirectoryItem dirItem
;
642 if (DirectoryItem::E_None
!= DirectoryItem::get( iniName
, dirItem
) ||
643 DirectoryItem::E_None
!= dirItem
.getFileStatus( status
))
647 iniName
= status
.getFileURL();
649 Bootstrap_Impl
* that
;
650 osl::ResettableMutexGuard
guard( osl::Mutex::getGlobalMutex() );
651 bootstrap_map::t
* p_bootstrap_map
= bootstrap_map::get();
652 bootstrap_map::t::const_iterator
iFind( p_bootstrap_map
->find( iniName
) );
653 if (iFind
== p_bootstrap_map
->end())
655 bootstrap_map::release();
657 that
= new Bootstrap_Impl( iniName
);
659 p_bootstrap_map
= bootstrap_map::get();
660 iFind
= p_bootstrap_map
->find( iniName
);
661 if (iFind
== p_bootstrap_map
->end())
664 ::std::pair
< bootstrap_map::t::iterator
, bool > insertion(
665 p_bootstrap_map
->insert(
666 bootstrap_map::t::value_type( iniName
, that
) ) );
667 (void) insertion
; // WaE: unused variable
668 OSL_ASSERT( insertion
.second
);
672 Bootstrap_Impl
* obsolete
= that
;
673 that
= iFind
->second
;
681 that
= iFind
->second
;
684 return static_cast< rtlBootstrapHandle
>( that
);
687 void SAL_CALL
rtl_bootstrap_args_close (
688 rtlBootstrapHandle handle
689 ) SAL_THROW_EXTERN_C()
693 Bootstrap_Impl
* that
= static_cast< Bootstrap_Impl
* >( handle
);
695 osl::MutexGuard
guard( osl::Mutex::getGlobalMutex() );
696 bootstrap_map::t
* p_bootstrap_map
= bootstrap_map::get();
698 p_bootstrap_map
->find( that
->_iniName
)->second
== that
);
700 if (that
->_nRefCount
== 0)
702 ::std::size_t nLeaking
= 8; // only hold up to 8 files statically
704 #if OSL_DEBUG_LEVEL == 1 // nonpro
706 #elif OSL_DEBUG_LEVEL > 1 // debug
708 #endif /* OSL_DEBUG_LEVEL */
710 if (p_bootstrap_map
->size() > nLeaking
)
712 ::std::size_t erased
= p_bootstrap_map
->erase( that
->_iniName
);
718 bootstrap_map::release();
722 sal_Bool SAL_CALL
rtl_bootstrap_get_from_handle(
723 rtlBootstrapHandle handle
,
725 rtl_uString
** ppValue
,
726 rtl_uString
* pDefault
727 ) SAL_THROW_EXTERN_C()
729 osl::MutexGuard
guard( osl::Mutex::getGlobalMutex() );
735 handle
= get_static_bootstrap_handle();
736 found
= static_cast< Bootstrap_Impl
* >( handle
)->getValue(
737 pName
, ppValue
, pDefault
, LOOKUP_MODE_NORMAL
, false, NULL
);
743 void SAL_CALL
rtl_bootstrap_get_iniName_from_handle (
744 rtlBootstrapHandle handle
,
745 rtl_uString
** ppIniName
746 ) SAL_THROW_EXTERN_C()
752 Bootstrap_Impl
* pImpl
= static_cast<Bootstrap_Impl
*>(handle
);
753 rtl_uString_assign(ppIniName
, pImpl
->_iniName
.pData
);
757 const OUString
& iniName
= getIniFileName_Impl();
758 rtl_uString_assign(ppIniName
, iniName
.pData
);
763 void SAL_CALL
rtl_bootstrap_setIniFileName (
765 ) SAL_THROW_EXTERN_C()
767 osl::MutexGuard
guard( osl::Mutex::getGlobalMutex() );
768 OUString
& file
= getIniFileName_Impl();
772 sal_Bool SAL_CALL
rtl_bootstrap_get (
774 rtl_uString
** ppValue
,
775 rtl_uString
* pDefault
776 ) SAL_THROW_EXTERN_C()
778 return rtl_bootstrap_get_from_handle(0, pName
, ppValue
, pDefault
);
781 void SAL_CALL
rtl_bootstrap_set (
784 ) SAL_THROW_EXTERN_C()
786 const OUString
name( pName
);
787 const OUString
value( pValue
);
789 osl::MutexGuard
guard( osl::Mutex::getGlobalMutex() );
791 NameValueList
& r_rtl_bootstrap_set_list
= rtl_bootstrap_set_list::get();
792 NameValueList::iterator
iPos( r_rtl_bootstrap_set_list
.begin() );
793 NameValueList::iterator
iEnd( r_rtl_bootstrap_set_list
.end() );
794 for ( ; iPos
!= iEnd
; ++iPos
)
796 if (iPos
->sName
.equals( name
))
798 iPos
->sValue
= value
;
803 #if OSL_DEBUG_LEVEL > 1
804 OString
cstr_name( OUStringToOString( name
, RTL_TEXTENCODING_ASCII_US
) );
805 OString
cstr_value( OUStringToOString( value
, RTL_TEXTENCODING_ASCII_US
) );
807 "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
808 cstr_name
.getStr(), cstr_value
.getStr() );
809 #endif /* OSL_DEBUG_LEVEL > 1 */
811 r_rtl_bootstrap_set_list
.push_back( rtl_bootstrap_NameValue( name
, value
) );
814 void SAL_CALL
rtl_bootstrap_expandMacros_from_handle (
815 rtlBootstrapHandle handle
,
817 ) SAL_THROW_EXTERN_C()
819 if (handle
== NULL
) {
820 handle
= get_static_bootstrap_handle();
822 OUString
expanded( expandMacros( static_cast< Bootstrap_Impl
* >( handle
),
823 * reinterpret_cast< OUString
const * >( macro
),
824 LOOKUP_MODE_NORMAL
, NULL
) );
825 rtl_uString_assign( macro
, expanded
.pData
);
828 void SAL_CALL
rtl_bootstrap_expandMacros(
829 rtl_uString
** macro
)
832 rtl_bootstrap_expandMacros_from_handle(NULL
, macro
);
835 void rtl_bootstrap_encode( rtl_uString
const * value
, rtl_uString
** encoded
)
838 OSL_ASSERT(value
!= NULL
);
839 rtl::OUStringBuffer b
;
840 for (sal_Int32 i
= 0; i
< value
->length
; ++i
) {
841 sal_Unicode c
= value
->buffer
[i
];
842 if (c
== '$' || c
== '\\') {
847 rtl_uString_assign(encoded
, b
.makeStringAndClear().pData
);
852 int hex(sal_Unicode c
) {
854 c
>= '0' && c
<= '9' ? c
- '0' :
855 c
>= 'A' && c
<= 'F' ? c
- 'A' + 10 :
856 c
>= 'a' && c
<= 'f' ? c
- 'a' + 10 : -1;
859 sal_Unicode
read(rtl::OUString
const & text
, sal_Int32
* pos
, bool * escaped
) {
861 pos
!= NULL
&& *pos
>= 0 && *pos
< text
.getLength() && escaped
!= NULL
);
862 sal_Unicode c
= text
[(*pos
)++];
865 if (*pos
< text
.getLength() - 4 && text
[*pos
] == 'u' &&
866 ((n1
= hex(text
[*pos
+ 1])) >= 0) &&
867 ((n2
= hex(text
[*pos
+ 2])) >= 0) &&
868 ((n3
= hex(text
[*pos
+ 3])) >= 0) &&
869 ((n4
= hex(text
[*pos
+ 4])) >= 0))
873 return static_cast< sal_Unicode
>(
874 (n1
<< 12) | (n2
<< 8) | (n3
<< 4) | n4
);
875 } else if (*pos
< text
.getLength()) {
877 return text
[(*pos
)++];
884 rtl::OUString
lookup(
885 Bootstrap_Impl
const * file
, LookupMode mode
, bool override
,
886 rtl::OUString
const & key
, ExpandRequestLink
const * requestStack
)
889 (file
== NULL
? get_static_bootstrap_handle() : file
)->getValue(
890 key
, &v
.pData
, NULL
, mode
, override
, requestStack
);
894 rtl::OUString
expandMacros(
895 Bootstrap_Impl
const * file
, rtl::OUString
const & text
, LookupMode mode
,
896 ExpandRequestLink
const * requestStack
)
898 rtl::OUStringBuffer buf
;
899 for (sal_Int32 i
= 0; i
< text
.getLength();) {
901 sal_Unicode c
= read(text
, &i
, &escaped
);
902 if (escaped
|| c
!= '$') {
905 if (i
< text
.getLength() && text
[i
] == '{') {
908 sal_Int32 nesting
= 0;
909 rtl::OUString seg
[3];
911 while (i
< text
.getLength()) {
913 c
= read(text
, &i
, &escaped
);
921 seg
[n
++] = text
.copy(p
, j
- p
);
928 if (nesting
== 0 && n
< 2) {
929 seg
[n
++] = text
.copy(p
, j
- p
);
937 for (int j
= 0; j
< n
; ++j
) {
938 seg
[j
] = expandMacros(file
, seg
[j
], mode
, requestStack
);
941 buf
.append(lookup(file
, mode
, false, seg
[0], requestStack
));
942 } else if (n
== 2 && seg
[0] == ".link") {
944 rtl::ByteSequence seq
;
947 // Silently ignore any errors (is that good?):
948 if ((f
.open(osl_File_OpenFlag_Read
) ==
949 osl::FileBase::E_None
) &&
950 f
.readLine(seq
) == osl::FileBase::E_None
&&
951 rtl_convertStringToUString(
953 reinterpret_cast< char const * >(
954 seq
.getConstArray()),
955 seq
.getLength(), RTL_TEXTENCODING_UTF8
,
956 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
|
957 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
|
958 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR
)) &&
959 (osl::File::getFileURLFromSystemPath(line
, url
) ==
960 osl::FileBase::E_None
))
964 rtl::Uri::convertRelToAbs(seg
[1], url
));
965 } catch (const rtl::MalformedUriException
&) {}
967 } else if (n
== 3 && seg
[0] == ".override") {
968 rtl::Bootstrap
b(seg
[1]);
969 Bootstrap_Impl
* f
= static_cast< Bootstrap_Impl
* >(
972 lookup(f
, mode
, f
!= NULL
, seg
[2], requestStack
));
974 if (n
== 3 && seg
[1].isEmpty()) {
975 // For backward compatibility, treat ${file::key} the
976 // same as just ${file:key}:
983 static_cast< Bootstrap_Impl
* >(
984 rtl::Bootstrap(seg
[0]).getHandle()),
985 mode
, false, seg
[1], requestStack
));
987 // Going through osl::Profile, this code erroneously
988 // does not recursively expand macros in the resulting
989 // replacement text (and if it did, it would fail to
990 // detect cycles that pass through here):
992 rtl::OStringToOUString(
993 osl::Profile(seg
[0]).readString(
994 rtl::OUStringToOString(
995 seg
[1], RTL_TEXTENCODING_UTF8
),
996 rtl::OUStringToOString(
997 seg
[2], RTL_TEXTENCODING_UTF8
),
999 RTL_TEXTENCODING_UTF8
));
1003 rtl::OUStringBuffer kbuf
;
1004 for (; i
< text
.getLength();) {
1006 c
= read(text
, &j
, &escaped
);
1008 (c
== ' ' || c
== '$' || c
== '-' || c
== '/' ||
1009 c
== ';' || c
== '\\'))
1018 file
, mode
, false, kbuf
.makeStringAndClear(),
1023 return buf
.makeStringAndClear();
1028 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */