summaryrefslogtreecommitdiffstats
path: root/sal/rtl/bootstrap.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sal/rtl/bootstrap.cxx')
-rw-r--r--sal/rtl/bootstrap.cxx991
1 files changed, 991 insertions, 0 deletions
diff --git a/sal/rtl/bootstrap.cxx b/sal/rtl/bootstrap.cxx
new file mode 100644
index 0000000000..a3ada36a44
--- /dev/null
+++ b/sal/rtl/bootstrap.cxx
@@ -0,0 +1,991 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <config_folders.h>
+
+#include <rtl/bootstrap.h>
+#include <rtl/bootstrap.hxx>
+#include <osl/diagnose.h>
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <osl/mutex.hxx>
+#include <osl/profile.hxx>
+#include <osl/security.hxx>
+#include <rtl/string.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/byteseq.hxx>
+#include <sal/log.hxx>
+#include <o3tl/lru_map.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <utility>
+#include <vector>
+#include <algorithm>
+#include <cstddef>
+#include <string_view>
+#include <unordered_map>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#ifdef EMSCRIPTEN
+#include <osl/detail/emscripten-bootstrap.h>
+#endif
+
+#ifdef IOS
+#include <premac.h>
+#import <Foundation/Foundation.h>
+#include <postmac.h>
+#endif
+
+using osl::DirectoryItem;
+using osl::FileStatus;
+
+namespace
+{
+
+struct Bootstrap_Impl;
+
+constexpr std::u16string_view VND_SUN_STAR_PATHNAME = u"vnd.sun.star.pathname:";
+
+bool isPathnameUrl(std::u16string_view url)
+{
+ return o3tl::matchIgnoreAsciiCase(url, VND_SUN_STAR_PATHNAME);
+}
+
+bool resolvePathnameUrl(OUString * url)
+{
+ OSL_ASSERT(url);
+ if (!isPathnameUrl(*url) ||
+ (osl::FileBase::getFileURLFromSystemPath(
+ url->copy(VND_SUN_STAR_PATHNAME.size()), *url) ==
+ osl::FileBase::E_None))
+ {
+ return true;
+ }
+ *url = OUString();
+ return false;
+}
+
+enum class LookupMode {
+ NORMAL, URE_BOOTSTRAP,
+ URE_BOOTSTRAP_EXPANSION };
+
+struct ExpandRequestLink {
+ ExpandRequestLink const * next;
+ Bootstrap_Impl const * file;
+ OUString key;
+};
+
+OUString expandMacros(
+ Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
+ ExpandRequestLink const * requestStack);
+
+OUString recursivelyExpandMacros(
+ Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
+ Bootstrap_Impl const * requestFile, OUString const & requestKey,
+ ExpandRequestLink const * requestStack)
+{
+ for (; requestStack; requestStack = requestStack->next) {
+ if (requestStack->file == requestFile &&
+ requestStack->key == requestKey)
+ {
+ return "***RECURSION DETECTED***";
+ }
+ }
+ ExpandRequestLink link = { requestStack, requestFile, requestKey };
+ return expandMacros(file, text, mode, &link);
+}
+
+struct rtl_bootstrap_NameValue
+{
+ OUString sName;
+ OUString sValue;
+
+ rtl_bootstrap_NameValue()
+ {}
+ rtl_bootstrap_NameValue(OUString name, OUString value )
+ : sName(std::move( name )),
+ sValue(std::move( value ))
+ {}
+};
+
+} // end namespace
+
+typedef std::vector<rtl_bootstrap_NameValue> NameValueVector;
+
+static bool find(
+ NameValueVector const & vector, OUString const & key,
+ OUString * value)
+{
+ OSL_ASSERT(value);
+ auto i = std::find_if(vector.begin(), vector.end(),
+ [&key](const rtl_bootstrap_NameValue& rNameValue) { return rNameValue.sName == key; });
+ if (i != vector.end())
+ {
+ *value = i->sValue;
+ return true;
+ }
+ return false;
+}
+
+namespace
+{
+ NameValueVector rtl_bootstrap_set_vector;
+}
+
+static bool getFromCommandLineArgs(
+ OUString const & key, OUString * value )
+{
+ OSL_ASSERT(value);
+
+ static NameValueVector nameValueVector = []()
+ {
+ NameValueVector tmp;
+
+ sal_Int32 nArgCount = osl_getCommandArgCount();
+ for(sal_Int32 i = 0; i < nArgCount; ++ i)
+ {
+ OUString pArg;
+ osl_getCommandArg( i, &pArg.pData );
+ if( (pArg.startsWith("-") || pArg.startsWith("/") ) &&
+ pArg.match("env:", 1) )
+ {
+ sal_Int32 nIndex = pArg.indexOf( '=' );
+
+ if( nIndex >= 0 )
+ {
+ rtl_bootstrap_NameValue nameValue;
+ nameValue.sName = pArg.copy( 5, nIndex - 5 );
+ nameValue.sValue = pArg.copy( nIndex+1 );
+
+ if( i == nArgCount-1 &&
+ nameValue.sValue.getLength() &&
+ nameValue.sValue[nameValue.sValue.getLength()-1] == 13 )
+ {
+ // avoid the 13 linefeed for the last argument,
+ // when the executable is started from a script,
+ // that was edited on windows
+ nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
+ }
+
+ tmp.push_back( nameValue );
+ }
+ }
+ };
+ return tmp;
+ }();
+
+ return find(nameValueVector, key, value);
+}
+
+static void getExecutableDirectory_Impl(rtl_uString ** ppDirURL)
+{
+ OUString fileName;
+ osl_getExecutableFile(&(fileName.pData));
+
+ sal_Int32 nDirEnd = fileName.lastIndexOf('/');
+ OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
+
+ rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
+}
+
+static OUString & getIniFileName_Impl()
+{
+ static OUString aStaticName = []() {
+ OUString fileName;
+
+#if defined IOS
+ // On iOS hardcode the inifile as "rc" in the .app
+ // directory. Apps are self-contained anyway, there is no
+ // possibility to have several "applications" in the same
+ // installation location with different inifiles.
+ const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String];
+ fileName = OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
+ resolvePathnameUrl(&fileName);
+#elif defined ANDROID
+ // Apps are self-contained on Android, too, can as well hardcode
+ // it as "rc" in the "/assets" directory, i.e. inside the app's
+ // .apk (zip) archive as the /assets/rc file.
+ fileName = OUString("vnd.sun.star.pathname:/assets/rc");
+ resolvePathnameUrl(&fileName);
+#elif defined(EMSCRIPTEN)
+ fileName = OUString("vnd.sun.star.pathname:/instdir/program/sofficerc");
+ resolvePathnameUrl(&fileName);
+#else
+ if (getFromCommandLineArgs("INIFILENAME", &fileName))
+ {
+ resolvePathnameUrl(&fileName);
+ }
+ else
+ {
+ osl_getExecutableFile(&(fileName.pData));
+
+ // get rid of a potential executable extension
+ OUString progExt = ".bin";
+ if (fileName.getLength() > progExt.getLength()
+ && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt))
+ {
+ fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
+ }
+
+ progExt = ".exe";
+ if (fileName.getLength() > progExt.getLength()
+ && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt))
+ {
+ fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
+ }
+
+ // append config file suffix
+ fileName += SAL_CONFIGFILE("");
+
+#ifdef MACOSX
+ // We keep only executables in the MacOS folder, and all
+ // rc files in LIBO_ETC_FOLDER (typically "Resources").
+ sal_Int32 off = fileName.lastIndexOf( "/MacOS/" );
+ if (off != -1)
+ fileName = fileName.replaceAt(off + 1, strlen("MacOS"), u"" LIBO_ETC_FOLDER);
+#endif
+ }
+#endif
+
+ return fileName;
+ }();
+
+ return aStaticName;
+}
+
+// ensure the given file url has no final slash
+
+static void EnsureNoFinalSlash (OUString & url)
+{
+ sal_Int32 i = url.getLength();
+
+ if (i > 0 && url[i - 1] == '/')
+ url = url.copy(0, i - 1);
+}
+
+namespace {
+
+struct Bootstrap_Impl
+{
+ sal_Int32 _nRefCount;
+ Bootstrap_Impl * _base_ini;
+
+ NameValueVector _nameValueVector;
+ OUString _iniName;
+
+ explicit Bootstrap_Impl (OUString const & rIniName);
+ ~Bootstrap_Impl();
+
+ static void * operator new (std::size_t n)
+ { return malloc (sal_uInt32(n)); }
+ static void operator delete (void * p , std::size_t)
+ { free (p); }
+
+ bool getValue(
+ OUString const & key, rtl_uString ** value,
+ rtl_uString * defaultValue, LookupMode mode, bool override,
+ ExpandRequestLink const * requestStack) const;
+ bool getDirectValue(
+ OUString const & key, rtl_uString ** value, LookupMode mode,
+ ExpandRequestLink const * requestStack) const;
+ bool getAmbienceValue(
+ OUString const & key, rtl_uString ** value, LookupMode mode,
+ ExpandRequestLink const * requestStack) const;
+ void expandValue(
+ rtl_uString ** value, OUString const & text, LookupMode mode,
+ Bootstrap_Impl const * requestFile, OUString const & requestKey,
+ ExpandRequestLink const * requestStack) const;
+};
+
+}
+
+Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
+ : _nRefCount( 0 ),
+ _base_ini( nullptr ),
+ _iniName (rIniName)
+{
+ OUString base_ini(getIniFileName_Impl());
+ // normalize path
+ FileStatus status( osl_FileStatus_Mask_FileURL );
+ DirectoryItem dirItem;
+ if (DirectoryItem::get(base_ini, dirItem) == DirectoryItem::E_None &&
+ dirItem.getFileStatus(status) == DirectoryItem::E_None)
+ {
+ base_ini = status.getFileURL();
+ if (rIniName != base_ini)
+ {
+ _base_ini = static_cast< Bootstrap_Impl * >(
+ rtl_bootstrap_args_open(base_ini.pData));
+ }
+ }
+ SAL_INFO("sal.bootstrap", "Bootstrap_Impl(): sFile=" << _iniName);
+ oslFileHandle handle;
+ if (!_iniName.isEmpty() &&
+ osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read) == osl_File_E_None)
+ {
+ rtl::ByteSequence seq;
+
+ while (osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)) == osl_File_E_None)
+ {
+ OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength());
+ sal_Int32 nIndex = line.indexOf('=');
+ if (nIndex >= 1)
+ {
+ struct rtl_bootstrap_NameValue nameValue;
+ nameValue.sName = OStringToOUString(o3tl::trim(line.subView(0,nIndex)), RTL_TEXTENCODING_ASCII_US);
+ nameValue.sValue = OStringToOUString(o3tl::trim(line.subView(nIndex+1)), RTL_TEXTENCODING_UTF8);
+
+ SAL_INFO("sal.bootstrap", "pushing: name=" << nameValue.sName << " value=" << nameValue.sValue);
+
+ _nameValueVector.push_back(nameValue);
+ }
+ }
+ osl_closeFile(handle);
+ }
+ else
+ {
+ SAL_INFO( "sal.bootstrap", "couldn't open file: " << _iniName );
+ }
+}
+
+Bootstrap_Impl::~Bootstrap_Impl()
+{
+ if (_base_ini)
+ rtl_bootstrap_args_close( _base_ini );
+}
+
+namespace {
+
+Bootstrap_Impl * get_static_bootstrap_handle()
+{
+ static Bootstrap_Impl* s_handle = []() {
+ OUString iniName(getIniFileName_Impl());
+ Bootstrap_Impl* that = static_cast<Bootstrap_Impl*>(rtl_bootstrap_args_open(iniName.pData));
+ if (!that)
+ {
+ that = new Bootstrap_Impl(iniName);
+ ++that->_nRefCount;
+ }
+ return that;
+ }();
+
+ return s_handle;
+}
+
+struct FundamentalIniData
+{
+ rtlBootstrapHandle ini;
+
+ FundamentalIniData()
+ {
+ OUString uri;
+ ini =
+ (get_static_bootstrap_handle()->getValue(
+ "URE_BOOTSTRAP", &uri.pData, nullptr, LookupMode::NORMAL, false,
+ nullptr)
+ && resolvePathnameUrl(&uri))
+ ? rtl_bootstrap_args_open(uri.pData) : nullptr;
+ }
+
+ ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
+
+ FundamentalIniData(const FundamentalIniData&) = delete;
+ FundamentalIniData& operator=(const FundamentalIniData&) = delete;
+};
+
+FundamentalIniData& FundamentalIni()
+{
+ static FundamentalIniData SINGLETON;
+ return SINGLETON;
+}
+
+}
+
+bool Bootstrap_Impl::getValue(
+ OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
+ LookupMode mode, bool override, ExpandRequestLink const * requestStack)
+ const
+{
+ if (mode == LookupMode::NORMAL && key == "URE_BOOTSTRAP")
+ mode = LookupMode::URE_BOOTSTRAP;
+
+ if (override && getDirectValue(key, value, mode, requestStack))
+ return true;
+
+ if (key == "_OS")
+ {
+ rtl_uString_assign(
+ value, OUString(RTL_OS).pData);
+ return true;
+ }
+
+ if (key == "_ARCH")
+ {
+ rtl_uString_assign(
+ value, OUString(RTL_ARCH).pData);
+ return true;
+ }
+
+ if (key == "_CPPU_ENV")
+ {
+ rtl_uString_assign(
+ value,
+ (OUString(
+ SAL_STRINGIFY(CPPU_ENV)).
+ pData));
+ return true;
+ }
+
+#if defined ANDROID || defined EMSCRIPTEN
+ if (key == "APP_DATA_DIR")
+ {
+ const char *app_data_dir = lo_get_app_data_dir();
+ rtl_uString_assign(
+ value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
+ return true;
+ }
+#endif
+
+#ifdef IOS
+ if (key == "APP_DATA_DIR")
+ {
+ const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLPathAllowedCharacterSet]] UTF8String];
+ rtl_uString_assign(
+ value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
+ return true;
+ }
+#endif
+
+ if (key == "ORIGIN")
+ {
+ rtl_uString_assign(
+ value,
+ _iniName.copy(
+ 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
+ return true;
+ }
+
+ if (getAmbienceValue(key, value, mode, requestStack))
+ return true;
+
+ if (key == "SYSUSERCONFIG")
+ {
+ OUString v;
+ bool b = osl::Security().getConfigDir(v);
+ EnsureNoFinalSlash(v);
+ rtl_uString_assign(value, v.pData);
+ return b;
+ }
+
+ if (key == "SYSUSERHOME")
+ {
+ OUString v;
+ bool b = osl::Security().getHomeDir(v);
+ EnsureNoFinalSlash(v);
+ rtl_uString_assign(value, v.pData);
+ return b;
+ }
+
+ if (key == "SYSBINDIR")
+ {
+ getExecutableDirectory_Impl(value);
+ return true;
+ }
+
+ if (_base_ini != nullptr && _base_ini->getDirectValue(key, value, mode, requestStack))
+ return true;
+
+ if (!override && getDirectValue(key, value, mode, requestStack))
+ return true;
+
+ if (mode == LookupMode::NORMAL)
+ {
+ FundamentalIniData const & d = FundamentalIni();
+ Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
+ if (b != nullptr && b != this && b->getDirectValue(key, value, mode, requestStack))
+ return true;
+ }
+
+ if (defaultValue != nullptr)
+ {
+ rtl_uString_assign(value, defaultValue);
+ return true;
+ }
+
+ rtl_uString_new(value);
+ return false;
+}
+
+bool Bootstrap_Impl::getDirectValue(
+ OUString const & key, rtl_uString ** value, LookupMode mode,
+ ExpandRequestLink const * requestStack) const
+{
+ OUString v;
+ if (find(_nameValueVector, key, &v))
+ {
+ expandValue(value, v, mode, this, key, requestStack);
+ return true;
+ }
+
+ return false;
+}
+
+bool Bootstrap_Impl::getAmbienceValue(
+ OUString const & key, rtl_uString ** value, LookupMode mode,
+ ExpandRequestLink const * requestStack) const
+{
+ OUString v;
+ bool f;
+
+ {
+ osl::MutexGuard g(osl::Mutex::getGlobalMutex());
+ f = find(rtl_bootstrap_set_vector, key, &v);
+ }
+
+ if (f || getFromCommandLineArgs(key, &v) ||
+ osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
+ {
+ expandValue(value, v, mode, nullptr, key, requestStack);
+ return true;
+ }
+
+ return false;
+}
+
+void Bootstrap_Impl::expandValue(
+ rtl_uString ** value, OUString const & text, LookupMode mode,
+ Bootstrap_Impl const * requestFile, OUString const & requestKey,
+ ExpandRequestLink const * requestStack) const
+{
+ rtl_uString_assign(
+ value,
+ (mode == LookupMode::URE_BOOTSTRAP && isPathnameUrl(text) ?
+ text :
+ recursivelyExpandMacros(
+ this, text,
+ (mode == LookupMode::URE_BOOTSTRAP ?
+ LookupMode::URE_BOOTSTRAP_EXPANSION : mode),
+ requestFile, requestKey, requestStack)).pData);
+}
+
+namespace {
+
+typedef std::unordered_map< OUString, Bootstrap_Impl * > bootstrap_map_t;
+bootstrap_map_t bootstrap_map;
+
+}
+
+rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName)
+{
+ static o3tl::lru_map<OUString,OUString> fileUrlLookupCache(16);
+
+ OUString originalIniName( pIniName );
+ OUString iniName;
+
+ osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex());
+ auto cacheIt = fileUrlLookupCache.find(originalIniName);
+ bool foundInCache = cacheIt != fileUrlLookupCache.end();
+ if (foundInCache)
+ iniName = cacheIt->second;
+ guard.clear();
+
+ // normalize path
+ if (!foundInCache)
+ {
+ FileStatus status(osl_FileStatus_Mask_FileURL);
+ DirectoryItem dirItem;
+ if (DirectoryItem::get(originalIniName, dirItem) != DirectoryItem::E_None ||
+ dirItem.getFileStatus(status) != DirectoryItem::E_None)
+ {
+ return nullptr;
+ }
+ iniName = status.getFileURL();
+ }
+
+ guard.reset();
+ if (!foundInCache)
+ fileUrlLookupCache.insert({originalIniName, iniName});
+ Bootstrap_Impl * that;
+ auto iFind(bootstrap_map.find(iniName));
+ if (iFind == bootstrap_map.end())
+ {
+ guard.clear();
+ that = new Bootstrap_Impl(iniName);
+ guard.reset();
+ iFind = bootstrap_map.find(iniName);
+ if (iFind == bootstrap_map.end())
+ {
+ ++that->_nRefCount;
+ ::std::pair< bootstrap_map_t::iterator, bool > insertion(
+ bootstrap_map.emplace(iniName, that));
+ OSL_ASSERT(insertion.second);
+ }
+ else
+ {
+ Bootstrap_Impl * obsolete = that;
+ that = iFind->second;
+ ++that->_nRefCount;
+ guard.clear();
+ delete obsolete;
+ }
+ }
+ else
+ {
+ that = iFind->second;
+ ++that->_nRefCount;
+ }
+ return static_cast< rtlBootstrapHandle >( that );
+}
+
+void SAL_CALL rtl_bootstrap_args_close(rtlBootstrapHandle handle) SAL_THROW_EXTERN_C()
+{
+ if (!handle)
+ return;
+
+ Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
+
+ osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
+ OSL_ASSERT(bootstrap_map.find(that->_iniName)->second == that);
+ --that->_nRefCount;
+
+ if (that->_nRefCount != 0)
+ return;
+
+ std::size_t const nLeaking = 8; // only hold up to 8 files statically
+ if (bootstrap_map.size() > nLeaking)
+ {
+ ::std::size_t erased = bootstrap_map.erase( that->_iniName );
+ if (erased != 1) {
+ OSL_ASSERT( false );
+ }
+ delete that;
+ }
+}
+
+sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
+ rtlBootstrapHandle handle,
+ rtl_uString * pName,
+ rtl_uString ** ppValue,
+ rtl_uString * pDefault
+)
+{
+ osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
+
+ bool found = false;
+ if(ppValue && pName)
+ {
+ if (!handle)
+ handle = get_static_bootstrap_handle();
+
+ found = static_cast< Bootstrap_Impl * >(handle)->getValue(
+ pName, ppValue, pDefault, LookupMode::NORMAL, false, nullptr );
+ }
+
+ return found;
+}
+
+void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
+ rtlBootstrapHandle handle,
+ rtl_uString ** ppIniName
+)
+{
+ if(!ppIniName)
+ return;
+
+ if(handle)
+ {
+ Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
+ rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
+ }
+ else
+ {
+ const OUString & iniName = getIniFileName_Impl();
+ rtl_uString_assign(ppIniName, iniName.pData);
+ }
+}
+
+void SAL_CALL rtl_bootstrap_setIniFileName (
+ rtl_uString * pName
+)
+{
+ osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
+ OUString & file = getIniFileName_Impl();
+ file = pName;
+}
+
+sal_Bool SAL_CALL rtl_bootstrap_get (
+ rtl_uString * pName,
+ rtl_uString ** ppValue,
+ rtl_uString * pDefault
+)
+{
+ return rtl_bootstrap_get_from_handle(nullptr, pName, ppValue, pDefault);
+}
+
+void SAL_CALL rtl_bootstrap_set (
+ rtl_uString * pName,
+ rtl_uString * pValue
+)
+{
+ const OUString name(pName);
+ const OUString value(pValue);
+
+ osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
+
+ for (auto & item : rtl_bootstrap_set_vector)
+ {
+ if (item.sName == name)
+ {
+ item.sValue = value;
+ return;
+ }
+ }
+
+ SAL_INFO("sal.bootstrap", "explicitly setting: name=" << name << " value=" <<value);
+
+ rtl_bootstrap_set_vector.emplace_back(name, value);
+}
+
+void SAL_CALL rtl_bootstrap_expandMacros_from_handle(
+ rtlBootstrapHandle handle,
+ rtl_uString ** macro)
+{
+ if (!handle)
+ handle = get_static_bootstrap_handle();
+
+ OUString expanded(expandMacros(static_cast< Bootstrap_Impl * >(handle),
+ OUString::unacquired(macro),
+ LookupMode::NORMAL, nullptr));
+ rtl_uString_assign(macro, expanded.pData);
+}
+
+void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro)
+{
+ rtl_bootstrap_expandMacros_from_handle(nullptr, macro);
+}
+
+void rtl_bootstrap_encode(rtl_uString const * value, rtl_uString ** encoded)
+{
+ OSL_ASSERT(value);
+ OUStringBuffer b(value->length+5);
+ for (sal_Int32 i = 0; i < value->length; ++i)
+ {
+ sal_Unicode c = value->buffer[i];
+ if (c == '$' || c == '\\')
+ b.append('\\');
+
+ b.append(c);
+ }
+
+ rtl_uString_assign(encoded, b.makeStringAndClear().pData);
+}
+
+namespace {
+
+int hex(sal_Unicode c)
+{
+ return
+ c >= '0' && c <= '9' ? c - '0' :
+ c >= 'A' && c <= 'F' ? c - 'A' + 10 :
+ c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
+}
+
+sal_Unicode read(std::u16string_view text, std::size_t * pos, bool * escaped)
+{
+ OSL_ASSERT(pos && *pos < text.length() && escaped);
+ sal_Unicode c = text[(*pos)++];
+ if (c == '\\')
+ {
+ int n1, n2, n3, n4;
+ if (*pos < text.length() - 4 && text[*pos] == 'u' &&
+ ((n1 = hex(text[*pos + 1])) >= 0) &&
+ ((n2 = hex(text[*pos + 2])) >= 0) &&
+ ((n3 = hex(text[*pos + 3])) >= 0) &&
+ ((n4 = hex(text[*pos + 4])) >= 0))
+ {
+ *pos += 5;
+ *escaped = true;
+ return static_cast< sal_Unicode >(
+ (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
+ }
+
+ if (*pos < text.length())
+ {
+ *escaped = true;
+ return text[(*pos)++];
+ }
+ }
+
+ *escaped = false;
+ return c;
+}
+
+OUString lookup(
+ Bootstrap_Impl const * file, LookupMode mode, bool override,
+ OUString const & key, ExpandRequestLink const * requestStack)
+{
+ OUString v;
+ (file == nullptr ? get_static_bootstrap_handle() : file)->getValue(
+ key, &v.pData, nullptr, mode, override, requestStack);
+ return v;
+}
+
+OUString expandMacros(
+ Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
+ ExpandRequestLink const * requestStack)
+{
+ SAL_INFO("sal.bootstrap", "expandMacros called with: " << OUString(text));
+ OUStringBuffer buf(2048);
+
+ for (std::size_t i = 0; i < text.length();)
+ {
+ bool escaped;
+ sal_Unicode c = read(text, &i, &escaped);
+ if (escaped || c != '$')
+ {
+ buf.append(c);
+ }
+ else
+ {
+ if (i < text.length() && text[i] == '{')
+ {
+ ++i;
+ std::size_t p = i;
+ sal_Int32 nesting = 0;
+ OUString seg[3];
+ int n = 0;
+
+ while (i < text.length())
+ {
+ std::size_t j = i;
+ c = read(text, &i, &escaped);
+
+ if (!escaped)
+ {
+ switch (c)
+ {
+ case '{':
+ ++nesting;
+ break;
+ case '}':
+ if (nesting == 0)
+ {
+ seg[n++] = text.substr(p, j - p);
+ goto done;
+ }
+ else
+ {
+ --nesting;
+ }
+ break;
+ case ':':
+ if (nesting == 0 && n < 2)
+ {
+ seg[n++] = text.substr(p, j - p);
+ p = i;
+ }
+ break;
+ }
+ }
+ }
+ done:
+ for (int j = 0; j < n; ++j)
+ {
+ seg[j] = expandMacros(file, seg[j], mode, requestStack);
+ }
+
+ if (n == 3 && seg[0] != ".override" && seg[1].isEmpty())
+ {
+ // For backward compatibility, treat ${file::key} the
+ // same as just ${file:key}:
+ seg[1] = seg[2];
+ n = 2;
+ }
+
+ if (n == 1)
+ {
+ buf.append(lookup(file, mode, false, seg[0], requestStack));
+ }
+ else if (n == 2)
+ {
+ rtl::Bootstrap b(seg[0]);
+ Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
+ buf.append(lookup(f, mode, false, seg[1], requestStack));
+ }
+ else if (n == 3 && seg[0] == ".override")
+ {
+ rtl::Bootstrap b(seg[1]);
+ Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
+ buf.append(lookup(f, mode, f != nullptr, seg[2], requestStack));
+ }
+ else
+ {
+ // Going through osl::Profile, this code erroneously
+ // does not recursively expand macros in the resulting
+ // replacement text (and if it did, it would fail to
+ // detect cycles that pass through here):
+ buf.append(
+ OStringToOUString(
+ osl::Profile(seg[0]).readString(
+ OUStringToOString(
+ seg[1], RTL_TEXTENCODING_UTF8),
+ OUStringToOString(
+ seg[2], RTL_TEXTENCODING_UTF8),
+ OString()),
+ RTL_TEXTENCODING_UTF8));
+ }
+ }
+ else
+ {
+ OUStringBuffer kbuf(sal_Int32(text.length()));
+ for (; i < text.length();)
+ {
+ std::size_t j = i;
+ c = read(text, &j, &escaped);
+ if (!escaped &&
+ (c == ' ' || c == '$' || c == '-' || c == '/' ||
+ c == ';' || c == '\\'))
+ {
+ break;
+ }
+
+ kbuf.append(c);
+ i = j;
+ }
+
+ buf.append(
+ lookup(
+ file, mode, false, kbuf.makeStringAndClear(),
+ requestStack));
+ }
+ }
+ }
+
+ OUString result(buf.makeStringAndClear());
+ SAL_INFO("sal.bootstrap", "expandMacros result: " << result);
+
+ return result;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */