tdf#163486: PVS: container is empty
[LibreOffice.git] / sal / rtl / bootstrap.cxx
blob584d0eaeb4d8b6175bede3ca705d11cefb6fbdec
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_folders.h>
21 #include <rtl/bootstrap.h>
22 #include <rtl/bootstrap.hxx>
23 #include <osl/diagnose.h>
24 #include <osl/process.h>
25 #include <osl/file.hxx>
26 #include <osl/mutex.hxx>
27 #include <osl/profile.hxx>
28 #include <osl/security.hxx>
29 #include <rtl/string.hxx>
30 #include <rtl/ustrbuf.hxx>
31 #include <rtl/ustring.hxx>
32 #include <rtl/byteseq.hxx>
33 #include <sal/log.hxx>
34 #include <o3tl/lru_map.hxx>
35 #include <o3tl/string_view.hxx>
37 #include <utility>
38 #include <vector>
39 #include <algorithm>
40 #include <cstddef>
41 #include <string_view>
42 #include <unordered_map>
44 #ifdef ANDROID
45 #include <osl/detail/android-bootstrap.h>
46 #endif
48 #ifdef EMSCRIPTEN
49 #include <osl/detail/emscripten-bootstrap.h>
50 #endif
52 #ifdef IOS
53 #include <premac.h>
54 #import <Foundation/Foundation.h>
55 #include <postmac.h>
56 #endif
58 using osl::DirectoryItem;
59 using osl::FileStatus;
61 namespace
64 struct Bootstrap_Impl;
66 constexpr std::u16string_view VND_SUN_STAR_PATHNAME = u"vnd.sun.star.pathname:";
68 bool isPathnameUrl(std::u16string_view url)
70 return o3tl::matchIgnoreAsciiCase(url, VND_SUN_STAR_PATHNAME);
73 bool resolvePathnameUrl(OUString * url)
75 assert(url);
76 if (!isPathnameUrl(*url) ||
77 (osl::FileBase::getFileURLFromSystemPath(
78 url->copy(VND_SUN_STAR_PATHNAME.size()), *url) ==
79 osl::FileBase::E_None))
81 return true;
83 *url = OUString();
84 return false;
87 enum class LookupMode {
88 NORMAL, URE_BOOTSTRAP,
89 URE_BOOTSTRAP_EXPANSION };
91 struct ExpandRequestLink {
92 ExpandRequestLink const * next;
93 Bootstrap_Impl const * file;
94 OUString key;
97 OUString expandMacros(
98 Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
99 ExpandRequestLink const * requestStack);
101 OUString recursivelyExpandMacros(
102 Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
103 Bootstrap_Impl const * requestFile, OUString const & requestKey,
104 ExpandRequestLink const * requestStack)
106 for (; requestStack; requestStack = requestStack->next) {
107 if (requestStack->file == requestFile &&
108 requestStack->key == requestKey)
110 return u"***RECURSION DETECTED***"_ustr;
113 ExpandRequestLink link = { requestStack, requestFile, requestKey };
114 return expandMacros(file, text, mode, &link);
117 struct rtl_bootstrap_NameValue
119 OUString sName;
120 OUString sValue;
122 rtl_bootstrap_NameValue()
124 rtl_bootstrap_NameValue(OUString name, OUString value )
125 : sName(std::move( name )),
126 sValue(std::move( value ))
130 } // end namespace
132 typedef std::vector<rtl_bootstrap_NameValue> NameValueVector;
134 static bool find(
135 NameValueVector const & vector, OUString const & key,
136 OUString * value)
138 OSL_ASSERT(value);
139 auto i = std::find_if(vector.begin(), vector.end(),
140 [&key](const rtl_bootstrap_NameValue& rNameValue) { return rNameValue.sName == key; });
141 if (i != vector.end())
143 *value = i->sValue;
144 return true;
146 return false;
149 namespace
151 NameValueVector rtl_bootstrap_set_vector;
154 static bool getFromCommandLineArgs(
155 OUString const & key, OUString * value )
157 OSL_ASSERT(value);
159 static NameValueVector nameValueVector = []()
161 NameValueVector tmp;
163 sal_Int32 nArgCount = osl_getCommandArgCount();
164 for(sal_Int32 i = 0; i < nArgCount; ++ i)
166 OUString pArg;
167 osl_getCommandArg( i, &pArg.pData );
168 if( (pArg.startsWith("-") || pArg.startsWith("/") ) &&
169 pArg.match("env:", 1) )
171 sal_Int32 nIndex = pArg.indexOf( '=' );
173 if( nIndex >= 0 )
175 rtl_bootstrap_NameValue nameValue;
176 nameValue.sName = pArg.copy( 5, nIndex - 5 );
177 nameValue.sValue = pArg.copy( nIndex+1 );
179 if( i == nArgCount-1 &&
180 nameValue.sValue.getLength() &&
181 nameValue.sValue[nameValue.sValue.getLength()-1] == 13 )
183 // avoid the 13 linefeed for the last argument,
184 // when the executable is started from a script,
185 // that was edited on windows
186 nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
189 tmp.push_back( nameValue );
193 return tmp;
194 }();
196 return find(nameValueVector, key, value);
199 static void getExecutableDirectory_Impl(rtl_uString ** ppDirURL)
201 OUString fileName;
202 osl_getExecutableFile(&(fileName.pData));
204 sal_Int32 nDirEnd = fileName.lastIndexOf('/');
205 OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
207 rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
210 static OUString getIniFileName(bool overriding) {
211 OUString fileName;
213 #if defined IOS
214 // On iOS hardcode the inifile as "rc" in the .app
215 // directory. Apps are self-contained anyway, there is no
216 // possibility to have several "applications" in the same
217 // installation location with different inifiles.
218 const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: (overriding ? @"fundamental.override.ini" : @"rc")]] UTF8String];
219 fileName = OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
220 resolvePathnameUrl(&fileName);
221 #elif defined ANDROID
222 // Apps are self-contained on Android, too, can as well hardcode
223 // it as "rc" in the "/assets" directory, i.e. inside the app's
224 // .apk (zip) archive as the /assets/rc file.
225 fileName = overriding
226 ? OUString("vnd.sun.star.pathname:/assets/fundamental.override.ini")
227 : OUString("vnd.sun.star.pathname:/assets/rc");
228 resolvePathnameUrl(&fileName);
229 #elif defined(EMSCRIPTEN)
230 fileName = overriding
231 ? OUString("vnd.sun.star.pathname:/instdir/program/fundamental.override.ini")
232 : OUString("vnd.sun.star.pathname:/instdir/program/sofficerc");
233 resolvePathnameUrl(&fileName);
234 #else
235 if (!overriding && getFromCommandLineArgs(u"INIFILENAME"_ustr, &fileName))
237 resolvePathnameUrl(&fileName);
239 else
241 osl_getExecutableFile(&(fileName.pData));
243 if (overriding) {
244 auto const i = fileName.lastIndexOf('/') + 1;
245 fileName = fileName.replaceAt(i, fileName.getLength() - i, u"fundamental.override.ini");
246 } else {
247 // get rid of a potential executable extension
248 OUString progExt = u".bin"_ustr;
249 if (fileName.getLength() > progExt.getLength()
250 && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt))
252 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
255 progExt = ".exe";
256 if (fileName.getLength() > progExt.getLength()
257 && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt))
259 fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
262 // append config file suffix
263 fileName += SAL_CONFIGFILE("");
266 #ifdef MACOSX
267 // We keep only executables in the MacOS folder, and all
268 // rc files in LIBO_ETC_FOLDER (typically "Resources").
269 sal_Int32 off = fileName.lastIndexOf( "/MacOS/" );
270 if (off != -1)
271 fileName = fileName.replaceAt(off + 1, strlen("MacOS"), u"" LIBO_ETC_FOLDER);
272 #endif
274 #endif
276 return fileName;
279 static OUString const & getOverrideIniFileName_Impl()
281 static OUString aStaticName = getIniFileName(true);
283 return aStaticName;
286 static OUString & getIniFileName_Impl()
288 static OUString aStaticName = getIniFileName(false);
290 return aStaticName;
293 // ensure the given file url has no final slash
295 static void EnsureNoFinalSlash (OUString & url)
297 sal_Int32 i = url.getLength();
299 if (i > 0 && url[i - 1] == '/')
300 url = url.copy(0, i - 1);
303 namespace {
305 struct Bootstrap_Impl
307 sal_Int32 _nRefCount;
308 Bootstrap_Impl * _override_base_ini;
309 Bootstrap_Impl * _base_ini;
311 NameValueVector _nameValueVector;
312 OUString _iniName;
314 explicit Bootstrap_Impl (OUString const & rIniName);
315 ~Bootstrap_Impl();
317 static void * operator new (std::size_t n)
318 { return malloc (sal_uInt32(n)); }
319 static void operator delete (void * p , std::size_t)
320 { free (p); }
322 bool getValue(
323 OUString const & key, rtl_uString ** value,
324 rtl_uString * defaultValue, LookupMode mode, bool override,
325 ExpandRequestLink const * requestStack) const;
326 bool getDirectValue(
327 OUString const & key, rtl_uString ** value, LookupMode mode,
328 ExpandRequestLink const * requestStack) const;
329 bool getAmbienceValue(
330 OUString const & key, rtl_uString ** value, LookupMode mode,
331 ExpandRequestLink const * requestStack) const;
332 void expandValue(
333 rtl_uString ** value, OUString const & text, LookupMode mode,
334 Bootstrap_Impl const * requestFile, OUString const & requestKey,
335 ExpandRequestLink const * requestStack) const;
340 Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
341 : _nRefCount( 0 ),
342 _override_base_ini( nullptr ),
343 _base_ini( nullptr ),
344 _iniName (rIniName)
346 OUString override_base_ini(getOverrideIniFileName_Impl());
347 // normalize path
348 FileStatus override_status( osl_FileStatus_Mask_FileURL );
349 DirectoryItem override_dirItem;
350 bool skip_base_ini = false;
351 if (DirectoryItem::get(override_base_ini, override_dirItem) == DirectoryItem::E_None &&
352 override_dirItem.getFileStatus(override_status) == DirectoryItem::E_None)
354 override_base_ini = override_status.getFileURL();
355 if (rIniName != override_base_ini)
357 _override_base_ini = static_cast< Bootstrap_Impl * >(
358 rtl_bootstrap_args_open(override_base_ini.pData));
360 else
362 skip_base_ini = true;
366 if (!skip_base_ini) {
367 OUString base_ini(getIniFileName_Impl());
368 // normalize path
369 FileStatus status( osl_FileStatus_Mask_FileURL );
370 DirectoryItem dirItem;
371 if (DirectoryItem::get(base_ini, dirItem) == DirectoryItem::E_None &&
372 dirItem.getFileStatus(status) == DirectoryItem::E_None)
374 base_ini = status.getFileURL();
375 if (rIniName != base_ini)
377 _base_ini = static_cast< Bootstrap_Impl * >(
378 rtl_bootstrap_args_open(base_ini.pData));
383 SAL_INFO("sal.bootstrap", "Bootstrap_Impl(): sFile=" << _iniName);
384 oslFileHandle handle;
385 if (!_iniName.isEmpty() &&
386 osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read) == osl_File_E_None)
388 rtl::ByteSequence seq;
390 while (osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)) == osl_File_E_None)
392 OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength());
393 sal_Int32 nIndex = line.indexOf('=');
394 if (nIndex >= 1)
396 struct rtl_bootstrap_NameValue nameValue;
397 nameValue.sName = OStringToOUString(o3tl::trim(line.subView(0,nIndex)), RTL_TEXTENCODING_ASCII_US);
398 nameValue.sValue = OStringToOUString(o3tl::trim(line.subView(nIndex+1)), RTL_TEXTENCODING_UTF8);
400 SAL_INFO("sal.bootstrap", "pushing: name=" << nameValue.sName << " value=" << nameValue.sValue);
402 _nameValueVector.push_back(nameValue);
405 osl_closeFile(handle);
407 else
409 SAL_INFO( "sal.bootstrap", "couldn't open file: " << _iniName );
413 Bootstrap_Impl::~Bootstrap_Impl()
415 if (_base_ini)
416 rtl_bootstrap_args_close( _base_ini );
417 if (_override_base_ini)
418 rtl_bootstrap_args_close( _override_base_ini );
421 namespace {
423 Bootstrap_Impl * get_static_bootstrap_handle()
425 static Bootstrap_Impl* s_handle = []() {
426 OUString iniName(getIniFileName_Impl());
427 Bootstrap_Impl* that = static_cast<Bootstrap_Impl*>(rtl_bootstrap_args_open(iniName.pData));
428 if (!that)
430 that = new Bootstrap_Impl(iniName);
431 ++that->_nRefCount;
433 return that;
434 }();
436 return s_handle;
439 struct FundamentalIniData
441 rtlBootstrapHandle ini;
443 FundamentalIniData()
445 OUString uri;
446 ini =
447 (get_static_bootstrap_handle()->getValue(
448 u"URE_BOOTSTRAP"_ustr, &uri.pData, nullptr, LookupMode::NORMAL, false,
449 nullptr)
450 && resolvePathnameUrl(&uri))
451 ? rtl_bootstrap_args_open(uri.pData) : nullptr;
454 ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
456 FundamentalIniData(const FundamentalIniData&) = delete;
457 FundamentalIniData& operator=(const FundamentalIniData&) = delete;
460 FundamentalIniData& FundamentalIni()
462 static FundamentalIniData SINGLETON;
463 return SINGLETON;
468 bool Bootstrap_Impl::getValue(
469 OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
470 LookupMode mode, bool override, ExpandRequestLink const * requestStack)
471 const
473 if (mode == LookupMode::NORMAL && key == "URE_BOOTSTRAP")
474 mode = LookupMode::URE_BOOTSTRAP;
476 if (override && getDirectValue(key, value, mode, requestStack))
477 return true;
479 if (_override_base_ini != nullptr
480 && _override_base_ini->getDirectValue(key, value, mode, requestStack))
482 SAL_INFO("sal.bootstrap", "getValue(" << key << ") from fundamental.override.ini");
483 return true;
486 if (key == "_OS")
488 rtl_uString_assign(
489 value, (u"" RTL_OS ""_ustr).pData);
490 return true;
493 if (key == "_ARCH")
495 rtl_uString_assign(
496 value, (u"" RTL_ARCH ""_ustr).pData);
497 return true;
500 if (key == "_CPPU_ENV")
502 rtl_uString_assign(
503 value,
504 (u"" SAL_STRINGIFY(CPPU_ENV) ""_ustr).pData);
505 return true;
508 #if defined ANDROID || defined EMSCRIPTEN
509 if (key == "APP_DATA_DIR")
511 const char *app_data_dir = lo_get_app_data_dir();
512 rtl_uString_assign(
513 value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
514 return true;
516 #endif
518 #ifdef IOS
519 if (key == "APP_DATA_DIR")
521 const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLPathAllowedCharacterSet]] UTF8String];
522 rtl_uString_assign(
523 value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
524 return true;
526 #endif
528 if (key == "ORIGIN")
530 rtl_uString_assign(
531 value,
532 _iniName.copy(
533 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
534 return true;
537 if (getAmbienceValue(key, value, mode, requestStack))
538 return true;
540 if (key == "SYSUSERCONFIG")
542 OUString v;
543 bool b = osl::Security().getConfigDir(v);
544 EnsureNoFinalSlash(v);
545 rtl_uString_assign(value, v.pData);
546 return b;
549 if (key == "SYSUSERHOME")
551 OUString v;
552 bool b = osl::Security().getHomeDir(v);
553 EnsureNoFinalSlash(v);
554 rtl_uString_assign(value, v.pData);
555 return b;
558 if (key == "SYSBINDIR")
560 getExecutableDirectory_Impl(value);
561 return true;
564 if (_base_ini != nullptr && _base_ini->getDirectValue(key, value, mode, requestStack))
565 return true;
567 if (!override && getDirectValue(key, value, mode, requestStack))
568 return true;
570 if (mode == LookupMode::NORMAL)
572 FundamentalIniData const & d = FundamentalIni();
573 Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
574 if (b != nullptr && b != this && b->getDirectValue(key, value, mode, requestStack))
575 return true;
578 if (defaultValue != nullptr)
580 rtl_uString_assign(value, defaultValue);
581 return true;
584 rtl_uString_new(value);
585 return false;
588 bool Bootstrap_Impl::getDirectValue(
589 OUString const & key, rtl_uString ** value, LookupMode mode,
590 ExpandRequestLink const * requestStack) const
592 OUString v;
593 if (find(_nameValueVector, key, &v))
595 expandValue(value, v, mode, this, key, requestStack);
596 return true;
599 return false;
602 bool Bootstrap_Impl::getAmbienceValue(
603 OUString const & key, rtl_uString ** value, LookupMode mode,
604 ExpandRequestLink const * requestStack) const
606 OUString v;
607 bool f;
610 osl::MutexGuard g(osl::Mutex::getGlobalMutex());
611 f = find(rtl_bootstrap_set_vector, key, &v);
614 if (f || getFromCommandLineArgs(key, &v) ||
615 osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
617 expandValue(value, v, mode, nullptr, key, requestStack);
618 return true;
621 return false;
624 void Bootstrap_Impl::expandValue(
625 rtl_uString ** value, OUString const & text, LookupMode mode,
626 Bootstrap_Impl const * requestFile, OUString const & requestKey,
627 ExpandRequestLink const * requestStack) const
629 rtl_uString_assign(
630 value,
631 (mode == LookupMode::URE_BOOTSTRAP && isPathnameUrl(text) ?
632 text :
633 recursivelyExpandMacros(
634 this, text,
635 (mode == LookupMode::URE_BOOTSTRAP ?
636 LookupMode::URE_BOOTSTRAP_EXPANSION : mode),
637 requestFile, requestKey, requestStack)).pData);
640 namespace {
642 typedef std::unordered_map< OUString, Bootstrap_Impl * > bootstrap_map_t;
643 bootstrap_map_t bootstrap_map;
647 rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName)
649 static o3tl::lru_map<OUString,OUString> fileUrlLookupCache(16);
651 OUString originalIniName( pIniName );
652 OUString iniName;
654 osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex());
655 auto cacheIt = fileUrlLookupCache.find(originalIniName);
656 bool foundInCache = cacheIt != fileUrlLookupCache.end();
657 if (foundInCache)
658 iniName = cacheIt->second;
659 guard.clear();
661 // normalize path
662 if (!foundInCache)
664 FileStatus status(osl_FileStatus_Mask_FileURL);
665 DirectoryItem dirItem;
666 if (DirectoryItem::get(originalIniName, dirItem) != DirectoryItem::E_None ||
667 dirItem.getFileStatus(status) != DirectoryItem::E_None)
669 return nullptr;
671 iniName = status.getFileURL();
674 guard.reset();
675 if (!foundInCache)
676 fileUrlLookupCache.insert({originalIniName, iniName});
677 Bootstrap_Impl * that;
678 auto iFind(bootstrap_map.find(iniName));
679 if (iFind == bootstrap_map.end())
681 guard.clear();
682 that = new Bootstrap_Impl(iniName);
683 guard.reset();
684 iFind = bootstrap_map.find(iniName);
685 if (iFind == bootstrap_map.end())
687 ++that->_nRefCount;
688 ::std::pair< bootstrap_map_t::iterator, bool > insertion(
689 bootstrap_map.emplace(iniName, that));
690 OSL_ASSERT(insertion.second);
692 else
694 Bootstrap_Impl * obsolete = that;
695 that = iFind->second;
696 ++that->_nRefCount;
697 guard.clear();
698 delete obsolete;
701 else
703 that = iFind->second;
704 ++that->_nRefCount;
706 return static_cast< rtlBootstrapHandle >( that );
709 void SAL_CALL rtl_bootstrap_args_close(rtlBootstrapHandle handle) noexcept
711 if (!handle)
712 return;
714 Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
716 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
717 OSL_ASSERT(bootstrap_map.find(that->_iniName)->second == that);
718 --that->_nRefCount;
720 if (that->_nRefCount != 0)
721 return;
723 std::size_t const nLeaking = 8; // only hold up to 8 files statically
724 if (bootstrap_map.size() > nLeaking)
726 ::std::size_t erased = bootstrap_map.erase( that->_iniName );
727 if (erased != 1) {
728 OSL_ASSERT( false );
730 delete that;
734 sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
735 rtlBootstrapHandle handle,
736 rtl_uString * pName,
737 rtl_uString ** ppValue,
738 rtl_uString * pDefault
741 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
743 bool found = false;
744 if(ppValue && pName)
746 if (!handle)
747 handle = get_static_bootstrap_handle();
749 found = static_cast< Bootstrap_Impl * >(handle)->getValue(
750 pName, ppValue, pDefault, LookupMode::NORMAL, false, nullptr );
753 return found;
756 void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
757 rtlBootstrapHandle handle,
758 rtl_uString ** ppIniName
761 if(!ppIniName)
762 return;
764 if(handle)
766 Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
767 rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
769 else
771 const OUString & iniName = getIniFileName_Impl();
772 rtl_uString_assign(ppIniName, iniName.pData);
776 void SAL_CALL rtl_bootstrap_setIniFileName (
777 rtl_uString * pName
780 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
781 OUString & file = getIniFileName_Impl();
782 file = pName;
785 sal_Bool SAL_CALL rtl_bootstrap_get (
786 rtl_uString * pName,
787 rtl_uString ** ppValue,
788 rtl_uString * pDefault
791 return rtl_bootstrap_get_from_handle(nullptr, pName, ppValue, pDefault);
794 void SAL_CALL rtl_bootstrap_set (
795 rtl_uString * pName,
796 rtl_uString * pValue
799 const OUString name(pName);
800 const OUString value(pValue);
802 osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
804 for (auto & item : rtl_bootstrap_set_vector)
806 if (item.sName == name)
808 item.sValue = value;
809 return;
813 SAL_INFO("sal.bootstrap", "explicitly setting: name=" << name << " value=" <<value);
815 rtl_bootstrap_set_vector.emplace_back(name, value);
818 void SAL_CALL rtl_bootstrap_expandMacros_from_handle(
819 rtlBootstrapHandle handle,
820 rtl_uString ** macro)
822 if (!handle)
823 handle = get_static_bootstrap_handle();
825 OUString expanded(expandMacros(static_cast< Bootstrap_Impl * >(handle),
826 OUString::unacquired(macro),
827 LookupMode::NORMAL, nullptr));
828 rtl_uString_assign(macro, expanded.pData);
831 void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro)
833 rtl_bootstrap_expandMacros_from_handle(nullptr, macro);
836 void rtl_bootstrap_encode(rtl_uString const * value, rtl_uString ** encoded)
838 assert(value);
839 OUStringBuffer b(value->length+5);
840 for (sal_Int32 i = 0; i < value->length; ++i)
842 sal_Unicode c = value->buffer[i];
843 if (c == '$' || c == '\\')
844 b.append('\\');
846 b.append(c);
849 rtl_uString_assign(encoded, b.makeStringAndClear().pData);
852 namespace {
854 int hex(sal_Unicode c)
856 return
857 c >= '0' && c <= '9' ? c - '0' :
858 c >= 'A' && c <= 'F' ? c - 'A' + 10 :
859 c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
862 sal_Unicode read(std::u16string_view text, std::size_t * pos, bool * escaped)
864 assert(pos && *pos < text.length() && escaped);
865 sal_Unicode c = text[(*pos)++];
866 if (c == '\\')
868 int n1, n2, n3, n4;
869 if (*pos < text.length() - 4 && text[*pos] == 'u' &&
870 ((n1 = hex(text[*pos + 1])) >= 0) &&
871 ((n2 = hex(text[*pos + 2])) >= 0) &&
872 ((n3 = hex(text[*pos + 3])) >= 0) &&
873 ((n4 = hex(text[*pos + 4])) >= 0))
875 *pos += 5;
876 *escaped = true;
877 return static_cast< sal_Unicode >(
878 (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
881 if (*pos < text.length())
883 *escaped = true;
884 return text[(*pos)++];
888 *escaped = false;
889 return c;
892 OUString lookup(
893 Bootstrap_Impl const * file, LookupMode mode, bool override,
894 OUString const & key, ExpandRequestLink const * requestStack)
896 OUString v;
897 (file == nullptr ? get_static_bootstrap_handle() : file)->getValue(
898 key, &v.pData, nullptr, mode, override, requestStack);
899 return v;
902 OUString expandMacros(
903 Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
904 ExpandRequestLink const * requestStack)
906 SAL_INFO("sal.bootstrap", "expandMacros called with: " << OUString(text));
907 OUStringBuffer buf(2048);
909 for (std::size_t i = 0; i < text.length();)
911 bool escaped;
912 sal_Unicode c = read(text, &i, &escaped);
913 if (escaped || c != '$')
915 buf.append(c);
917 else
919 if (i < text.length() && text[i] == '{')
921 ++i;
922 std::size_t p = i;
923 sal_Int32 nesting = 0;
924 OUString seg[3];
925 int n = 0;
927 while (i < text.length())
929 std::size_t j = i;
930 c = read(text, &i, &escaped);
932 if (!escaped)
934 switch (c)
936 case '{':
937 ++nesting;
938 break;
939 case '}':
940 if (nesting == 0)
942 seg[n++] = text.substr(p, j - p);
943 goto done;
945 else
947 --nesting;
949 break;
950 case ':':
951 if (nesting == 0 && n < 2)
953 seg[n++] = text.substr(p, j - p);
954 p = i;
956 break;
960 done:
961 for (int j = 0; j < n; ++j)
963 seg[j] = expandMacros(file, seg[j], mode, requestStack);
966 if (n == 3 && seg[0] != ".override" && seg[1].isEmpty())
968 // For backward compatibility, treat ${file::key} the
969 // same as just ${file:key}:
970 seg[1] = seg[2];
971 n = 2;
974 if (n == 1)
976 buf.append(lookup(file, mode, false, seg[0], requestStack));
978 else if (n == 2)
980 rtl::Bootstrap b(seg[0]);
981 Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
982 buf.append(lookup(f, mode, false, seg[1], requestStack));
984 else if (n == 3 && seg[0] == ".override")
986 rtl::Bootstrap b(seg[1]);
987 Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
988 buf.append(lookup(f, mode, f != nullptr, seg[2], requestStack));
990 else
992 // Going through osl::Profile, this code erroneously
993 // does not recursively expand macros in the resulting
994 // replacement text (and if it did, it would fail to
995 // detect cycles that pass through here):
996 buf.append(
997 OStringToOUString(
998 osl::Profile(seg[0]).readString(
999 OUStringToOString(
1000 seg[1], RTL_TEXTENCODING_UTF8),
1001 OUStringToOString(
1002 seg[2], RTL_TEXTENCODING_UTF8),
1003 OString()),
1004 RTL_TEXTENCODING_UTF8));
1007 else
1009 OUStringBuffer kbuf(sal_Int32(text.length()));
1010 for (; i < text.length();)
1012 std::size_t j = i;
1013 c = read(text, &j, &escaped);
1014 if (!escaped &&
1015 (c == ' ' || c == '$' || c == '-' || c == '/' ||
1016 c == ';' || c == '\\'))
1018 break;
1021 kbuf.append(c);
1022 i = j;
1025 buf.append(
1026 lookup(
1027 file, mode, false, kbuf.makeStringAndClear(),
1028 requestStack));
1033 OUString result(buf.makeStringAndClear());
1034 SAL_INFO("sal.bootstrap", "expandMacros result: " << result);
1036 return result;
1041 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */