lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / sal / rtl / bootstrap.cxx
blob897de890d890bf74f1d0cee8f6673c53c04f0fa3
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 .
19 #include <config_features.h>
20 #include <config_folders.h>
22 #include <rtl/bootstrap.h>
23 #include <rtl/bootstrap.hxx>
24 #include <osl/diagnose.h>
25 #include <osl/module.h>
26 #include <osl/process.h>
27 #include <osl/file.hxx>
28 #include <osl/mutex.hxx>
29 #include <osl/profile.hxx>
30 #include <osl/security.hxx>
31 #include <rtl/alloc.h>
32 #include <rtl/string.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <rtl/ustring.hxx>
35 #include <rtl/byteseq.hxx>
36 #include <rtl/instance.hxx>
37 #include <rtl/malformeduriexception.hxx>
38 #include <rtl/uri.hxx>
39 #include <sal/log.hxx>
41 #include <vector>
42 #include <algorithm>
43 #include <unordered_map>
45 #ifdef ANDROID
46 #include <osl/detail/android-bootstrap.h>
47 #endif
49 #ifdef IOS
50 #include <premac.h>
51 #import <Foundation/Foundation.h>
52 #include <postmac.h>
53 #endif
55 using osl::DirectoryItem;
56 using osl::FileStatus;
58 struct Bootstrap_Impl;
60 namespace
63 static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
65 bool isPathnameUrl(OUString const & url)
67 return url.matchIgnoreAsciiCase(VND_SUN_STAR_PATHNAME);
70 bool resolvePathnameUrl(OUString * url)
72 OSL_ASSERT(url);
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;
80 *url = OUString();
81 return false;
84 enum LookupMode {
85 LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
86 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
88 struct ExpandRequestLink {
89 ExpandRequestLink const * next;
90 Bootstrap_Impl const * file;
91 OUString const key;
94 OUString expandMacros(
95 Bootstrap_Impl const * file, OUString const & text, LookupMode mode,
96 ExpandRequestLink const * requestStack);
98 OUString recursivelyExpandMacros(
99 Bootstrap_Impl const * file, OUString const & text, LookupMode mode,
100 Bootstrap_Impl const * requestFile, OUString const & requestKey,
101 ExpandRequestLink const * requestStack)
103 for (; requestStack; requestStack = requestStack->next) {
104 if (requestStack->file == requestFile &&
105 requestStack->key == requestKey)
107 return OUString("***RECURSION DETECTED***");
110 ExpandRequestLink link = { requestStack, requestFile, requestKey };
111 return expandMacros(file, text, mode, &link);
114 } // end namespace
116 struct rtl_bootstrap_NameValue
118 OUString sName;
119 OUString sValue;
121 rtl_bootstrap_NameValue()
123 rtl_bootstrap_NameValue(OUString const & name, OUString const & value )
124 : sName( name ),
125 sValue( value )
129 typedef std::vector<rtl_bootstrap_NameValue> NameValueVector;
131 static bool find(
132 NameValueVector const & vector, OUString const & key,
133 OUString * value)
135 OSL_ASSERT(value);
136 for (NameValueVector::const_iterator i(vector.begin()); i != vector.end(); ++i)
138 if (i->sName == key)
140 *value = i->sValue;
141 return true;
144 return false;
147 namespace
149 struct rtl_bootstrap_set_vector :
150 public rtl::Static< NameValueVector, rtl_bootstrap_set_vector > {};
153 static bool getFromCommandLineArgs(
154 OUString const & key, OUString * value )
156 OSL_ASSERT(value);
158 static NameValueVector *pNameValueVector = nullptr;
159 if (!pNameValueVector)
161 static NameValueVector nameValueVector;
163 sal_Int32 nArgCount = osl_getCommandArgCount();
164 for(sal_Int32 i = 0; i < nArgCount; ++ i)
166 rtl_uString *pArg = nullptr;
167 osl_getCommandArg( i, &pArg );
168 if( (pArg->buffer[0] == '-' || pArg->buffer[0] == '/' ) &&
169 pArg->buffer[1] == 'e' &&
170 pArg->buffer[2] == 'n' &&
171 pArg->buffer[3] == 'v' &&
172 pArg->buffer[4] == ':' )
174 sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' );
176 if( nIndex >= 0 )
178 rtl_bootstrap_NameValue nameValue;
179 nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 );
180 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);
192 nameValueVector.push_back( nameValue );
195 rtl_uString_release( pArg );
197 pNameValueVector = &nameValueVector;
200 bool found = false;
202 for(NameValueVector::iterator ii = pNameValueVector->begin();
203 ii != pNameValueVector->end();
204 ++ii)
206 if ((*ii).sName == key)
208 *value = (*ii).sValue;
209 found = true;
210 break;
214 return found;
217 static void getExecutableDirectory_Impl(rtl_uString ** ppDirURL)
219 OUString fileName;
220 osl_getExecutableFile(&(fileName.pData));
222 sal_Int32 nDirEnd = fileName.lastIndexOf('/');
223 OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
225 rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
228 static OUString & getIniFileName_Impl()
230 static OUString aStaticName = []() {
231 OUString fileName;
233 #if defined IOS
234 // On iOS hardcode the inifile as "rc" in the .app
235 // directory. Apps are self-contained anyway, there is no
236 // possibility to have several "applications" in the same
237 // installation location with different inifiles.
238 const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String];
239 fileName = OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
240 resolvePathnameUrl(&fileName);
241 #elif defined ANDROID
242 // Apps are self-contained on Android, too, can as well hardcode
243 // it as "rc" in the "/assets" directory, i.e. inside the app's
244 // .apk (zip) archive as the /assets/rc file.
245 fileName = OUString("vnd.sun.star.pathname:/assets/rc");
246 resolvePathnameUrl(&fileName);
247 #else
248 if (getFromCommandLineArgs("INIFILENAME", &fileName))
250 resolvePathnameUrl(&fileName);
252 else
254 osl_getExecutableFile(&(fileName.pData));
256 // get rid of a potential executable extension
257 OUString progExt = ".bin";
258 if (fileName.getLength() > progExt.getLength()
259 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
261 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
264 progExt = ".exe";
265 if (fileName.getLength() > progExt.getLength()
266 && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
268 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
271 // append config file suffix
272 fileName += SAL_CONFIGFILE("");
274 #ifdef MACOSX
275 // We keep only executables in the MacOS folder, and all
276 // rc files in LIBO_ETC_FOLDER (typically "Resources").
277 sal_Int32 off = fileName.lastIndexOf( "/MacOS/" );
278 if (off != -1)
279 fileName = fileName.replaceAt(off + 1, strlen("MacOS"), LIBO_ETC_FOLDER);
280 #endif
282 #endif
284 return fileName;
285 }();
287 return aStaticName;
290 // ensure the given file url has no final slash
292 static void EnsureNoFinalSlash (OUString & url)
294 sal_Int32 i = url.getLength();
296 if (i > 0 && url[i - 1] == '/')
297 url = url.copy(0, i - 1);
300 struct Bootstrap_Impl
302 sal_Int32 _nRefCount;
303 Bootstrap_Impl * _base_ini;
305 NameValueVector _nameValueVector;
306 OUString const _iniName;
308 explicit Bootstrap_Impl (OUString const & rIniName);
309 ~Bootstrap_Impl();
311 static void * operator new (std::size_t n)
312 { return malloc (sal_uInt32(n)); }
313 static void operator delete (void * p , std::size_t)
314 { free (p); }
316 bool getValue(
317 OUString const & key, rtl_uString ** value,
318 rtl_uString * defaultValue, LookupMode mode, bool override,
319 ExpandRequestLink const * requestStack) const;
320 bool getDirectValue(
321 OUString const & key, rtl_uString ** value, LookupMode mode,
322 ExpandRequestLink const * requestStack) const;
323 bool getAmbienceValue(
324 OUString const & key, rtl_uString ** value, LookupMode mode,
325 ExpandRequestLink const * requestStack) const;
326 void expandValue(
327 rtl_uString ** value, OUString const & text, LookupMode mode,
328 Bootstrap_Impl const * requestFile, OUString const & requestKey,
329 ExpandRequestLink const * requestStack) const;
332 Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
333 : _nRefCount( 0 ),
334 _base_ini( nullptr ),
335 _iniName (rIniName)
337 OUString base_ini(getIniFileName_Impl());
338 // normalize path
339 FileStatus status( osl_FileStatus_Mask_FileURL );
340 DirectoryItem dirItem;
341 if (DirectoryItem::get(base_ini, dirItem) == DirectoryItem::E_None &&
342 dirItem.getFileStatus(status) == DirectoryItem::E_None)
344 base_ini = status.getFileURL();
345 if (rIniName != base_ini)
347 _base_ini = static_cast< Bootstrap_Impl * >(
348 rtl_bootstrap_args_open(base_ini.pData));
351 SAL_INFO("sal.bootstrap", "Bootstrap_Impl(): sFile=" << _iniName);
352 oslFileHandle handle;
353 if (!_iniName.isEmpty() &&
354 osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read) == osl_File_E_None)
356 rtl::ByteSequence seq;
358 while (osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)) == osl_File_E_None)
360 OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength());
361 sal_Int32 nIndex = line.indexOf('=');
362 if (nIndex >= 1)
364 struct rtl_bootstrap_NameValue nameValue;
365 nameValue.sName = OStringToOUString(line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US);
366 nameValue.sValue = OStringToOUString(line.copy(nIndex+1).trim(), RTL_TEXTENCODING_UTF8);
368 SAL_INFO("sal.bootstrap", "pushing: name=" << nameValue.sName << " value=" << nameValue.sValue);
370 _nameValueVector.push_back(nameValue);
373 osl_closeFile(handle);
375 else
377 SAL_INFO( "sal.bootstrap", "couldn't open file: " << _iniName );
381 Bootstrap_Impl::~Bootstrap_Impl()
383 if (_base_ini)
384 rtl_bootstrap_args_close( _base_ini );
387 namespace {
389 Bootstrap_Impl * get_static_bootstrap_handle()
391 static Bootstrap_Impl* s_handle = []() {
392 OUString iniName(getIniFileName_Impl());
393 Bootstrap_Impl* that = static_cast<Bootstrap_Impl*>(rtl_bootstrap_args_open(iniName.pData));
394 if (!that)
396 that = new Bootstrap_Impl(iniName);
397 ++that->_nRefCount;
399 return that;
400 }();
402 return s_handle;
405 struct FundamentalIniData
407 rtlBootstrapHandle ini;
409 FundamentalIniData()
411 OUString uri;
412 ini =
413 (get_static_bootstrap_handle()->getValue(
414 "URE_BOOTSTRAP", &uri.pData, nullptr, LOOKUP_MODE_NORMAL, false,
415 nullptr)
416 && resolvePathnameUrl(&uri))
417 ? rtl_bootstrap_args_open(uri.pData) : nullptr;
420 ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
422 FundamentalIniData(const FundamentalIniData&) = delete;
423 FundamentalIniData& operator=(const FundamentalIniData&) = delete;
426 struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
431 bool Bootstrap_Impl::getValue(
432 OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
433 LookupMode mode, bool override, ExpandRequestLink const * requestStack)
434 const
436 if (mode == LOOKUP_MODE_NORMAL && key == "URE_BOOTSTRAP")
437 mode = LOOKUP_MODE_URE_BOOTSTRAP;
439 if (override && getDirectValue(key, value, mode, requestStack))
440 return true;
442 if (key == "_OS")
444 rtl_uString_assign(
445 value, OUString(RTL_OS).pData);
446 return true;
449 if (key == "_ARCH")
451 rtl_uString_assign(
452 value, OUString(RTL_ARCH).pData);
453 return true;
456 if (key == "_CPPU_ENV")
458 rtl_uString_assign(
459 value,
460 (OUString(
461 SAL_STRINGIFY(CPPU_ENV)).
462 pData));
463 return true;
466 #ifdef ANDROID
467 if (key == "APP_DATA_DIR")
469 const char *app_data_dir = lo_get_app_data_dir();
470 rtl_uString_assign(
471 value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
472 return true;
474 #endif
476 #ifdef IOS
477 if (key == "APP_DATA_DIR")
479 const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLPathAllowedCharacterSet]] UTF8String];
480 rtl_uString_assign(
481 value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
482 return true;
484 #endif
486 if (key == "ORIGIN")
488 rtl_uString_assign(
489 value,
490 _iniName.copy(
491 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
492 return true;
495 if (getAmbienceValue(key, value, mode, requestStack))
496 return true;
498 if (key == "SYSUSERCONFIG")
500 OUString v;
501 bool b = osl::Security().getConfigDir(v);
502 EnsureNoFinalSlash(v);
503 rtl_uString_assign(value, v.pData);
504 return b;
507 if (key == "SYSUSERHOME")
509 OUString v;
510 bool b = osl::Security().getHomeDir(v);
511 EnsureNoFinalSlash(v);
512 rtl_uString_assign(value, v.pData);
513 return b;
516 if (key == "SYSBINDIR")
518 getExecutableDirectory_Impl(value);
519 return true;
522 if (_base_ini != nullptr && _base_ini->getDirectValue(key, value, mode, requestStack))
523 return true;
525 if (!override && getDirectValue(key, value, mode, requestStack))
526 return true;
528 if (mode == LOOKUP_MODE_NORMAL)
530 FundamentalIniData const & d = FundamentalIni::get();
531 Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
532 if (b != nullptr && b != this && b->getDirectValue(key, value, mode, requestStack))
533 return true;
536 if (defaultValue != nullptr)
538 rtl_uString_assign(value, defaultValue);
539 return true;
542 rtl_uString_new(value);
543 return false;
546 bool Bootstrap_Impl::getDirectValue(
547 OUString const & key, rtl_uString ** value, LookupMode mode,
548 ExpandRequestLink const * requestStack) const
550 OUString v;
551 if (find(_nameValueVector, key, &v))
553 expandValue(value, v, mode, this, key, requestStack);
554 return true;
557 return false;
560 bool Bootstrap_Impl::getAmbienceValue(
561 OUString const & key, rtl_uString ** value, LookupMode mode,
562 ExpandRequestLink const * requestStack) const
564 OUString v;
565 bool f;
568 osl::MutexGuard g(osl::Mutex::getGlobalMutex());
569 f = find(rtl_bootstrap_set_vector::get(), key, &v);
572 if (f || getFromCommandLineArgs(key, &v) ||
573 osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
575 expandValue(value, v, mode, nullptr, key, requestStack);
576 return true;
579 return false;
582 void Bootstrap_Impl::expandValue(
583 rtl_uString ** value, OUString const & text, LookupMode mode,
584 Bootstrap_Impl const * requestFile, OUString const & requestKey,
585 ExpandRequestLink const * requestStack) const
587 rtl_uString_assign(
588 value,
589 (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
590 text :
591 recursivelyExpandMacros(
592 this, text,
593 (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
594 LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
595 requestFile, requestKey, requestStack)).pData);
598 namespace {
600 struct bootstrap_map {
601 typedef std::unordered_map<
602 OUString, Bootstrap_Impl * > t;
604 bootstrap_map(const bootstrap_map&) = delete;
605 bootstrap_map& operator=(const bootstrap_map&) = delete;
607 // get and release must only be called properly synchronized via some mutex
608 // (e.g., osl::Mutex::getGlobalMutex()):
610 static t * get()
612 if (!m_map)
613 m_map = new t;
615 return m_map;
618 static void release()
620 if (m_map != nullptr && m_map->empty())
622 delete m_map;
623 m_map = nullptr;
627 private:
628 static t * m_map;
631 bootstrap_map::t * bootstrap_map::m_map = nullptr;
635 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName)
637 OUString iniName( pIniName );
639 // normalize path
640 FileStatus status(osl_FileStatus_Mask_FileURL);
641 DirectoryItem dirItem;
642 if (DirectoryItem::get(iniName, dirItem) != DirectoryItem::E_None ||
643 dirItem.getFileStatus(status) != DirectoryItem::E_None)
645 return nullptr;
648 iniName = status.getFileURL();
650 Bootstrap_Impl * that;
651 osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex());
652 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
653 bootstrap_map::t::const_iterator iFind(p_bootstrap_map->find(iniName));
654 if (iFind == p_bootstrap_map->end())
656 bootstrap_map::release();
657 guard.clear();
658 that = new Bootstrap_Impl(iniName);
659 guard.reset();
660 p_bootstrap_map = bootstrap_map::get();
661 iFind = p_bootstrap_map->find(iniName);
662 if (iFind == p_bootstrap_map->end())
664 ++that->_nRefCount;
665 ::std::pair< bootstrap_map::t::iterator, bool > insertion(
666 p_bootstrap_map->emplace(iniName, that));
667 OSL_ASSERT(insertion.second);
669 else
671 Bootstrap_Impl * obsolete = that;
672 that = iFind->second;
673 ++that->_nRefCount;
674 guard.clear();
675 delete obsolete;
678 else
680 that = iFind->second;
681 ++that->_nRefCount;
683 return static_cast< rtlBootstrapHandle >( that );
686 void SAL_CALL rtl_bootstrap_args_close(rtlBootstrapHandle handle) SAL_THROW_EXTERN_C()
688 if (!handle)
689 return;
691 Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
693 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
694 bootstrap_map::t* p_bootstrap_map = bootstrap_map::get();
695 OSL_ASSERT(p_bootstrap_map->find(that->_iniName)->second == that);
696 --that->_nRefCount;
698 if (that->_nRefCount == 0)
700 std::size_t const nLeaking = 8; // only hold up to 8 files statically
701 if (p_bootstrap_map->size() > nLeaking)
703 ::std::size_t erased = p_bootstrap_map->erase( that->_iniName );
704 if (erased != 1) {
705 OSL_ASSERT( false );
707 delete that;
709 bootstrap_map::release();
713 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
714 rtlBootstrapHandle handle,
715 rtl_uString * pName,
716 rtl_uString ** ppValue,
717 rtl_uString * pDefault
720 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
722 bool found = false;
723 if(ppValue && pName)
725 if (!handle)
726 handle = get_static_bootstrap_handle();
728 found = static_cast< Bootstrap_Impl * >(handle)->getValue(
729 pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, nullptr );
732 return found;
735 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
736 rtlBootstrapHandle handle,
737 rtl_uString ** ppIniName
740 if(ppIniName)
742 if(handle)
744 Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
745 rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
747 else
749 const OUString & iniName = getIniFileName_Impl();
750 rtl_uString_assign(ppIniName, iniName.pData);
755 void SAL_CALL rtl_bootstrap_setIniFileName (
756 rtl_uString * pName
759 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
760 OUString & file = getIniFileName_Impl();
761 file = pName;
764 sal_Bool SAL_CALL rtl_bootstrap_get (
765 rtl_uString * pName,
766 rtl_uString ** ppValue,
767 rtl_uString * pDefault
770 return rtl_bootstrap_get_from_handle(nullptr, pName, ppValue, pDefault);
773 void SAL_CALL rtl_bootstrap_set (
774 rtl_uString * pName,
775 rtl_uString * pValue
778 const OUString name(pName);
779 const OUString value(pValue);
781 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
783 NameValueVector& r_rtl_bootstrap_set_vector= rtl_bootstrap_set_vector::get();
784 for (auto & item : r_rtl_bootstrap_set_vector)
786 if (item.sName == name)
788 item.sValue = value;
789 return;
793 SAL_INFO("sal.bootstrap", "explicitly getting: name=" << name << " value=" <<value);
795 r_rtl_bootstrap_set_vector.emplace_back(name, value);
798 void SAL_CALL rtl_bootstrap_expandMacros_from_handle(
799 rtlBootstrapHandle handle,
800 rtl_uString ** macro)
802 if (!handle)
803 handle = get_static_bootstrap_handle();
805 OUString expanded(expandMacros(static_cast< Bootstrap_Impl * >(handle),
806 OUString::unacquired(macro),
807 LOOKUP_MODE_NORMAL, nullptr));
808 rtl_uString_assign(macro, expanded.pData);
811 void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro)
813 rtl_bootstrap_expandMacros_from_handle(nullptr, macro);
816 void rtl_bootstrap_encode(rtl_uString const * value, rtl_uString ** encoded)
818 OSL_ASSERT(value);
819 OUStringBuffer b;
820 for (sal_Int32 i = 0; i < value->length; ++i)
822 sal_Unicode c = value->buffer[i];
823 if (c == '$' || c == '\\')
824 b.append('\\');
826 b.append(c);
829 rtl_uString_assign(encoded, b.makeStringAndClear().pData);
832 namespace {
834 int hex(sal_Unicode c)
836 return
837 c >= '0' && c <= '9' ? c - '0' :
838 c >= 'A' && c <= 'F' ? c - 'A' + 10 :
839 c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
842 sal_Unicode read(OUString const & text, sal_Int32 * pos, bool * escaped)
844 OSL_ASSERT(pos && *pos >= 0 && *pos < text.getLength() && escaped);
845 sal_Unicode c = text[(*pos)++];
846 if (c == '\\')
848 int n1, n2, n3, n4;
849 if (*pos < text.getLength() - 4 && text[*pos] == 'u' &&
850 ((n1 = hex(text[*pos + 1])) >= 0) &&
851 ((n2 = hex(text[*pos + 2])) >= 0) &&
852 ((n3 = hex(text[*pos + 3])) >= 0) &&
853 ((n4 = hex(text[*pos + 4])) >= 0))
855 *pos += 5;
856 *escaped = true;
857 return static_cast< sal_Unicode >(
858 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
861 if (*pos < text.getLength())
863 *escaped = true;
864 return text[(*pos)++];
868 *escaped = false;
869 return c;
872 OUString lookup(
873 Bootstrap_Impl const * file, LookupMode mode, bool override,
874 OUString const & key, ExpandRequestLink const * requestStack)
876 OUString v;
877 (file == nullptr ? get_static_bootstrap_handle() : file)->getValue(
878 key, &v.pData, nullptr, mode, override, requestStack);
879 return v;
882 OUString expandMacros(
883 Bootstrap_Impl const * file, OUString const & text, LookupMode mode,
884 ExpandRequestLink const * requestStack)
886 SAL_INFO("sal.bootstrap", "expandMacros called with: " << text);
887 OUStringBuffer buf;
889 for (sal_Int32 i = 0; i < text.getLength();)
891 bool escaped;
892 sal_Unicode c = read(text, &i, &escaped);
893 if (escaped || c != '$')
895 buf.append(c);
897 else
899 if (i < text.getLength() && text[i] == '{')
901 ++i;
902 sal_Int32 p = i;
903 sal_Int32 nesting = 0;
904 OUString seg[3];
905 int n = 0;
907 while (i < text.getLength())
909 sal_Int32 j = i;
910 c = read(text, &i, &escaped);
912 if (!escaped)
914 switch (c)
916 case '{':
917 ++nesting;
918 break;
919 case '}':
920 if (nesting == 0)
922 seg[n++] = text.copy(p, j - p);
923 goto done;
925 else
927 --nesting;
929 break;
930 case ':':
931 if (nesting == 0 && n < 2)
933 seg[n++] = text.copy(p, j - p);
934 p = i;
936 break;
940 done:
941 for (int j = 0; j < n; ++j)
943 seg[j] = expandMacros(file, seg[j], mode, requestStack);
946 if (n == 1)
948 buf.append(lookup(file, mode, false, seg[0], requestStack));
950 else if (n == 3 && seg[0] == ".override")
952 rtl::Bootstrap b(seg[1]);
953 Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
954 buf.append(lookup(f, mode, f != nullptr, seg[2], requestStack));
956 else
958 if (n == 3 && seg[1].isEmpty())
960 // For backward compatibility, treat ${file::key} the
961 // same as just ${file:key}:
962 seg[1] = seg[2];
963 n = 2;
966 if (n == 2)
968 buf.append(
969 lookup(
970 static_cast< Bootstrap_Impl * >(
971 rtl::Bootstrap(seg[0]).getHandle()),
972 mode, false, seg[1], requestStack));
974 else
976 // Going through osl::Profile, this code erroneously
977 // does not recursively expand macros in the resulting
978 // replacement text (and if it did, it would fail to
979 // detect cycles that pass through here):
980 buf.append(
981 OStringToOUString(
982 osl::Profile(seg[0]).readString(
983 OUStringToOString(
984 seg[1], RTL_TEXTENCODING_UTF8),
985 OUStringToOString(
986 seg[2], RTL_TEXTENCODING_UTF8),
987 OString()),
988 RTL_TEXTENCODING_UTF8));
992 else
994 OUStringBuffer kbuf;
995 for (; i < text.getLength();)
997 sal_Int32 j = i;
998 c = read(text, &j, &escaped);
999 if (!escaped &&
1000 (c == ' ' || c == '$' || c == '-' || c == '/' ||
1001 c == ';' || c == '\\'))
1003 break;
1006 kbuf.append(c);
1007 i = j;
1010 buf.append(
1011 lookup(
1012 file, mode, false, kbuf.makeStringAndClear(),
1013 requestStack));
1018 OUString result(buf.makeStringAndClear());
1019 SAL_INFO("sal.bootstrap", "expandMacros result: " << result);
1021 return result;
1026 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */