/* -*- 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 <sal/config.h>
#include <cassert>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <utility>
#include <vector>
#include <set>
#include <com/sun/star/beans/Optional.hpp>
#include <com/sun/star/beans/UnknownPropertyException.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/NoSuchElementException.hpp>
#include <com/sun/star/lang/WrappedTargetException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Exception.hpp>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/uno/XInterface.hpp>
#include <cppuhelper/exc_hlp.hxx>
#include <config_dconf.h>
#include <config_folders.h>
#include <osl/conditn.hxx>
#include <osl/file.hxx>
#include <osl/mutex.hxx>
#include <rtl/bootstrap.hxx>
#include <rtl/ref.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <sal/types.h>
#include <salhelper/thread.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <o3tl/string_view.hxx>
#include "additions.hxx"
#include "components.hxx"
#include "data.hxx"
#include "lock.hxx"
#include "modifications.hxx"
#include "node.hxx"
#include "nodemap.hxx"
#include "parsemanager.hxx"
#include "partial.hxx"
#include "rootaccess.hxx"
#include "writemodfile.hxx"
#include "xcdparser.hxx"
#include "xcuparser.hxx"
#include "xcsparser.hxx"
#if ENABLE_DCONF
#include "dconf.hxx"
#endif
#if defined(_WIN32)
#include "winreg.hxx"
#endif
namespace configmgr {
namespace {
struct UnresolvedVectorItem {
OUString name;
rtl::Reference< ParseManager > manager;
UnresolvedVectorItem(
OUString theName,
rtl::Reference< ParseManager > theManager):
name(std::move(theName)), manager(std::move(theManager)) {}
};
typedef std::vector< UnresolvedVectorItem > UnresolvedVector;
void parseXcsFile(
OUString const & url, int layer, Data & data, Partial const * partial,
Modifications * modifications, Additions * additions)
{
assert(partial == nullptr && modifications == nullptr && additions == nullptr);
(void) partial; (void) modifications; (void) additions;
bool ok = rtl::Reference< ParseManager >(
new ParseManager(url, new XcsParser(layer, data)))->parse(nullptr);
assert(ok);
(void) ok; // avoid warnings
}
void parseXcuFile(
OUString const & url, int layer, Data & data, Partial const * partial,
Modifications * modifications, Additions * additions)
{
bool ok = rtl::Reference< ParseManager >(
new ParseManager(
url,
new XcuParser(layer, data, partial, modifications, additions)))->
parse(nullptr);
assert(ok);
(void) ok; // avoid warnings
}
OUString expand(OUString const & str) {
OUString s(str);
rtl::Bootstrap::expandMacros(s); //TODO: detect failure
return s;
}
bool canRemoveFromLayer(int layer, rtl::Reference< Node > const & node) {
assert(node.is());
if (node->getLayer() > layer && node->getLayer() < Data::NO_LAYER) {
return false;
}
switch (node->kind()) {
case Node::KIND_LOCALIZED_PROPERTY:
case Node::KIND_GROUP:
for (auto const& member : node->getMembers())
{
if (!canRemoveFromLayer(layer, member.second)) {
return false;
}
}
return true;
case Node::KIND_SET:
return node->getMembers().empty();
default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE
return true;
}
}
}
class Components::WriteThread: public salhelper::Thread {
public:
WriteThread(
rtl::Reference< WriteThread > * reference, Components & components,
OUString url, Data const & data);
void trigger() {
std::scoped_lock l(triggerMutex_);
triggered_ = true;
triggerCondition_.notify_all();
}
void flush() {
delayOrTerminate_.set();
trigger();
}
private:
virtual ~WriteThread() override {}
virtual void execute() override;
rtl::Reference< WriteThread > * reference_;
Components & components_;
OUString url_;
Data const & data_;
osl::Condition delayOrTerminate_;
std::mutex triggerMutex_;
std::condition_variable triggerCondition_;
bool triggered_;
std::shared_ptr<osl::Mutex> lock_;
};
Components::WriteThread::WriteThread(
rtl::Reference< WriteThread > * reference, Components & components,
OUString url, Data const & data):
Thread("configmgrWriter"), reference_(reference), components_(components),
url_(std::move(url)), data_(data),
triggered_(false),
lock_( lock() )
{
assert(reference != nullptr);
}
void Components::WriteThread::execute() {
for (;;) {
{
std::unique_lock l(triggerMutex_);
while (!triggered_) {
triggerCondition_.wait(l);
}
triggered_ = false;
}
delayOrTerminate_.wait(std::chrono::seconds(1));
// must not throw; result_error is harmless and ignored
osl::MutexGuard g(*lock_); // must not throw
try {
try {
writeModFile(components_, url_, data_);
} catch (css::uno::RuntimeException &) {
// Ignore write errors, instead of aborting:
TOOLS_WARN_EXCEPTION("configmgr", "error writing modifications");
}
} catch (...) {
reference_->clear();
throw;
}
if (!delayOrTerminate_.check()) {
continue;
}
reference_->clear();
break;
}
}
Components & Components::getSingleton(
css::uno::Reference< css::uno::XComponentContext > const & context)
{
assert(context.is());
static Components singleton(context);
return singleton;
}
bool Components::allLocales(std::u16string_view locale) {
return locale == u"*";
}
rtl::Reference< Node > Components::resolvePathRepresentation(
OUString const & pathRepresentation,
OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
const
{
return data_.resolvePathRepresentation(
pathRepresentation, canonicRepresentation, path, finalizedLayer);
}
rtl::Reference< Node > Components::getTemplate(OUString const & fullName) const
{
return data_.getTemplate(Data::NO_LAYER, fullName);
}
void Components::addRootAccess(rtl::Reference< RootAccess > const & access) {
roots_.insert(access.get());
}
void Components::removeRootAccess(RootAccess * access) {
roots_.erase(access);
}
void Components::initGlobalBroadcaster(
Modifications const & modifications,
rtl::Reference< RootAccess > const & exclude, Broadcaster * broadcaster)
{
//TODO: Iterate only over roots w/ listeners:
for (auto const& elemRoot : roots_)
{
rtl::Reference< RootAccess > root;
if (elemRoot->acquireCounting() > 1) {
root.set(elemRoot); // must not throw
}
elemRoot->releaseNondeleting();
if (root.is()) {
if (root != exclude) {
std::vector<OUString> path(root->getAbsolutePath());
Modifications::Node const * mods = &modifications.getRoot();
for (auto const& pathElem : path)
{
Modifications::Node::Children::const_iterator k(
mods->children.find(pathElem));
if (k == mods->children.end()) {
mods = nullptr;
break;
}
mods = &k->second;
}
//TODO: If the complete tree of which root is a part is deleted,
// or replaced, mods will be null, but some of the listeners
// from within root should probably fire nonetheless:
if (mods != nullptr) {
root->initBroadcaster(*mods, broadcaster);
}
}
}
}
}
void Components::addModification(std::vector<OUString> const & path) {
data_.modifications.add(path);
}
void Components::writeModifications() {
if (data_.modifications.empty())
return;
switch (modificationTarget_) {
case ModificationTarget::None:
break;
case ModificationTarget::File:
if (!writeThread_.is()) {
writeThread_ = new WriteThread(
&writeThread_, *this, modificationFileUrl_, data_);
writeThread_->launch();
}
writeThread_->trigger();
break;
case ModificationTarget::Dconf:
#if ENABLE_DCONF
dconf::writeModifications(*this, data_);
#endif
break;
}
}
void Components::flushModifications() {
rtl::Reference< WriteThread > thread;
{
osl::MutexGuard g(*lock_);
thread = writeThread_;
}
if (thread.is()) {
thread->flush();
thread->join();
}
}
void Components::insertExtensionXcsFile(
bool shared, OUString const & fileUri)
{
int layer = getExtensionLayer(shared);
try {
parseXcsFile(fileUri, layer, data_, nullptr, nullptr, nullptr);
} catch (css::container::NoSuchElementException & e) {
throw css::uno::RuntimeException(
"insertExtensionXcsFile does not exist: " + e.Message);
}
}
void Components::insertExtensionXcuFile(
bool shared, OUString const & fileUri, Modifications * modifications)
{
assert(modifications != nullptr);
int layer = getExtensionLayer(shared) + 1;
Additions * adds = data_.addExtensionXcuAdditions(fileUri, layer);
try {
parseXcuFile(fileUri, layer, data_, nullptr, modifications, adds);
} catch (css::container::NoSuchElementException & e) {
data_.removeExtensionXcuAdditions(fileUri);
throw css::uno::RuntimeException(
"insertExtensionXcuFile does not exist: " + e.Message);
}
}
void Components::removeExtensionXcuFile(
OUString const & fileUri, Modifications * modifications)
{
//TODO: Ideally, exactly the data coming from the specified xcu file would
// be removed. However, not enough information is recorded in the in-memory
// data structures to do so. So, as a workaround, all those set elements
// that were freshly added by the xcu and have afterwards been left
// unchanged or have only had their properties changed in the user layer are
// removed (and nothing else). The heuristic to determine
// whether a node has been left unchanged is to check the layer ID (as
// usual) and additionally to check that the node does not recursively
// contain any non-empty sets (multiple extension xcu files are merged into
// one layer, so checking layer ID alone is not enough). Since
// item->additions records all additions of set members in textual order,
// the latter check works well when iterating through item->additions in
// reverse order.
assert(modifications != nullptr);
rtl::Reference< Data::ExtensionXcu > item(
data_.removeExtensionXcuAdditions(fileUri));
if (!item.is())
return;
for (Additions::reverse_iterator i(item->additions.rbegin());
i != item->additions.rend(); ++i)
{
rtl::Reference< Node > parent;
NodeMap const * map = &data_.getComponents();
rtl::Reference< Node > node;
for (auto const& j : *i)
{
parent = node;
node = map->findNode(Data::NO_LAYER, j);
if (!node.is()) {
break;
}
map = &node->getMembers();
}
if (node.is()) {
assert(parent.is());
if (parent->kind() == Node::KIND_SET) {
assert(
node->kind() == Node::KIND_GROUP ||
node->kind() == Node::KIND_SET);
if (canRemoveFromLayer(item->layer, node)) {
parent->getMembers().erase(i->back());
data_.modifications.remove(*i);
modifications->add(*i);
}
}
}
}
writeModifications();
}
void Components::insertModificationXcuFile(
OUString const & fileUri,
css::uno::Sequence< OUString > const & includedPaths,
css::uno::Sequence< OUString > const & excludedPaths,
Modifications * modifications)
{
assert(modifications != nullptr);
Partial part(includedPaths, excludedPaths);
try {
parseFileLeniently(
&parseXcuFile, fileUri, Data::NO_LAYER, &part, modifications, nullptr);
} catch (const css::container::NoSuchElementException &) {
TOOLS_WARN_EXCEPTION(
"configmgr",
"error inserting non-existing \"" << fileUri << "\"");
}
}
css::beans::Optional< css::uno::Any > Components::getExternalValue(
std::u16string_view descriptor)
{
size_t i = descriptor.find(' ');
if (i == 0 || i == std::u16string_view::npos) {
throw css::uno::RuntimeException(
OUString::Concat("bad external value descriptor ") + descriptor);
}
//TODO: Do not make calls with mutex locked:
OUString name(descriptor.substr(0, i));
ExternalServices::iterator j(externalServices_.find(name));
if (j == externalServices_.end()) {
css::uno::Reference< css::uno::XInterface > service;
try {
service = context_->getServiceManager()->createInstanceWithContext(
name, context_);
} catch (const css::uno::RuntimeException &) {
// Assuming these exceptions are real errors:
throw;
} catch (const css::uno::Exception &) {
// Assuming these exceptions indicate that the service is not
// installed:
TOOLS_WARN_EXCEPTION(
"configmgr",
"createInstance(" << name << ") failed");
}
css::uno::Reference< css::beans::XPropertySet > propset;
if (service.is()) {
propset.set( service, css::uno::UNO_QUERY_THROW);
}
j = externalServices_.emplace(name, propset).first;
}
css::beans::Optional< css::uno::Any > value;
if (j->second.is()) {
try {
if (!(j->second->getPropertyValue(OUString(descriptor.substr(i + 1))) >>=
value))
{
throw css::uno::RuntimeException(
OUString::Concat("cannot obtain external value through ") + descriptor);
}
} catch (css::beans::UnknownPropertyException & e) {
throw css::uno::RuntimeException(
"unknown external value descriptor ID: " + e.Message);
} catch (css::lang::WrappedTargetException & e) {
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException(
"cannot obtain external value: " + e.Message,
nullptr, anyEx );
}
}
return value;
}
Components::Components(
css::uno::Reference< css::uno::XComponentContext > const & context):
context_(context), sharedExtensionLayer_(-1), userExtensionLayer_(-1),
modificationTarget_(ModificationTarget::None)
{
assert(context.is());
lock_ = lock();
OUString conf(expand(u"${CONFIGURATION_LAYERS}"_ustr));
int layer = 0;
for (sal_Int32 i = 0;;) {
while (i != conf.getLength() && conf[i] == ' ') {
++i;
}
if (i == conf.getLength()) {
break;
}
if (modificationTarget_ != ModificationTarget::None) {
throw css::uno::RuntimeException(
u"CONFIGURATION_LAYERS: modification target layer followed by"
" further layers"_ustr);
}
sal_Int32 c = i;
for (;; ++c) {
if (c == conf.getLength() || conf[c] == ' ') {
throw css::uno::RuntimeException(
"CONFIGURATION_LAYERS: missing ':' in \"" + conf + "\"");
}
if (conf[c] == ':') {
break;
}
}
sal_Int32 n = conf.indexOf(' ', c + 1);
if (n == -1) {
n = conf.getLength();
}
OUString type(conf.copy(i, c - i));
OUString url(conf.copy(c + 1, n - c - 1));
if (type == "xcsxcu") {
sal_uInt32 nStartTime = osl_getGlobalTimer();
parseXcsXcuLayer(layer, url);
SAL_INFO("configmgr", "parseXcsXcuLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
layer += 2; //TODO: overflow
} else if (type == "bundledext") {
parseXcsXcuIniLayer(layer, url, false);
layer += 2; //TODO: overflow
} else if (type == "sharedext") {
if (sharedExtensionLayer_ != -1) {
throw css::uno::RuntimeException(
u"CONFIGURATION_LAYERS: multiple \"sharedext\" layers"_ustr);
}
sharedExtensionLayer_ = layer;
parseXcsXcuIniLayer(layer, url, true);
layer += 2; //TODO: overflow
} else if (type == "userext") {
if (userExtensionLayer_ != -1) {
throw css::uno::RuntimeException(
u"CONFIGURATION_LAYERS: multiple \"userext\" layers"_ustr);
}
userExtensionLayer_ = layer;
parseXcsXcuIniLayer(layer, url, true);
layer += 2; //TODO: overflow
} else if (type == "res") {
sal_uInt32 nStartTime = osl_getGlobalTimer();
parseResLayer(layer, url);
SAL_INFO("configmgr", "parseResLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
++layer; //TODO: overflow
#if ENABLE_DCONF
} else if (type == "dconf") {
if (url == "!") {
modificationTarget_ = ModificationTarget::Dconf;
dconf::readLayer(data_, Data::NO_LAYER);
} else if (url == "*") {
dconf::readLayer(data_, layer);
} else {
throw css::uno::RuntimeException(
"CONFIGURATION_LAYERS: unknown \"dconf\" kind \"" + url
+ "\"");
}
++layer; //TODO: overflow
#endif
#if defined(_WIN32)
} else if (type == "winreg") {
WinRegType eType;
if (url == "LOCAL_MACHINE" || url.isEmpty()/*backwards comp.*/) {
eType = WinRegType::LOCAL_MACHINE;
} else if (url == "CURRENT_USER") {
eType = WinRegType::CURRENT_USER;
} else {
throw css::uno::RuntimeException(
"CONFIGURATION_LAYERS: unknown \"winreg\" kind \"" + url
+ "\"");
}
OUString aTempFileURL;
if (dumpWindowsRegistry(&aTempFileURL, eType)) {
parseFileLeniently(&parseXcuFile, aTempFileURL, layer, nullptr, nullptr, nullptr);
if (!getenv("SAL_CONFIG_WINREG_RETAIN_TMP"))
osl::File::remove(aTempFileURL);
}
++layer; //TODO: overflow
#endif
} else if (type == "user") {
bool write;
if (url.startsWith("!", &url)) {
write = true;
} else if (url.startsWith("*", &url)) {
write = false;
} else {
write = true; // for backwards compatibility
}
if (url.isEmpty()) {
throw css::uno::RuntimeException(
u"CONFIGURATION_LAYERS: empty \"user\" URL"_ustr);
}
bool ignore = false;
#if ENABLE_DCONF
if (write) {
OUString token(
expand("${SYSUSERCONFIG}/libreoffice/dconfwrite"));
osl::DirectoryItem it;
osl::FileBase::RC e = osl::DirectoryItem::get(token, it);
ignore = e == osl::FileBase::E_None;
SAL_INFO(
"configmgr",
"dconf write (<" << token << "> " << +e << "): "
<< int(ignore));
if (ignore) {
modificationTarget_ = ModificationTarget::Dconf;
}
}
#endif
if (!ignore) {
if (write) {
modificationTarget_ = ModificationTarget::File;
modificationFileUrl_ = url;
}
parseModificationLayer(write ? Data::NO_LAYER : layer, url);
}
++layer; //TODO: overflow
} else {
throw css::uno::RuntimeException(
"CONFIGURATION_LAYERS: unknown layer type \"" + type + "\"");
}
i = n;
}
}
Components::~Components()
{
flushModifications();
for (auto const& rootElem : roots_)
{
rootElem->setAlive(false);
}
}
void Components::parseFileLeniently(
FileParser * parseFile, OUString const & url, int layer,
Partial const * partial, Modifications * modifications,
Additions * additions)
{
assert(parseFile != nullptr);
try {
(*parseFile)(url, layer, data_, partial, modifications, additions);
} catch (const css::container::NoSuchElementException &) {
throw;
} catch (const css::uno::Exception &) { //TODO: more specific exception catching
// Ignore invalid XML files, instead of completely preventing OOo from
// starting:
TOOLS_WARN_EXCEPTION(
"configmgr",
"error reading \"" << url << "\"");
}
}
void Components::parseFiles(
int layer, OUString const & extension, FileParser * parseFile,
OUString const & url, bool recursive)
{
osl::Directory dir(url);
switch (dir.open()) {
case osl::FileBase::E_None:
break;
case osl::FileBase::E_NOENT:
if (!recursive) {
return;
}
[[fallthrough]];
default:
throw css::uno::RuntimeException(
"cannot open directory " + url);
}
for (;;) {
osl::DirectoryItem i;
osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
if (rc == osl::FileBase::E_NOENT) {
break;
}
if (rc != osl::FileBase::E_None) {
throw css::uno::RuntimeException(
"cannot iterate directory " + url);
}
osl::FileStatus stat(
osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
osl_FileStatus_Mask_FileURL);
if (i.getFileStatus(stat) != osl::FileBase::E_None) {
throw css::uno::RuntimeException(
"cannot stat in directory " + url);
}
if (stat.getFileType() == osl::FileStatus::Directory) { //TODO: symlinks
parseFiles(layer, extension, parseFile, stat.getFileURL(), true);
} else {
OUString file(stat.getFileName());
if (file.endsWith(extension)) {
try {
parseFileLeniently(
parseFile, stat.getFileURL(), layer, nullptr, nullptr, nullptr);
} catch (css::container::NoSuchElementException & e) {
if (stat.getFileType() == osl::FileStatus::Link) {
SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
continue;
}
throw css::uno::RuntimeException(
"stat'ed file does not exist: " + e.Message);
}
}
}
}
}
void Components::parseFileList(
int layer, FileParser * parseFile, std::u16string_view urls,
bool recordAdditions)
{
for (sal_Int32 i = 0;;) {
OUString url(o3tl::getToken(urls, 0, ' ', i));
if (!url.isEmpty()) {
Additions * adds = nullptr;
if (recordAdditions) {
adds = data_.addExtensionXcuAdditions(url, layer);
}
try {
parseFileLeniently(parseFile, url, layer, nullptr, nullptr, adds);
} catch (const css::container::NoSuchElementException &) {
TOOLS_WARN_EXCEPTION("configmgr", "file does not exist");
if (adds != nullptr) {
data_.removeExtensionXcuAdditions(url);
}
}
}
if (i == -1) {
break;
}
}
}
void Components::parseXcdFiles(int layer, OUString const & url) {
osl::Directory dir(url);
switch (dir.open()) {
case osl::FileBase::E_None:
break;
case osl::FileBase::E_NOENT:
return;
default:
throw css::uno::RuntimeException(
"cannot open directory " + url);
}
UnresolvedVector unres;
std::set< OUString > existingDeps;
std::set< OUString > processedDeps;
for (;;) {
osl::DirectoryItem i;
osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
if (rc == osl::FileBase::E_NOENT) {
break;
}
if (rc != osl::FileBase::E_None) {
throw css::uno::RuntimeException(
"cannot iterate directory " + url);
}
osl::FileStatus stat(
osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
osl_FileStatus_Mask_FileURL);
if (i.getFileStatus(stat) != osl::FileBase::E_None) {
throw css::uno::RuntimeException(
"cannot stat in directory " + url);
}
if (stat.getFileType() != osl::FileStatus::Directory) { //TODO: symlinks
OUString file(stat.getFileName());
OUString name;
if (file.endsWith(".xcd", &name)) {
existingDeps.insert(name);
rtl::Reference< ParseManager > manager;
try {
manager = new ParseManager(
stat.getFileURL(),
new XcdParser(layer, processedDeps, data_));
} catch (css::container::NoSuchElementException & e) {
if (stat.getFileType() == osl::FileStatus::Link) {
SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
continue;
}
throw css::uno::RuntimeException(
"stat'ed file does not exist: " + e.Message);
}
if (manager->parse(nullptr)) {
processedDeps.insert(name);
} else {
unres.emplace_back(name, manager);
}
}
}
}
while (!unres.empty()) {
bool resolved = false;
for (UnresolvedVector::iterator i(unres.begin()); i != unres.end();) {
if (i->manager->parse(&existingDeps)) {
processedDeps.insert(i->name);
i = unres.erase(i);
resolved = true;
} else {
++i;
}
}
if (!resolved) {
throw css::uno::RuntimeException(
"xcd: unresolved dependencies in " + url);
}
}
}
void Components::parseXcsXcuLayer(int layer, OUString const & url) {
parseXcdFiles(layer, url);
parseFiles(layer, u".xcs"_ustr, &parseXcsFile, url + "/schema", false);
parseFiles(layer + 1, u".xcu"_ustr, &parseXcuFile, url + "/data", false);
}
void Components::parseXcsXcuIniLayer(
int layer, OUString const & url, bool recordAdditions)
{
// Check if ini file exists (otherwise .override would still read global
// SCHEMA/DATA variables, which could interfere with unrelated environment
// variables):
if (rtl::Bootstrap(url).getHandle() == nullptr) return;
OUStringBuffer prefix("${.override:");
for (sal_Int32 i = 0; i != url.getLength(); ++i) {
sal_Unicode c = url[i];
switch (c) {
case '$':
case ':':
case '\\':
prefix.append('\\');
[[fallthrough]];
default:
prefix.append(c);
}
}
prefix.append(':');
OUString urls(prefix + "SCHEMA}");
rtl::Bootstrap::expandMacros(urls);
if (!urls.isEmpty()) {
parseFileList(layer, &parseXcsFile, urls, false);
}
urls = prefix + "DATA}";
rtl::Bootstrap::expandMacros(urls);
if (!urls.isEmpty()) {
parseFileList(layer + 1, &parseXcuFile, urls, recordAdditions);
}
}
void Components::parseResLayer(int layer, std::u16string_view url) {
OUString resUrl(OUString::Concat(url) + "/res");
parseXcdFiles(layer, resUrl);
parseFiles(layer, u".xcu"_ustr, &parseXcuFile, resUrl, false);
}
void Components::parseModificationLayer(int layer, OUString const & url) {
try {
parseFileLeniently(&parseXcuFile, url, layer, nullptr, nullptr, nullptr);
} catch (css::container::NoSuchElementException &) {
SAL_INFO(
"configmgr", "user registrymodifications.xcu does not (yet) exist");
// Migrate old user layer data (can be removed once migration is no
// longer relevant, probably OOo 4; also see hack for xsi namespace in
// xmlreader::XmlReader::registerNamespaceIri):
parseFiles(
layer, u".xcu"_ustr, &parseXcuFile,
expand(
u"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
":UserInstallation}/user/registry/data"_ustr),
false);
}
}
int Components::getExtensionLayer(bool shared) const {
int layer = shared ? sharedExtensionLayer_ : userExtensionLayer_;
if (layer == -1) {
throw css::uno::RuntimeException(
u"insert extension xcs/xcu file into undefined layer"_ustr);
}
return layer;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V547 Expression '!ignore' is always true.
↑ V1089 Waiting on condition variable without predicate. A thread can wait indefinitely or experience a spurious wakeup. Consider passing a predicate as the second argument.