Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / sal / rtl / bootstrap.cxx
blob86f22c823e8b4e26d6a4c0d80a6ad81926d17bd5
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 .
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>
43 #include <list>
45 #include "getexecutablefile.hxx"
47 #ifdef ANDROID
48 #include <osl/detail/android-bootstrap.h>
49 #endif
51 #ifdef IOS
52 #include <premac.h>
53 #import <Foundation/Foundation.h>
54 #include <postmac.h>
55 #endif
57 using osl::DirectoryItem;
58 using osl::FileStatus;
60 using rtl::OString;
61 using rtl::OUString;
62 using rtl::OUStringToOString;
64 struct Bootstrap_Impl;
66 namespace {
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))
81 return true;
82 } else {
83 *url = rtl::OUString();
84 return false;
88 enum LookupMode {
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;
95 rtl::OUString key;
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
122 OUString sName;
123 OUString sValue;
125 inline rtl_bootstrap_NameValue() SAL_THROW( () )
127 inline rtl_bootstrap_NameValue(
128 OUString const & name, OUString const & value ) SAL_THROW( () )
129 : sName( name ),
130 sValue( value )
134 typedef std::list<rtl_bootstrap_NameValue> NameValueList;
136 bool find(
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) {
143 *value = i->sValue;
144 return true;
147 return false;
150 namespace {
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, '=' );
176 if( nIndex >= 0 )
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;
199 bool found = false;
201 for( NameValueList::iterator ii = pNameValueList->begin() ;
202 ii != pNameValueList->end() ;
203 ++ii )
205 if( (*ii).sName.equals(key) )
207 *value = (*ii).sValue;
208 found = true;
209 break;
213 return found;
216 inline void getExecutableFile_Impl (rtl_uString ** ppFileURL)
218 osl_bootstrap_getExecutableFile_Impl (ppFileURL);
221 static void getExecutableDirectory_Impl (rtl_uString ** ppDirURL)
223 OUString fileName;
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;
236 if( ! pStaticName )
238 OUString fileName;
240 #if defined IOS
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);
254 #else
255 if(getFromCommandLineArgs(
256 OUString("INIFILENAME"), &fileName))
258 resolvePathnameUrl(&fileName);
260 else
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());
270 progExt = ".exe";
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 );
283 #endif
285 #endif
287 static OUString theFileName;
288 if(fileName.getLength())
289 theFileName = fileName;
291 pStaticName = &theFileName;
294 return *pStaticName;
297 // #111772#
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;
314 OUString _iniName;
316 explicit Bootstrap_Impl (OUString const & rIniName);
317 ~Bootstrap_Impl();
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); }
324 bool getValue(
325 rtl::OUString const & key, rtl_uString ** value,
326 rtl_uString * defaultValue, LookupMode mode, bool override,
327 ExpandRequestLink const * requestStack) const;
328 bool getDirectValue(
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;
334 void expandValue(
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 )
341 : _nRefCount( 0 ),
342 _base_ini( 0 ),
343 _iniName (rIniName)
345 OUString base_ini( getIniFileName_Impl() );
346 // normalize path
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('=');
375 if (nIndex >= 1)
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);
386 OSL_TRACE(
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
397 else
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()
407 if (_base_ini != 0)
408 rtl_bootstrap_args_close( _base_ini );
411 namespace {
413 Bootstrap_Impl * get_static_bootstrap_handle() SAL_THROW(())
415 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
416 static Bootstrap_Impl * s_handle = 0;
417 if (s_handle == 0)
419 OUString iniName (getIniFileName_Impl());
420 s_handle = static_cast< Bootstrap_Impl * >(
421 rtl_bootstrap_args_open( iniName.pData ) );
422 if (s_handle == 0)
424 Bootstrap_Impl * that = new Bootstrap_Impl( iniName );
425 ++that->_nRefCount;
426 s_handle = that;
429 return s_handle;
432 struct FundamentalIniData: private boost::noncopyable {
433 rtlBootstrapHandle ini;
435 FundamentalIniData() {
436 OUString uri;
437 ini =
438 ((static_cast< Bootstrap_Impl * >(get_static_bootstrap_handle())->
439 getValue(
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)
457 const
459 if (mode == LOOKUP_MODE_NORMAL && key == "URE_BOOTSTRAP") {
460 mode = LOOKUP_MODE_URE_BOOTSTRAP;
462 if (override && getDirectValue(key, value, mode, requestStack)) {
463 return true;
465 if (key == "_OS") {
466 rtl_uString_assign(
467 value, rtl::OUString(RTL_OS).pData);
468 return true;
470 if (key == "_ARCH") {
471 rtl_uString_assign(
472 value, rtl::OUString(RTL_ARCH).pData);
473 return true;
475 if (key == "_CPPU_ENV") {
476 rtl_uString_assign(
477 value,
478 (rtl::OUString(
479 SAL_STRINGIFY(CPPU_ENV)).
480 pData));
481 return true;
483 #ifdef ANDROID
484 if (key == "APP_DATA_DIR") {
485 const char *app_data_dir = lo_get_app_data_dir();
486 rtl_uString_assign(
487 value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
488 return true;
490 #endif
491 #ifdef IOS
492 if (key == "APP_DATA_DIR") {
493 const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] UTF8String];
494 rtl_uString_assign(
495 value, rtl::OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
496 return true;
498 #endif
499 if (key == "ORIGIN") {
500 rtl_uString_assign(
501 value,
502 _iniName.copy(
503 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
504 return true;
506 if (getAmbienceValue(key, value, mode, requestStack)) {
507 return true;
509 if (key == "SYSUSERCONFIG") {
510 rtl::OUString v;
511 bool b = osl::Security().getConfigDir(v);
512 EnsureNoFinalSlash(v);
513 rtl_uString_assign(value, v.pData);
514 return b;
516 if (key == "SYSUSERHOME") {
517 rtl::OUString v;
518 bool b = osl::Security().getHomeDir(v);
519 EnsureNoFinalSlash(v);
520 rtl_uString_assign(value, v.pData);
521 return b;
523 if (key == "SYSBINDIR") {
524 getExecutableDirectory_Impl(value);
525 return true;
527 if (_base_ini != NULL &&
528 _base_ini->getDirectValue(key, value, mode, requestStack))
530 return true;
532 if (!override && getDirectValue(key, value, mode, requestStack)) {
533 return true;
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))
541 return true;
544 if (defaultValue != NULL) {
545 rtl_uString_assign(value, defaultValue);
546 return true;
548 rtl_uString_new(value);
549 return false;
552 bool Bootstrap_Impl::getDirectValue(
553 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
554 ExpandRequestLink const * requestStack) const
556 rtl::OUString v;
557 if (find(_nameValueList, key, &v)) {
558 expandValue(value, v, mode, this, key, requestStack);
559 return true;
560 } else {
561 return false;
565 bool Bootstrap_Impl::getAmbienceValue(
566 rtl::OUString const & key, rtl_uString ** value, LookupMode mode,
567 ExpandRequestLink const * requestStack) const
569 rtl::OUString v;
570 bool f;
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);
579 return true;
580 } else {
581 return false;
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
590 rtl_uString_assign(
591 value,
592 (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
593 text :
594 recursivelyExpandMacros(
595 this, text,
596 (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
597 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
598 requestFile, requestKey, requestStack)).pData);
601 namespace {
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()):
611 static t * get() {
612 if (m_map == NULL) {
613 m_map = new t;
615 return m_map;
618 static void release() {
619 if (m_map != NULL && m_map->empty()) {
620 delete m_map;
621 m_map = NULL;
625 private:
626 static t * m_map;
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 );
639 // normalize path
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 ))
645 return 0;
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();
656 guard.clear();
657 that = new Bootstrap_Impl( iniName );
658 guard.reset();
659 p_bootstrap_map = bootstrap_map::get();
660 iFind = p_bootstrap_map->find( iniName );
661 if (iFind == p_bootstrap_map->end())
663 ++that->_nRefCount;
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 );
670 else
672 Bootstrap_Impl * obsolete = that;
673 that = iFind->second;
674 ++that->_nRefCount;
675 guard.clear();
676 delete obsolete;
679 else
681 that = iFind->second;
682 ++that->_nRefCount;
684 return static_cast< rtlBootstrapHandle >( that );
687 void SAL_CALL rtl_bootstrap_args_close (
688 rtlBootstrapHandle handle
689 ) SAL_THROW_EXTERN_C()
691 if (handle == 0)
692 return;
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();
697 OSL_ASSERT(
698 p_bootstrap_map->find( that->_iniName )->second == that );
699 --that->_nRefCount;
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
705 nLeaking = 0;
706 #elif OSL_DEBUG_LEVEL > 1 // debug
707 nLeaking = 1;
708 #endif /* OSL_DEBUG_LEVEL */
710 if (p_bootstrap_map->size() > nLeaking)
712 ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
713 if (erased != 1) {
714 OSL_ASSERT( false );
716 delete that;
718 bootstrap_map::release();
722 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
723 rtlBootstrapHandle handle,
724 rtl_uString * pName,
725 rtl_uString ** ppValue,
726 rtl_uString * pDefault
727 ) SAL_THROW_EXTERN_C()
729 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
731 bool found = false;
732 if(ppValue && pName)
734 if (handle == 0)
735 handle = get_static_bootstrap_handle();
736 found = static_cast< Bootstrap_Impl * >( handle )->getValue(
737 pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, NULL );
740 return found;
743 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
744 rtlBootstrapHandle handle,
745 rtl_uString ** ppIniName
746 ) SAL_THROW_EXTERN_C()
748 if(ppIniName)
750 if(handle)
752 Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
753 rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
755 else
757 const OUString & iniName = getIniFileName_Impl();
758 rtl_uString_assign(ppIniName, iniName.pData);
763 void SAL_CALL rtl_bootstrap_setIniFileName (
764 rtl_uString * pName
765 ) SAL_THROW_EXTERN_C()
767 osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
768 OUString & file = getIniFileName_Impl();
769 file = pName;
772 sal_Bool SAL_CALL rtl_bootstrap_get (
773 rtl_uString * pName,
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 (
782 rtl_uString * pName,
783 rtl_uString * pValue
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;
799 return;
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 ) );
806 OSL_TRACE(
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,
816 rtl_uString ** macro
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 )
830 SAL_THROW_EXTERN_C()
832 rtl_bootstrap_expandMacros_from_handle(NULL, macro);
835 void rtl_bootstrap_encode( rtl_uString const * value, rtl_uString ** encoded )
836 SAL_THROW_EXTERN_C()
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 == '\\') {
843 b.append('\\');
845 b.append(c);
847 rtl_uString_assign(encoded, b.makeStringAndClear().pData);
850 namespace {
852 int hex(sal_Unicode c) {
853 return
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) {
860 OSL_ASSERT(
861 pos != NULL && *pos >= 0 && *pos < text.getLength() && escaped != NULL);
862 sal_Unicode c = text[(*pos)++];
863 if (c == '\\') {
864 int n1, n2, n3, n4;
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))
871 *pos += 5;
872 *escaped = true;
873 return static_cast< sal_Unicode >(
874 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
875 } else if (*pos < text.getLength()) {
876 *escaped = true;
877 return text[(*pos)++];
880 *escaped = false;
881 return c;
884 rtl::OUString lookup(
885 Bootstrap_Impl const * file, LookupMode mode, bool override,
886 rtl::OUString const & key, ExpandRequestLink const * requestStack)
888 rtl::OUString v;
889 (file == NULL ? get_static_bootstrap_handle() : file)->getValue(
890 key, &v.pData, NULL, mode, override, requestStack);
891 return v;
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();) {
900 bool escaped;
901 sal_Unicode c = read(text, &i, &escaped);
902 if (escaped || c != '$') {
903 buf.append(c);
904 } else {
905 if (i < text.getLength() && text[i] == '{') {
906 ++i;
907 sal_Int32 p = i;
908 sal_Int32 nesting = 0;
909 rtl::OUString seg[3];
910 int n = 0;
911 while (i < text.getLength()) {
912 sal_Int32 j = i;
913 c = read(text, &i, &escaped);
914 if (!escaped) {
915 switch (c) {
916 case '{':
917 ++nesting;
918 break;
919 case '}':
920 if (nesting == 0) {
921 seg[n++] = text.copy(p, j - p);
922 goto done;
923 } else {
924 --nesting;
926 break;
927 case ':':
928 if (nesting == 0 && n < 2) {
929 seg[n++] = text.copy(p, j - p);
930 p = i;
932 break;
936 done:
937 for (int j = 0; j < n; ++j) {
938 seg[j] = expandMacros(file, seg[j], mode, requestStack);
940 if (n == 1) {
941 buf.append(lookup(file, mode, false, seg[0], requestStack));
942 } else if (n == 2 && seg[0] == ".link") {
943 osl::File f(seg[1]);
944 rtl::ByteSequence seq;
945 rtl::OUString line;
946 rtl::OUString url;
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(
952 &line.pData,
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))
962 try {
963 buf.append(
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 * >(
970 b.getHandle());
971 buf.append(
972 lookup(f, mode, f != NULL, seg[2], requestStack));
973 } else {
974 if (n == 3 && seg[1].isEmpty()) {
975 // For backward compatibility, treat ${file::key} the
976 // same as just ${file:key}:
977 seg[1] = seg[2];
978 n = 2;
980 if (n == 2) {
981 buf.append(
982 lookup(
983 static_cast< Bootstrap_Impl * >(
984 rtl::Bootstrap(seg[0]).getHandle()),
985 mode, false, seg[1], requestStack));
986 } else {
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):
991 buf.append(
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),
998 rtl::OString()),
999 RTL_TEXTENCODING_UTF8));
1002 } else {
1003 rtl::OUStringBuffer kbuf;
1004 for (; i < text.getLength();) {
1005 sal_Int32 j = i;
1006 c = read(text, &j, &escaped);
1007 if (!escaped &&
1008 (c == ' ' || c == '$' || c == '-' || c == '/' ||
1009 c == ';' || c == '\\'))
1011 break;
1013 kbuf.append(c);
1014 i = j;
1016 buf.append(
1017 lookup(
1018 file, mode, false, kbuf.makeStringAndClear(),
1019 requestStack));
1023 return buf.makeStringAndClear();
1028 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */