office-gobmx/sal/rtl/bootstrap.cxx
Stephan Bergmann 2bc1a0431b New loplugin:unusedcapturedefault
In sc/qa/unit/ucalc_formula.cxx, dropping the capture-default from the
lExpectedinF lambda revealed that MSVC in C++17 mode (i.e., when building
without --with-latest-c++) requires ROW_RANGE (a local const int variable from
the enclosing TestFormula::testTdf97369) to be captured, even though all uses of
that variable within the lambda body are constant expressions.  That is still
true at least for the latest Visual Studio 2019 version 16.11.1.  (This is not
an issue for the lExpectedinH and lExpectedinI lambdas a few lines further down,
as they, in addition to using that ROW_RANGE, also use the local const double
variables SHIFT1 and SHIFT2, whose uses are not constant expressions, so
they are implicitly captured and loplugin:unusedcapturedefault does not suggest
dropping those lambdas' capture-defaults in the first place.)

Change-Id: Iee7efb485187cbe8eba6a2d470afca4993eb1816
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120693
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2021-08-20 14:25:38 +02:00

988 lines
28 KiB
C++

/* -*- 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_features.h>
#include <config_folders.h>
#include <rtl/bootstrap.h>
#include <rtl/bootstrap.hxx>
#include <osl/diagnose.h>
#include <osl/module.h>
#include <osl/process.h>
#include <osl/file.hxx>
#include <osl/mutex.hxx>
#include <osl/profile.hxx>
#include <osl/security.hxx>
#include <rtl/alloc.h>
#include <rtl/string.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <rtl/byteseq.hxx>
#include <rtl/instance.hxx>
#include <rtl/malformeduriexception.hxx>
#include <rtl/uri.hxx>
#include <sal/log.hxx>
#include <o3tl/lru_map.hxx>
#include <vector>
#include <algorithm>
#include <unordered_map>
#ifdef ANDROID
#include <osl/detail/android-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;
char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:";
bool isPathnameUrl(OUString const & url)
{
return url.matchIgnoreAsciiCase(VND_SUN_STAR_PATHNAME);
}
bool resolvePathnameUrl(OUString * url)
{
OSL_ASSERT(url);
if (!isPathnameUrl(*url) ||
(osl::FileBase::getFileURLFromSystemPath(
url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *url) ==
osl::FileBase::E_None))
{
return true;
}
*url = OUString();
return false;
}
enum LookupMode {
LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
struct ExpandRequestLink {
ExpandRequestLink const * next;
Bootstrap_Impl const * file;
OUString key;
};
OUString expandMacros(
Bootstrap_Impl const * file, OUString const & text, LookupMode mode,
ExpandRequestLink const * requestStack);
OUString recursivelyExpandMacros(
Bootstrap_Impl const * file, OUString const & 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 const & name, OUString const & value )
: sName( name ),
sValue( 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);
#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()
&& fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt))
{
fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
}
progExt = ".exe";
if (fileName.getLength() > progExt.getLength()
&& fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(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"), 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(line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US);
nameValue.sValue = OStringToOUString(line.copy(nIndex+1).trim(), 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, LOOKUP_MODE_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;
};
struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni >
{};
}
bool Bootstrap_Impl::getValue(
OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
LookupMode mode, bool override, ExpandRequestLink const * requestStack)
const
{
if (mode == LOOKUP_MODE_NORMAL && key == "URE_BOOTSTRAP")
mode = LOOKUP_MODE_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;
}
#ifdef ANDROID
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 == LOOKUP_MODE_NORMAL)
{
FundamentalIniData const & d = FundamentalIni::get();
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 == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
text :
recursivelyExpandMacros(
this, text,
(mode == LOOKUP_MODE_URE_BOOTSTRAP ?
LOOKUP_MODE_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, LOOKUP_MODE_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 getting: 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),
LOOKUP_MODE_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(OUString const & text, sal_Int32 * pos, bool * escaped)
{
OSL_ASSERT(pos && *pos >= 0 && *pos < text.getLength() && escaped);
sal_Unicode c = text[(*pos)++];
if (c == '\\')
{
int n1, n2, n3, n4;
if (*pos < text.getLength() - 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.getLength())
{
*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, OUString const & text, LookupMode mode,
ExpandRequestLink const * requestStack)
{
SAL_INFO("sal.bootstrap", "expandMacros called with: " << text);
OUStringBuffer buf(2048);
for (sal_Int32 i = 0; i < text.getLength();)
{
bool escaped;
sal_Unicode c = read(text, &i, &escaped);
if (escaped || c != '$')
{
buf.append(c);
}
else
{
if (i < text.getLength() && text[i] == '{')
{
++i;
sal_Int32 p = i;
sal_Int32 nesting = 0;
OUString seg[3];
int n = 0;
while (i < text.getLength())
{
sal_Int32 j = i;
c = read(text, &i, &escaped);
if (!escaped)
{
switch (c)
{
case '{':
++nesting;
break;
case '}':
if (nesting == 0)
{
seg[n++] = text.copy(p, j - p);
goto done;
}
else
{
--nesting;
}
break;
case ':':
if (nesting == 0 && n < 2)
{
seg[n++] = text.copy(p, j - p);
p = i;
}
break;
}
}
}
done:
for (int j = 0; j < n; ++j)
{
seg[j] = expandMacros(file, seg[j], mode, requestStack);
}
if (n == 1)
{
buf.append(lookup(file, mode, false, seg[0], 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
{
if (n == 3 && seg[1].isEmpty())
{
// For backward compatibility, treat ${file::key} the
// same as just ${file:key}:
seg[1] = seg[2];
n = 2;
}
if (n == 2)
{
buf.append(
lookup(
static_cast< Bootstrap_Impl * >(
rtl::Bootstrap(seg[0]).getHandle()),
mode, false, seg[1], 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(text.getLength());
for (; i < text.getLength();)
{
sal_Int32 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: */