bump product version to 4.1.6.2
[LibreOffice.git] / sal / rtl / bootstrap.cxx
blobc6a2f74801f8e8fc6d3b3307fd9d9a316b3f0540
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include "rtl/bootstrap.h"
22 #include "rtl/bootstrap.hxx"
23 #include <osl/diagnose.h>
24 #include <osl/module.h>
25 #include <osl/process.h>
26 #include <osl/file.hxx>
27 #include <osl/mutex.hxx>
28 #include <osl/profile.hxx>
29 #include <osl/security.hxx>
30 #include <rtl/alloc.h>
31 #include <rtl/string.hxx>
32 #include <rtl/ustrbuf.hxx>
33 #include <rtl/ustring.hxx>
34 #include <rtl/byteseq.hxx>
35 #include <rtl/instance.hxx>
36 #include <rtl/malformeduriexception.hxx>
37 #include <rtl/uri.hxx>
38 #include "rtl/allocator.hxx"
40 #include <boost/unordered_map.hpp>
41 #include <list>
43 #ifdef ANDROID
44 #include <osl/detail/android-bootstrap.h>
45 #endif
47 #ifdef IOS
48 #include <premac.h>
49 #import <Foundation/Foundation.h>
50 #include <postmac.h>
51 #endif
53 using osl::DirectoryItem;
54 using osl::FileStatus;
56 using rtl::OString;
57 using rtl::OUString;
58 using rtl::OUStringToOString;
60 struct Bootstrap_Impl;
62 namespace {
64 static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
66 bool isPathnameUrl(rtl::OUString const & url) {
67 return url.matchIgnoreAsciiCaseAsciiL(
68 RTL_CONSTASCII_STRINGPARAM(VND_SUN_STAR_PATHNAME));
71 bool resolvePathnameUrl(rtl::OUString * url) {
72 OSL_ASSERT(url != NULL);
73 if (!isPathnameUrl(*url) ||
74 (osl::FileBase::getFileURLFromSystemPath(
75 url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
76 osl::FileBase::E_None))
78 return true;
79 } else {
80 *url = rtl::OUString();
81 return false;
85 enum LookupMode {
86 LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
87 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
89 struct ExpandRequestLink {
90 ExpandRequestLink const * next;
91 Bootstrap_Impl const * file;
92 rtl::OUString key;
95 rtl::OUString expandMacros(
96 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
97 ExpandRequestLink const * requestStack);
99 rtl::OUString recursivelyExpandMacros(
100 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
101 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
102 ExpandRequestLink const * requestStack)
104 for (; requestStack != NULL; requestStack = requestStack->next) {
105 if (requestStack->file == requestFile &&
106 requestStack->key == requestKey)
108 return rtl::OUString("***RECURSION DETECTED***");
111 ExpandRequestLink link = { requestStack, requestFile, requestKey };
112 return expandMacros(file, text, mode, &link);
117 //----------------------------------------------------------------------------
119 struct rtl_bootstrap_NameValue
121 OUString sName;
122 OUString sValue;
124 inline rtl_bootstrap_NameValue() SAL_THROW( () )
126 inline rtl_bootstrap_NameValue(
127 OUString const & name, OUString const & value ) SAL_THROW( () )
128 : sName( name ),
129 sValue( value )
133 typedef std::list<
134 rtl_bootstrap_NameValue,
135 rtl::Allocator< rtl_bootstrap_NameValue >
136 > NameValueList;
138 bool find(
139 NameValueList const & list, rtl::OUString const & key,
140 rtl::OUString * value)
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;
149 return false;
152 namespace {
153 struct rtl_bootstrap_set_list :
154 public rtl::Static< NameValueList, rtl_bootstrap_set_list > {};
157 //----------------------------------------------------------------------------
159 static sal_Bool getFromCommandLineArgs(
160 rtl::OUString const & key, rtl::OUString * value )
162 OSL_ASSERT(value != NULL);
163 static NameValueList *pNameValueList = 0;
164 if( ! pNameValueList )
166 static NameValueList nameValueList;
168 sal_Int32 nArgCount = osl_getCommandArgCount();
169 for(sal_Int32 i = 0; i < nArgCount; ++ i)
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] )
179 sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
180 if( nIndex >= 0 )
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 )
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);
195 nameValueList.push_back( nameValue );
198 rtl_uString_release( pArg );
200 pNameValueList = &nameValueList;
203 sal_Bool found = sal_False;
205 for( NameValueList::iterator ii = pNameValueList->begin() ;
206 ii != pNameValueList->end() ;
207 ++ii )
209 if( (*ii).sName.equals(key) )
211 *value = (*ii).sValue;
212 found = sal_True;
213 break;
217 return found;
220 //----------------------------------------------------------------------------
222 extern "C" oslProcessError SAL_CALL osl_bootstrap_getExecutableFile_Impl (
223 rtl_uString ** ppFileURL) SAL_THROW_EXTERN_C();
225 inline void getExecutableFile_Impl (rtl_uString ** ppFileURL)
227 osl_bootstrap_getExecutableFile_Impl (ppFileURL);
230 //----------------------------------------------------------------------------
232 static void getExecutableDirectory_Impl (rtl_uString ** ppDirURL)
234 OUString fileName;
235 getExecutableFile_Impl (&(fileName.pData));
237 sal_Int32 nDirEnd = fileName.lastIndexOf('/');
238 OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
240 rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
243 //----------------------------------------------------------------------------
245 static OUString & getIniFileName_Impl()
247 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
248 static OUString *pStaticName = 0;
249 if( ! pStaticName )
251 OUString fileName;
253 #if defined IOS
254 // On iOS hardcode the inifile as "rc" in the .app
255 // directory. Apps are self-contained anyway, there is no
256 // possibility to have several "applications" in the same
257 // installation location with different inifiles.
258 const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String];
259 fileName = rtl::OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
260 resolvePathnameUrl(&fileName);
261 #elif defined ANDROID
262 // Apps are self-contained on Android, too, can as well hardcode
263 // it as "rc" in the "/assets" directory, i.e. inside the app's
264 // .apk (zip) archive as the /assets/rc file.
265 fileName = rtl::OUString("vnd.sun.star.pathname:/assets/rc");
266 resolvePathnameUrl(&fileName);
267 #else
268 if(getFromCommandLineArgs(
269 OUString("INIFILENAME"), &fileName))
271 resolvePathnameUrl(&fileName);
273 else
275 getExecutableFile_Impl (&(fileName.pData));
277 // get rid of a potential executable extension
278 OUString progExt = ".bin";
279 if(fileName.getLength() > progExt.getLength()
280 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
281 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
283 progExt = ".exe";
284 if(fileName.getLength() > progExt.getLength()
285 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
286 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
288 // append config file suffix
289 fileName += OUString(SAL_CONFIGFILE(""));
291 #endif
293 static OUString theFileName;
294 if(fileName.getLength())
295 theFileName = fileName;
297 pStaticName = &theFileName;
300 return *pStaticName;
303 //----------------------------------------------------------------------------
305 static inline bool path_exists( OUString const & path )
307 DirectoryItem dirItem;
308 return (DirectoryItem::E_None == DirectoryItem::get( path, dirItem ));
311 //----------------------------------------------------------------------------
312 // #111772#
313 // ensure the given file url has no final slash
315 inline void EnsureNoFinalSlash (rtl::OUString & url)
317 sal_Int32 i = url.getLength();
318 if (i > 0 && url[i - 1] == '/') {
319 url = url.copy(0, i - 1);
323 struct Bootstrap_Impl
325 sal_Int32 _nRefCount;
326 Bootstrap_Impl * _base_ini;
328 NameValueList _nameValueList;
329 OUString _iniName;
331 explicit Bootstrap_Impl (OUString const & rIniName);
332 ~Bootstrap_Impl();
334 static void * operator new (std::size_t n) SAL_THROW(())
335 { return rtl_allocateMemory (sal_uInt32(n)); }
336 static void operator delete (void * p , std::size_t) SAL_THROW(())
337 { rtl_freeMemory (p); }
339 bool getValue(
340 rtl::OUString const & key, rtl_uString ** value,
341 rtl_uString * defaultValue, LookupMode mode, bool override,
342 ExpandRequestLink const * requestStack) const;
343 bool getDirectValue(
344 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
345 ExpandRequestLink const * requestStack) const;
346 bool getAmbienceValue(
347 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
348 ExpandRequestLink const * requestStack) const;
349 void expandValue(
350 rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
351 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
352 ExpandRequestLink const * requestStack) const;
355 //----------------------------------------------------------------------------
357 Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
358 : _nRefCount( 0 ),
359 _base_ini( 0 ),
360 _iniName (rIniName)
362 OUString base_ini( getIniFileName_Impl() );
363 // normalize path
364 FileStatus status( osl_FileStatus_Mask_FileURL );
365 DirectoryItem dirItem;
366 if (DirectoryItem::E_None == DirectoryItem::get( base_ini, dirItem ) &&
367 DirectoryItem::E_None == dirItem.getFileStatus( status ))
369 base_ini = status.getFileURL();
370 if (! rIniName.equals( base_ini ))
372 _base_ini = static_cast< Bootstrap_Impl * >(
373 rtl_bootstrap_args_open( base_ini.pData ) );
377 #if OSL_DEBUG_LEVEL > 1
378 OString sFile = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
379 OSL_TRACE("Bootstrap_Impl(): sFile=%s", sFile.getStr());
380 #endif /* OSL_DEBUG_LEVEL > 1 */
382 oslFileHandle handle;
383 if (!_iniName.isEmpty() &&
384 osl_File_E_None == osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read))
386 rtl::ByteSequence seq;
388 while (osl_File_E_None == osl_readLine(handle , (sal_Sequence **)&seq))
390 OString line( (const sal_Char *) seq.getConstArray(), seq.getLength() );
391 sal_Int32 nIndex = line.indexOf('=');
392 if (nIndex >= 1)
394 struct rtl_bootstrap_NameValue nameValue;
395 nameValue.sName = OStringToOUString(
396 line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US );
397 nameValue.sValue = OStringToOUString(
398 line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8 );
400 #if OSL_DEBUG_LEVEL > 1
401 OString name_tmp = OUStringToOString(nameValue.sName, RTL_TEXTENCODING_ASCII_US);
402 OString value_tmp = OUStringToOString(nameValue.sValue, RTL_TEXTENCODING_UTF8);
403 OSL_TRACE(
404 "pushing: name=%s value=%s",
405 name_tmp.getStr(), value_tmp.getStr() );
406 #endif /* OSL_DEBUG_LEVEL > 1 */
408 _nameValueList.push_back(nameValue);
411 osl_closeFile(handle);
413 #if OSL_DEBUG_LEVEL > 1
414 else
416 OString file_tmp = OUStringToOString(_iniName, RTL_TEXTENCODING_ASCII_US);
417 OSL_TRACE( "couldn't open file: %s", file_tmp.getStr() );
419 #endif /* OSL_DEBUG_LEVEL > 1 */
422 //----------------------------------------------------------------------------
424 Bootstrap_Impl::~Bootstrap_Impl()
426 if (_base_ini != 0)
427 rtl_bootstrap_args_close( _base_ini );
430 //----------------------------------------------------------------------------
432 namespace {
434 Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(())
436 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
437 static Bootstrap_Impl * s_handle = 0;
438 if (s_handle == 0)
440 OUString iniName (getIniFileName_Impl());
441 s_handle = static_cast< Bootstrap_Impl * >(
442 rtl_bootstrap_args_open( iniName.pData ) );
443 if (s_handle == 0)
445 Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
446 ++that->_nRefCount;
447 s_handle = that;
450 return s_handle;
453 struct FundamentalIniData {
454 rtlBootstrapHandle ini;
456 FundamentalIniData() {
457 OUString uri;
458 ini =
459 ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
460 getValue(
461 rtl::OUString("URE_BOOTSTRAP"),
462 &uri.pData, 0, LOOKUP_MODE_NORMAL, false, 0)) &&
463 resolvePathnameUrl(&uri))
464 ? rtl_bootstrap_args_open(uri.pData) : NULL;
467 ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
469 private:
470 FundamentalIniData(FundamentalIniData &); // not defined
471 void operator =(FundamentalIniData &); // not defined
474 struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
479 bool Bootstrap_Impl::getValue(
480 rtl::OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
481 LookupMode mode, bool override, ExpandRequestLink const * requestStack)
482 const
484 if (mode == LOOKUP_MODE_NORMAL &&
485 key.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URE_BOOTSTRAP")))
487 mode = LOOKUP_MODE_URE_BOOTSTRAP;
489 if (override && getDirectValue(key, value, mode, requestStack)) {
490 return true;
492 if (key == "_OS") {
493 rtl_uString_assign(
494 value, rtl::OUString(RTL_OS).pData);
495 return true;
497 if (key == "_ARCH") {
498 rtl_uString_assign(
499 value, rtl::OUString(RTL_ARCH).pData);
500 return true;
502 if (key == "_CPPU_ENV") {
503 rtl_uString_assign(
504 value,
505 (rtl::OUString(
506 SAL_STRINGIFY(CPPU_ENV)).
507 pData));
508 return true;
510 #ifdef ANDROID
511 if (key == "APP_DATA_DIR") {
512 const char *app_data_dir = lo_get_app_data_dir();
513 rtl_uString_assign(
514 value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
515 return true;
517 #endif
518 #ifdef IOS
519 if (key == "APP_DATA_DIR") {
520 const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] UTF8String];
521 rtl_uString_assign(
522 value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
523 return true;
525 #endif
526 if (key == "ORIGIN") {
527 rtl_uString_assign(
528 value,
529 _iniName.copy(
530 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
531 return true;
533 if (getAmbienceValue(key, value, mode, requestStack)) {
534 return true;
536 if (key == "SYSUSERCONFIG") {
537 rtl::OUString v;
538 bool b = osl::Security().getConfigDir(v);
539 EnsureNoFinalSlash(v);
540 rtl_uString_assign(value, v.pData);
541 return b;
543 if (key == "SYSUSERHOME") {
544 rtl::OUString v;
545 bool b = osl::Security().getHomeDir(v);
546 EnsureNoFinalSlash(v);
547 rtl_uString_assign(value, v.pData);
548 return b;
550 if (key == "SYSBINDIR") {
551 getExecutableDirectory_Impl(value);
552 return true;
554 if (_base_ini != NULL &&
555 _base_ini->getDirectValue(key, value, mode, requestStack))
557 return true;
559 if (!override && getDirectValue(key, value, mode, requestStack)) {
560 return true;
562 if (mode == LOOKUP_MODE_NORMAL) {
563 FundamentalIniData const & d = FundamentalIni::get();
564 Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
565 if (b != NULL && b != this &&
566 b->getDirectValue(key, value, mode, requestStack))
568 return true;
571 if (defaultValue != NULL) {
572 rtl_uString_assign(value, defaultValue);
573 return true;
575 rtl_uString_new(value);
576 return false;
579 bool Bootstrap_Impl::getDirectValue(
580 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
581 ExpandRequestLink const * requestStack) const
583 rtl::OUString v;
584 if (find(_nameValueList, key, &v)) {
585 expandValue(value, v, mode, this, key, requestStack);
586 return true;
587 } else {
588 return false;
592 bool Bootstrap_Impl::getAmbienceValue(
593 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
594 ExpandRequestLink const * requestStack) const
596 rtl::OUString v;
597 bool f;
599 osl::MutexGuard g(osl::Mutex::getGlobalMutex());
600 f = find(rtl_bootstrap_set_list::get(), key, &v);
602 if (f || getFromCommandLineArgs(key, &v) ||
603 osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
605 expandValue(value, v, mode, NULL, key, requestStack);
606 return true;
607 } else {
608 return false;
612 void Bootstrap_Impl::expandValue(
613 rtl_uString ** value, rtl::OUString const & text, LookupMode mode,
614 Bootstrap_Impl const * requestFile, rtl::OUString const & requestKey,
615 ExpandRequestLink const * requestStack) const
617 rtl_uString_assign(
618 value,
619 (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
620 text :
621 recursivelyExpandMacros(
622 this, text,
623 (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
624 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
625 requestFile, requestKey, requestStack)).pData);
628 namespace {
630 struct bootstrap_map {
631 typedef boost::unordered_map<
632 rtl::OUString, Bootstrap_Impl *,
633 rtl::OUStringHash, std::equal_to< rtl::OUString >,
634 rtl::Allocator< OUString > > t;
636 // get and release must only be called properly synchronized via some mutex
637 // (e.g., osl::Mutex::getGlobalMutex()):
639 static t * get() {
640 if (m_map == NULL) {
641 m_map = new t;
643 return m_map;
646 static void release() {
647 if (m_map != NULL && m_map->empty()) {
648 delete m_map;
649 m_map = NULL;
653 private:
654 bootstrap_map(); // not defined
656 static t * m_map;
659 bootstrap_map::t * bootstrap_map::m_map = NULL;
663 //----------------------------------------------------------------------------
665 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open (
666 rtl_uString * pIniName
667 ) SAL_THROW_EXTERN_C()
669 OUString iniName( pIniName );
671 // normalize path
672 FileStatus status( osl_FileStatus_Mask_FileURL );
673 DirectoryItem dirItem;
674 if (DirectoryItem::E_None != DirectoryItem::get( iniName, dirItem ) ||
675 DirectoryItem::E_None != dirItem.getFileStatus( status ))
677 return 0;
679 iniName = status.getFileURL();
681 Bootstrap_Impl * that;
682 osl::ResettableMutexGuard guard( osl::Mutex::getGlobalMutex() );
683 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
684 bootstrap_map::t::const_iterator iFind( p_bootstrap_map->find( iniName ) );
685 if (iFind == p_bootstrap_map->end())
687 bootstrap_map::release();
688 guard.clear();
689 that = new Bootstrap_Impl( iniName );
690 guard.reset();
691 p_bootstrap_map = bootstrap_map::get();
692 iFind = p_bootstrap_map->find( iniName );
693 if (iFind == p_bootstrap_map->end())
695 ++that->_nRefCount;
696 ::std::pair< bootstrap_map::t::iterator, bool > insertion(
697 p_bootstrap_map->insert(
698 bootstrap_map::t::value_type( iniName, that ) ) );
699 (void) insertion; // WaE: unused variable
700 OSL_ASSERT( insertion.second );
702 else
704 Bootstrap_Impl * obsolete = that;
705 that = iFind->second;
706 ++that->_nRefCount;
707 guard.clear();
708 delete obsolete;
711 else
713 that = iFind->second;
714 ++that->_nRefCount;
716 return static_cast< rtlBootstrapHandle >( that );
719 //----------------------------------------------------------------------------
721 void SAL_CALL rtl_bootstrap_args_close (
722 rtlBootstrapHandle handle
723 ) SAL_THROW_EXTERN_C()
725 if (handle == 0)
726 return;
727 Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
729 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
730 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
731 OSL_ASSERT(
732 p_bootstrap_map->find( that->_iniName )->second == that );
733 --that->_nRefCount;
734 if (that->_nRefCount == 0)
736 ::std::size_t nLeaking = 8; // only hold up to 8 files statically
738 #if OSL_DEBUG_LEVEL == 1 // nonpro
739 nLeaking = 0;
740 #elif OSL_DEBUG_LEVEL > 1 // debug
741 nLeaking = 1;
742 #endif /* OSL_DEBUG_LEVEL */
744 if (p_bootstrap_map->size() > nLeaking)
746 ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
747 if (erased != 1) {
748 OSL_ASSERT( false );
750 delete that;
752 bootstrap_map::release();
756 //----------------------------------------------------------------------------
758 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
759 rtlBootstrapHandle handle,
760 rtl_uString * pName,
761 rtl_uString ** ppValue,
762 rtl_uString * pDefault
763 ) SAL_THROW_EXTERN_C()
765 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
767 sal_Bool found = sal_False;
768 if(ppValue && pName)
770 if (handle == 0)
771 handle = get_static_bootstrap_handle();
772 found = static_cast< Bootstrap_Impl * >( handle )->getValue(
773 pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
776 return found;
779 //----------------------------------------------------------------------------
781 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
782 rtlBootstrapHandle handle,
783 rtl_uString ** ppIniName
784 ) SAL_THROW_EXTERN_C()
786 if(ppIniName)
788 if(handle)
790 Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
791 rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
793 else
795 const OUString & iniName = getIniFileName_Impl();
796 rtl_uString_assign(ppIniName, iniName.pData);
801 //----------------------------------------------------------------------------
803 void SAL_CALL rtl_bootstrap_setIniFileName (
804 rtl_uString * pName
805 ) SAL_THROW_EXTERN_C()
807 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
808 OUString & file = getIniFileName_Impl();
809 file = pName;
812 //----------------------------------------------------------------------------
814 sal_Bool SAL_CALL rtl_bootstrap_get (
815 rtl_uString * pName,
816 rtl_uString ** ppValue,
817 rtl_uString * pDefault
818 ) SAL_THROW_EXTERN_C()
820 return rtl_bootstrap_get_from_handle(0, pName, ppValue, pDefault);
823 //----------------------------------------------------------------------------
825 void SAL_CALL rtl_bootstrap_set (
826 rtl_uString * pName,
827 rtl_uString * pValue
828 ) SAL_THROW_EXTERN_C()
830 const OUString name( pName );
831 const OUString value( pValue );
833 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
835 NameValueList& r_rtl_bootstrap_set_list = rtl_bootstrap_set_list::get();
836 NameValueList::iterator iPos( r_rtl_bootstrap_set_list.begin() );
837 NameValueList::iterator iEnd( r_rtl_bootstrap_set_list.end() );
838 for ( ; iPos != iEnd; ++iPos )
840 if (iPos->sName.equals( name ))
842 iPos->sValue = value;
843 return;
847 #if OSL_DEBUG_LEVEL > 1
848 OString cstr_name( OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
849 OString cstr_value( OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ) );
850 OSL_TRACE(
851 "bootstrap.cxx: explicitly setting: name=%s value=%s\n",
852 cstr_name.getStr(), cstr_value.getStr() );
853 #endif /* OSL_DEBUG_LEVEL > 1 */
855 r_rtl_bootstrap_set_list.push_back( rtl_bootstrap_NameValue( name, value ) );
858 //----------------------------------------------------------------------------
860 void SAL_CALL rtl_bootstrap_expandMacros_from_handle (
861 rtlBootstrapHandle handle,
862 rtl_uString ** macro
863 ) SAL_THROW_EXTERN_C()
865 if (handle == NULL) {
866 handle = get_static_bootstrap_handle();
868 OUString expanded( expandMacros( static_cast< Bootstrap_Impl * >( handle ),
869 * reinterpret_cast< OUString const * >( macro ),
870 LOOKUP_MODE_NORMAL, NULL ) );
871 rtl_uString_assign( macro, expanded.pData );
874 //----------------------------------------------------------------------------
876 void SAL_CALL rtl_bootstrap_expandMacros(
877 rtl_uString ** macro )
878 SAL_THROW_EXTERN_C()
880 rtl_bootstrap_expandMacros_from_handle(NULL, macro);
883 void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
884 SAL_THROW_EXTERN_C()
886 OSL_ASSERT(value != NULL);
887 rtl::OUStringBuffer b;
888 for (sal_Int32 i = 0; i < value->length; ++i) {
889 sal_Unicode c = value->buffer[i];
890 if (c == '$' || c == '\\') {
891 b.append(sal_Unicode('\\'));
893 b.append(c);
895 rtl_uString_assign(encoded, b.makeStringAndClear().pData);
898 namespace {
900 int hex(sal_Unicode c) {
901 return
902 c >= '0' && c <= '9' ? c - '0' :
903 c >= 'A' && c <= 'F' ? c - 'A' + 10 :
904 c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
907 sal_Unicode read(rtl::OUString const & text, sal_Int32 * pos, bool * escaped) {
908 OSL_ASSERT(
909 pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
910 sal_Unicode c = text[(*pos)++];
911 if (c == '\\') {
912 int n1, n2, n3, n4;
913 if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
914 ((n1 = hex(text[*pos + 1])) >= 0) &&
915 ((n2 = hex(text[*pos + 2])) >= 0) &&
916 ((n3 = hex(text[*pos + 3])) >= 0) &&
917 ((n4 = hex(text[*pos + 4])) >= 0))
919 *pos += 5;
920 *escaped = true;
921 return static_cast< sal_Unicode >(
922 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
923 } else if (*pos < text.getLength()) {
924 *escaped = true;
925 return text[(*pos)++];
928 *escaped = false;
929 return c;
932 rtl::OUString lookup(
933 Bootstrap_Impl const * file, LookupMode mode, bool override,
934 rtl::OUString const & key, ExpandRequestLink const * requestStack)
936 rtl::OUString v;
937 (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
938 key, &v.pData, NULL, mode, override, requestStack);
939 return v;
942 rtl::OUString expandMacros(
943 Bootstrap_Impl const * file, rtl::OUString const & text, LookupMode mode,
944 ExpandRequestLink const * requestStack)
946 rtl::OUStringBuffer buf;
947 for (sal_Int32 i = 0; i < text.getLength();) {
948 bool escaped;
949 sal_Unicode c = read(text, &i, &escaped);
950 if (escaped || c != '$') {
951 buf.append(c);
952 } else {
953 if (i < text.getLength() && text[i] == '{') {
954 ++i;
955 sal_Int32 p = i;
956 sal_Int32 nesting = 0;
957 rtl::OUString seg[3];
958 int n = 0;
959 while (i < text.getLength()) {
960 sal_Int32 j = i;
961 c = read(text, &i, &escaped);
962 if (!escaped) {
963 switch (c) {
964 case '{':
965 ++nesting;
966 break;
967 case '}':
968 if (nesting == 0) {
969 seg[n++] = text.copy(p, j - p);
970 goto done;
971 } else {
972 --nesting;
974 break;
975 case ':':
976 if (nesting == 0 && n < 2) {
977 seg[n++] = text.copy(p, j - p);
978 p = i;
980 break;
984 done:
985 for (int j = 0; j < n; ++j) {
986 seg[j] = expandMacros(file, seg[j], mode, requestStack);
988 if (n == 1) {
989 buf.append(lookup(file, mode, false, seg[0], requestStack));
990 } else if (n == 2 && seg[0] == ".link") {
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(osl_File_OpenFlag_Read) ==
997 osl::FileBase::E_None) &&
998 f.readLine(seq) == osl::FileBase::E_None &&
999 rtl_convertStringToUString(
1000 &line.pData,
1001 reinterpret_cast< char const * >(
1002 seq.getConstArray()),
1003 seq.getLength(), RTL_TEXTENCODING_UTF8,
1004 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
1005 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
1006 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) &&
1007 (osl::File::getFileURLFromSystemPath(line, url) ==
1008 osl::FileBase::E_None))
1010 try {
1011 buf.append(
1012 rtl::Uri::convertRelToAbs(seg[1], url));
1013 } catch (const rtl::MalformedUriException &) {}
1015 } else if (n == 3 && seg[0] == ".override") {
1016 rtl::Bootstrap b(seg[1]);
1017 Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(
1018 b.getHandle());
1019 buf.append(
1020 lookup(f, mode, f != NULL, seg[2], requestStack));
1021 } else {
1022 if (n == 3 && seg[1].isEmpty()) {
1023 // For backward compatibility, treat ${file::key} the
1024 // same as just ${file:key}:
1025 seg[1] = seg[2];
1026 n = 2;
1028 if (n == 2) {
1029 buf.append(
1030 lookup(
1031 static_cast< Bootstrap_Impl * >(
1032 rtl::Bootstrap(seg[0]).getHandle()),
1033 mode, false, seg[1], requestStack));
1034 } else {
1035 // Going through osl::Profile, this code erroneously
1036 // does not recursively expand macros in the resulting
1037 // replacement text (and if it did, it would fail to
1038 // detect cycles that pass through here):
1039 buf.append(
1040 rtl::OStringToOUString(
1041 osl::Profile(seg[0]).readString(
1042 rtl::OUStringToOString(
1043 seg[1], RTL_TEXTENCODING_UTF8),
1044 rtl::OUStringToOString(
1045 seg[2], RTL_TEXTENCODING_UTF8),
1046 rtl::OString()),
1047 RTL_TEXTENCODING_UTF8));
1050 } else {
1051 rtl::OUStringBuffer kbuf;
1052 for (; i < text.getLength();) {
1053 sal_Int32 j = i;
1054 c = read(text, &j, &escaped);
1055 if (!escaped &&
1056 (c == ' ' || c == '$' || c == '-' || c == '/' ||
1057 c == ';' || c == '\\'))
1059 break;
1061 kbuf.append(c);
1062 i = j;
1064 buf.append(
1065 lookup(
1066 file, mode, false, kbuf.makeStringAndClear(),
1067 requestStack));
1071 return buf.makeStringAndClear();
1076 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */