1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
20 #include <sal/config.h>
29 #include <com/sun/star/beans/Optional.hpp>
30 #include <com/sun/star/beans/UnknownPropertyException.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/container/NoSuchElementException.hpp>
33 #include <com/sun/star/lang/WrappedTargetException.hpp>
34 #include <com/sun/star/uno/Any.hxx>
35 #include <com/sun/star/uno/Exception.hpp>
36 #include <com/sun/star/uno/Reference.hxx>
37 #include <com/sun/star/uno/RuntimeException.hpp>
38 #include <com/sun/star/uno/XComponentContext.hpp>
39 #include <com/sun/star/uno/XInterface.hpp>
40 #include <config_dconf.h>
41 #include <config_folders.h>
42 #include <osl/conditn.hxx>
43 #include <osl/file.hxx>
44 #include <osl/mutex.hxx>
45 #include <rtl/bootstrap.hxx>
46 #include <rtl/ref.hxx>
47 #include <rtl/ustrbuf.hxx>
48 #include <rtl/ustring.hxx>
49 #include <rtl/instance.hxx>
50 #include <sal/log.hxx>
51 #include <sal/types.h>
52 #include <salhelper/thread.hxx>
53 #include <comphelper/backupfilehelper.hxx>
55 #include "additions.hxx"
56 #include "components.hxx"
59 #include "modifications.hxx"
61 #include "nodemap.hxx"
62 #include "parsemanager.hxx"
63 #include "partial.hxx"
64 #include "rootaccess.hxx"
65 #include "writemodfile.hxx"
66 #include "xcdparser.hxx"
67 #include "xcuparser.hxx"
68 #include "xcsparser.hxx"
82 struct UnresolvedVectorItem
{
84 rtl::Reference
< ParseManager
> manager
;
87 OUString
const & theName
,
88 rtl::Reference
< ParseManager
> const & theManager
):
89 name(theName
), manager(theManager
) {}
92 typedef std::vector
< UnresolvedVectorItem
> UnresolvedVector
;
95 OUString
const & url
, int layer
, Data
& data
, Partial
const * partial
,
96 Modifications
* modifications
, Additions
* additions
)
98 assert(partial
== nullptr && modifications
== nullptr && additions
== nullptr);
99 (void) partial
; (void) modifications
; (void) additions
;
100 bool ok
= rtl::Reference
< ParseManager
>(
101 new ParseManager(url
, new XcsParser(layer
, data
)))->parse(nullptr);
103 (void) ok
; // avoid warnings
107 OUString
const & url
, int layer
, Data
& data
, Partial
const * partial
,
108 Modifications
* modifications
, Additions
* additions
)
110 bool ok
= rtl::Reference
< ParseManager
>(
113 new XcuParser(layer
, data
, partial
, modifications
, additions
)))->
116 (void) ok
; // avoid warnings
119 OUString
expand(OUString
const & str
) {
121 rtl::Bootstrap::expandMacros(s
); //TODO: detect failure
125 bool canRemoveFromLayer(int layer
, rtl::Reference
< Node
> const & node
) {
127 if (node
->getLayer() > layer
&& node
->getLayer() < Data::NO_LAYER
) {
130 switch (node
->kind()) {
131 case Node::KIND_LOCALIZED_PROPERTY
:
132 case Node::KIND_GROUP
:
133 for (NodeMap::const_iterator
i(node
->getMembers().begin());
134 i
!= node
->getMembers().end(); ++i
)
136 if (!canRemoveFromLayer(layer
, i
->second
)) {
142 return node
->getMembers().empty();
143 default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE
150 class Components::WriteThread
: public salhelper::Thread
{
153 rtl::Reference
< WriteThread
> * reference
, Components
& components
,
154 OUString
const & url
, Data
const & data
);
156 void flush() { delay_
.set(); }
159 virtual ~WriteThread() override
{}
161 virtual void execute() override
;
163 rtl::Reference
< WriteThread
> * reference_
;
164 Components
& components_
;
167 osl::Condition delay_
;
168 std::shared_ptr
<osl::Mutex
> lock_
;
171 Components::WriteThread::WriteThread(
172 rtl::Reference
< WriteThread
> * reference
, Components
& components
,
173 OUString
const & url
, Data
const & data
):
174 Thread("configmgrWriter"), reference_(reference
), components_(components
),
175 url_(url
), data_(data
),
178 assert(reference
!= nullptr);
181 void Components::WriteThread::execute() {
182 delay_
.wait(std::chrono::seconds(1)); // must not throw; result_error is harmless and ignored
183 osl::MutexGuard
g(*lock_
); // must not throw
186 writeModFile(components_
, url_
, data_
);
187 } catch (css::uno::RuntimeException
& e
) {
188 // Ignore write errors, instead of aborting:
189 SAL_WARN("configmgr", "error writing modifications: " << e
);
198 class theComponentsSingleton
:
199 public rtl::StaticWithArg
<
201 css::uno::Reference
< css::uno::XComponentContext
>,
202 theComponentsSingleton
>
206 Components
& Components::getSingleton(
207 css::uno::Reference
< css::uno::XComponentContext
> const & context
)
209 assert(context
.is());
210 return theComponentsSingleton::get(context
);
213 bool Components::allLocales(OUString
const & locale
) {
214 return locale
== "*";
217 rtl::Reference
< Node
> Components::resolvePathRepresentation(
218 OUString
const & pathRepresentation
,
219 OUString
* canonicRepresentation
, std::vector
<OUString
> * path
, int * finalizedLayer
)
222 return data_
.resolvePathRepresentation(
223 pathRepresentation
, canonicRepresentation
, path
, finalizedLayer
);
226 rtl::Reference
< Node
> Components::getTemplate(OUString
const & fullName
) const
228 return data_
.getTemplate(Data::NO_LAYER
, fullName
);
231 void Components::addRootAccess(rtl::Reference
< RootAccess
> const & access
) {
232 roots_
.insert(access
.get());
235 void Components::removeRootAccess(RootAccess
* access
) {
236 roots_
.erase(access
);
239 void Components::initGlobalBroadcaster(
240 Modifications
const & modifications
,
241 rtl::Reference
< RootAccess
> const & exclude
, Broadcaster
* broadcaster
)
243 //TODO: Iterate only over roots w/ listeners:
244 for (WeakRootSet::iterator
i(roots_
.begin()); i
!= roots_
.end(); ++i
) {
245 rtl::Reference
< RootAccess
> root
;
246 if ((*i
)->acquireCounting() > 1) {
247 root
.set(*i
); // must not throw
249 (*i
)->releaseNondeleting();
251 if (root
!= exclude
) {
252 std::vector
<OUString
> path(root
->getAbsolutePath());
253 Modifications::Node
const * mods
= &modifications
.getRoot();
254 for (auto j(path
.begin()); j
!= path
.end(); ++j
) {
255 Modifications::Node::Children::const_iterator
k(
256 mods
->children
.find(*j
));
257 if (k
== mods
->children
.end()) {
263 //TODO: If the complete tree of which root is a part is deleted,
264 // or replaced, mods will be null, but some of the listeners
265 // from within root should probably fire nonetheless:
266 if (mods
!= nullptr) {
267 root
->initBroadcaster(*mods
, broadcaster
);
274 void Components::addModification(std::vector
<OUString
> const & path
) {
275 data_
.modifications
.add(path
);
278 void Components::writeModifications() {
280 if (!data_
.modifications
.empty()) {
281 switch (modificationTarget_
) {
282 case ModificationTarget::None
:
284 case ModificationTarget::File
:
285 if (!writeThread_
.is()) {
286 writeThread_
= new WriteThread(
287 &writeThread_
, *this, modificationFileUrl_
, data_
);
288 writeThread_
->launch();
291 case ModificationTarget::Dconf
:
293 dconf::writeModifications(*this, data_
);
300 void Components::flushModifications() {
301 rtl::Reference
< WriteThread
> thread
;
303 osl::MutexGuard
g(*lock_
);
304 thread
= writeThread_
;
312 void Components::insertExtensionXcsFile(
313 bool shared
, OUString
const & fileUri
)
315 int layer
= getExtensionLayer(shared
);
317 parseXcsFile(fileUri
, layer
, data_
, nullptr, nullptr, nullptr);
318 } catch (css::container::NoSuchElementException
& e
) {
319 throw css::uno::RuntimeException(
320 "insertExtensionXcsFile does not exist: " + e
.Message
);
324 void Components::insertExtensionXcuFile(
325 bool shared
, OUString
const & fileUri
, Modifications
* modifications
)
327 assert(modifications
!= nullptr);
328 int layer
= getExtensionLayer(shared
) + 1;
329 Additions
* adds
= data_
.addExtensionXcuAdditions(fileUri
, layer
);
331 parseXcuFile(fileUri
, layer
, data_
, nullptr, modifications
, adds
);
332 } catch (css::container::NoSuchElementException
& e
) {
333 data_
.removeExtensionXcuAdditions(fileUri
);
334 throw css::uno::RuntimeException(
335 "insertExtensionXcuFile does not exist: " + e
.Message
);
339 void Components::removeExtensionXcuFile(
340 OUString
const & fileUri
, Modifications
* modifications
)
342 //TODO: Ideally, exactly the data coming from the specified xcu file would
343 // be removed. However, not enough information is recorded in the in-memory
344 // data structures to do so. So, as a workaround, all those set elements
345 // that were freshly added by the xcu and have afterwards been left
346 // unchanged or have only had their properties changed in the user layer are
347 // removed (and nothing else). The heuristic to determine
348 // whether a node has been left unchanged is to check the layer ID (as
349 // usual) and additionally to check that the node does not recursively
350 // contain any non-empty sets (multiple extension xcu files are merged into
351 // one layer, so checking layer ID alone is not enough). Since
352 // item->additions records all additions of set members in textual order,
353 // the latter check works well when iterating through item->additions in
355 assert(modifications
!= nullptr);
356 rtl::Reference
< Data::ExtensionXcu
> item(
357 data_
.removeExtensionXcuAdditions(fileUri
));
359 for (Additions::reverse_iterator
i(item
->additions
.rbegin());
360 i
!= item
->additions
.rend(); ++i
)
362 rtl::Reference
< Node
> parent
;
363 NodeMap
const * map
= &data_
.getComponents();
364 rtl::Reference
< Node
> node
;
365 for (auto j(i
->begin()); j
!= i
->end(); ++j
) {
367 node
= map
->findNode(Data::NO_LAYER
, *j
);
371 map
= &node
->getMembers();
375 if (parent
->kind() == Node::KIND_SET
) {
377 node
->kind() == Node::KIND_GROUP
||
378 node
->kind() == Node::KIND_SET
);
379 if (canRemoveFromLayer(item
->layer
, node
)) {
380 parent
->getMembers().erase(i
->back());
381 data_
.modifications
.remove(*i
);
382 modifications
->add(*i
);
387 writeModifications();
391 void Components::insertModificationXcuFile(
392 OUString
const & fileUri
,
393 std::set
< OUString
> const & includedPaths
,
394 std::set
< OUString
> const & excludedPaths
,
395 Modifications
* modifications
)
397 assert(modifications
!= nullptr);
398 Partial
part(includedPaths
, excludedPaths
);
401 &parseXcuFile
, fileUri
, Data::NO_LAYER
, &part
, modifications
, nullptr);
402 } catch (css::container::NoSuchElementException
& e
) {
405 "error inserting non-existing \"" << fileUri
<< "\": " << e
);
409 css::beans::Optional
< css::uno::Any
> Components::getExternalValue(
410 OUString
const & descriptor
)
412 sal_Int32 i
= descriptor
.indexOf(' ');
414 throw css::uno::RuntimeException(
415 "bad external value descriptor " + descriptor
);
417 //TODO: Do not make calls with mutex locked:
418 OUString
name(descriptor
.copy(0, i
));
419 ExternalServices::iterator
j(externalServices_
.find(name
));
420 if (j
== externalServices_
.end()) {
421 css::uno::Reference
< css::uno::XInterface
> service
;
423 service
= context_
->getServiceManager()->createInstanceWithContext(
425 } catch (css::uno::RuntimeException
&) {
426 // Assuming these exceptions are real errors:
428 } catch (css::uno::Exception
& e
) {
429 // Assuming these exceptions indicate that the service is not
433 "createInstance(" << name
<< ") failed with " << e
);
435 css::uno::Reference
< css::beans::XPropertySet
> propset
;
437 propset
.set( service
, css::uno::UNO_QUERY_THROW
);
439 j
= externalServices_
.emplace(name
, propset
).first
;
441 css::beans::Optional
< css::uno::Any
> value
;
442 if (j
->second
.is()) {
444 if (!(j
->second
->getPropertyValue(descriptor
.copy(i
+ 1)) >>=
447 throw css::uno::RuntimeException(
448 "cannot obtain external value through " + descriptor
);
450 } catch (css::beans::UnknownPropertyException
& e
) {
451 throw css::uno::RuntimeException(
452 "unknown external value descriptor ID: " + e
.Message
);
453 } catch (css::lang::WrappedTargetException
& e
) {
454 throw css::uno::RuntimeException(
455 "cannot obtain external value: " + e
.Message
);
461 Components::Components(
462 css::uno::Reference
< css::uno::XComponentContext
> const & context
):
463 context_(context
), sharedExtensionLayer_(-1), userExtensionLayer_(-1),
464 modificationTarget_(ModificationTarget::None
)
466 assert(context
.is());
468 OUString
conf(expand("${CONFIGURATION_LAYERS}"));
470 for (sal_Int32 i
= 0;;) {
471 while (i
!= conf
.getLength() && conf
[i
] == ' ') {
474 if (i
== conf
.getLength()) {
477 if (modificationTarget_
!= ModificationTarget::None
) {
478 throw css::uno::RuntimeException(
479 "CONFIGURATION_LAYERS: modification target layer followed by"
484 if (c
== conf
.getLength() || conf
[c
] == ' ') {
485 throw css::uno::RuntimeException(
486 "CONFIGURATION_LAYERS: missing ':' in \"" + conf
+ "\"");
488 if (conf
[c
] == ':') {
492 sal_Int32 n
= conf
.indexOf(' ', c
+ 1);
494 n
= conf
.getLength();
496 OUString
type(conf
.copy(i
, c
- i
));
497 OUString
url(conf
.copy(c
+ 1, n
- c
- 1));
498 if (type
== "xcsxcu") {
499 sal_uInt32 nStartTime
= osl_getGlobalTimer();
500 parseXcsXcuLayer(layer
, url
);
501 SAL_INFO("configmgr", "parseXcsXcuLayer() took " << (osl_getGlobalTimer() - nStartTime
) << " ms");
502 layer
+= 2; //TODO: overflow
503 } else if (type
== "bundledext") {
504 parseXcsXcuIniLayer(layer
, url
, false);
505 layer
+= 2; //TODO: overflow
506 } else if (type
== "sharedext") {
507 if (sharedExtensionLayer_
!= -1) {
508 throw css::uno::RuntimeException(
509 "CONFIGURATION_LAYERS: multiple \"sharedext\" layers");
511 sharedExtensionLayer_
= layer
;
512 parseXcsXcuIniLayer(layer
, url
, true);
513 layer
+= 2; //TODO: overflow
514 } else if (type
== "userext") {
515 if (userExtensionLayer_
!= -1) {
516 throw css::uno::RuntimeException(
517 "CONFIGURATION_LAYERS: multiple \"userext\" layers");
519 userExtensionLayer_
= layer
;
520 parseXcsXcuIniLayer(layer
, url
, true);
521 layer
+= 2; //TODO: overflow
522 } else if (type
== "module") {
523 parseModuleLayer(layer
, url
);
524 ++layer
; //TODO: overflow
525 } else if (type
== "res") {
526 sal_uInt32 nStartTime
= osl_getGlobalTimer();
527 parseResLayer(layer
, url
);
528 SAL_INFO("configmgr", "parseResLayer() took " << (osl_getGlobalTimer() - nStartTime
) << " ms");
529 ++layer
; //TODO: overflow
531 } else if (type
== "dconf") {
533 modificationTarget_
= ModificationTarget::Dconf
;
534 dconf::readLayer(data_
, Data::NO_LAYER
);
535 } else if (url
== "*") {
536 dconf::readLayer(data_
, layer
);
538 throw css::uno::RuntimeException(
539 "CONFIGURATION_LAYERS: unknown \"dconf\" kind \"" + url
542 ++layer
; //TODO: overflow
545 } else if (type
== "winreg") {
547 if (url
== "LOCAL_MACHINE" || url
.isEmpty()/*backwards comp.*/) {
548 eType
= WinRegType::LOCAL_MACHINE
;
549 } else if (url
== "CURRENT_USER") {
550 eType
= WinRegType::CURRENT_USER
;
552 throw css::uno::RuntimeException(
553 "CONFIGURATION_LAYERS: unknown \"winreg\" kind \"" + url
556 OUString aTempFileURL
;
557 if (dumpWindowsRegistry(&aTempFileURL
, eType
)) {
558 parseFileLeniently(&parseXcuFile
, aTempFileURL
, layer
, nullptr, nullptr, nullptr);
559 if (!getenv("SAL_CONFIG_WINREG_RETAIN_TMP"))
560 osl::File::remove(aTempFileURL
);
562 ++layer
; //TODO: overflow
564 } else if (type
== "user") {
566 if (url
.startsWith("!", &url
)) {
568 } else if (url
.startsWith("*", &url
)) {
571 write
= true; // for backwards compatibility
574 throw css::uno::RuntimeException(
575 "CONFIGURATION_LAYERS: empty \"user\" URL");
581 expand("${SYSUSERCONFIG}/libreoffice/dconfwrite"));
582 osl::DirectoryItem it
;
583 osl::FileBase::RC e
= osl::DirectoryItem::get(token
, it
);
584 ignore
= e
== osl::FileBase::E_None
;
587 "dconf write (<" << token
<< "> " << +e
<< "): "
590 modificationTarget_
= ModificationTarget::Dconf
;
596 modificationTarget_
= ModificationTarget::File
;
597 modificationFileUrl_
= url
;
599 parseModificationLayer(write
? Data::NO_LAYER
: layer
, url
);
601 ++layer
; //TODO: overflow
603 throw css::uno::RuntimeException(
604 "CONFIGURATION_LAYERS: unknown layer type \"" + type
+ "\"");
610 Components::~Components()
612 // get flag if _exit was already called which is a sign to not secure user config.
613 // this is used for win only currently where calling _exit() unfortunately still
614 // calls destructors (what is not wanted). May be needed for other systems, too
615 // (unknown yet) but can do no harm
616 const bool bExitWasCalled(comphelper::BackupFileHelper::getExitWasCalled());
619 // we can add a SAL_WARN here for other systems where the destructor gets called after
620 // an _exit() call. Still safe - the getExitWasCalled() is used, but a hint that _exit
621 // behaves different on a system
622 SAL_WARN_IF(bExitWasCalled
, "configmgr", "Components::~Components() called after _exit() call");
627 // do not write, re-join threads
628 osl::MutexGuard
g(*lock_
);
630 if (writeThread_
.is())
632 writeThread_
->join();
638 flushModifications();
641 for (WeakRootSet::iterator
i(roots_
.begin()); i
!= roots_
.end(); ++i
) {
642 (*i
)->setAlive(false);
646 void Components::parseFileLeniently(
647 FileParser
* parseFile
, OUString
const & url
, int layer
,
648 Partial
const * partial
, Modifications
* modifications
,
649 Additions
* additions
)
651 assert(parseFile
!= nullptr);
653 (*parseFile
)(url
, layer
, data_
, partial
, modifications
, additions
);
654 } catch (css::container::NoSuchElementException
&) {
656 } catch (css::uno::Exception
& e
) { //TODO: more specific exception catching
657 // Ignore invalid XML files, instead of completely preventing OOo from
661 "error reading \"" << url
<< "\": " << e
);
665 void Components::parseFiles(
666 int layer
, OUString
const & extension
, FileParser
* parseFile
,
667 OUString
const & url
, bool recursive
)
669 osl::Directory
dir(url
);
670 switch (dir
.open()) {
671 case osl::FileBase::E_None
:
673 case osl::FileBase::E_NOENT
:
679 throw css::uno::RuntimeException(
680 "cannot open directory " + url
);
683 osl::DirectoryItem i
;
684 osl::FileBase::RC rc
= dir
.getNextItem(i
, SAL_MAX_UINT32
);
685 if (rc
== osl::FileBase::E_NOENT
) {
688 if (rc
!= osl::FileBase::E_None
) {
689 throw css::uno::RuntimeException(
690 "cannot iterate directory " + url
);
692 osl::FileStatus
stat(
693 osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_FileName
|
694 osl_FileStatus_Mask_FileURL
);
695 if (i
.getFileStatus(stat
) != osl::FileBase::E_None
) {
696 throw css::uno::RuntimeException(
697 "cannot stat in directory " + url
);
699 if (stat
.getFileType() == osl::FileStatus::Directory
) { //TODO: symlinks
700 parseFiles(layer
, extension
, parseFile
, stat
.getFileURL(), true);
702 OUString
file(stat
.getFileName());
703 if (file
.endsWith(extension
)) {
706 parseFile
, stat
.getFileURL(), layer
, nullptr, nullptr, nullptr);
707 } catch (css::container::NoSuchElementException
& e
) {
708 if (stat
.getFileType() == osl::FileStatus::Link
) {
709 SAL_WARN("configmgr", "dangling link <" << stat
.getFileURL() << ">");
712 throw css::uno::RuntimeException(
713 "stat'ed file does not exist: " + e
.Message
);
720 void Components::parseFileList(
721 int layer
, FileParser
* parseFile
, OUString
const & urls
,
722 bool recordAdditions
)
724 for (sal_Int32 i
= 0;;) {
725 OUString
url(urls
.getToken(0, ' ', i
));
726 if (!url
.isEmpty()) {
727 Additions
* adds
= nullptr;
728 if (recordAdditions
) {
729 adds
= data_
.addExtensionXcuAdditions(url
, layer
);
732 parseFileLeniently(parseFile
, url
, layer
, nullptr, nullptr, adds
);
733 } catch (css::container::NoSuchElementException
& e
) {
734 SAL_WARN("configmgr", "file does not exist: " << e
);
735 if (adds
!= nullptr) {
736 data_
.removeExtensionXcuAdditions(url
);
746 void Components::parseXcdFiles(int layer
, OUString
const & url
) {
747 osl::Directory
dir(url
);
748 switch (dir
.open()) {
749 case osl::FileBase::E_None
:
751 case osl::FileBase::E_NOENT
:
754 throw css::uno::RuntimeException(
755 "cannot open directory " + url
);
757 UnresolvedVector unres
;
758 std::set
< OUString
> existingDeps
;
759 std::set
< OUString
> processedDeps
;
761 osl::DirectoryItem i
;
762 osl::FileBase::RC rc
= dir
.getNextItem(i
, SAL_MAX_UINT32
);
763 if (rc
== osl::FileBase::E_NOENT
) {
766 if (rc
!= osl::FileBase::E_None
) {
767 throw css::uno::RuntimeException(
768 "cannot iterate directory " + url
);
770 osl::FileStatus
stat(
771 osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_FileName
|
772 osl_FileStatus_Mask_FileURL
);
773 if (i
.getFileStatus(stat
) != osl::FileBase::E_None
) {
774 throw css::uno::RuntimeException(
775 "cannot stat in directory " + url
);
777 if (stat
.getFileType() != osl::FileStatus::Directory
) { //TODO: symlinks
778 OUString
file(stat
.getFileName());
780 if (file
.endsWith(".xcd", &name
)) {
781 existingDeps
.insert(name
);
782 rtl::Reference
< ParseManager
> manager
;
784 manager
= new ParseManager(
786 new XcdParser(layer
, processedDeps
, data_
));
787 } catch (css::container::NoSuchElementException
& e
) {
788 if (stat
.getFileType() == osl::FileStatus::Link
) {
789 SAL_WARN("configmgr", "dangling link <" << stat
.getFileURL() << ">");
792 throw css::uno::RuntimeException(
793 "stat'ed file does not exist: " + e
.Message
);
795 if (manager
->parse(nullptr)) {
796 processedDeps
.insert(name
);
798 unres
.emplace_back(name
, manager
);
803 while (!unres
.empty()) {
804 bool isResolved
= false;
805 for (UnresolvedVector::iterator
i(unres
.begin()); i
!= unres
.end();) {
806 if (i
->manager
->parse(&existingDeps
)) {
807 processedDeps
.insert(i
->name
);
815 throw css::uno::RuntimeException(
816 "xcd: unresolved dependencies in " + url
);
821 void Components::parseXcsXcuLayer(int layer
, OUString
const & url
) {
822 parseXcdFiles(layer
, url
);
823 parseFiles(layer
, ".xcs", &parseXcsFile
, url
+ "/schema", false);
824 parseFiles(layer
+ 1, ".xcu", &parseXcuFile
, url
+ "/data", false);
827 void Components::parseXcsXcuIniLayer(
828 int layer
, OUString
const & url
, bool recordAdditions
)
830 // Check if ini file exists (otherwise .override would still read global
831 // SCHEMA/DATA variables, which could interfere with unrelated environment
833 if (rtl::Bootstrap(url
).getHandle() != nullptr) {
834 OUStringBuffer
prefix("${.override:");
835 for (sal_Int32 i
= 0; i
!= url
.getLength(); ++i
) {
836 sal_Unicode c
= url
[i
];
848 OUString
urls(prefix
.toString() + "SCHEMA}");
849 rtl::Bootstrap::expandMacros(urls
);
850 if (!urls
.isEmpty()) {
851 parseFileList(layer
, &parseXcsFile
, urls
, false);
853 urls
= prefix
.makeStringAndClear() + "DATA}";
854 rtl::Bootstrap::expandMacros(urls
);
855 if (!urls
.isEmpty()) {
856 parseFileList(layer
+ 1, &parseXcuFile
, urls
, recordAdditions
);
861 void Components::parseModuleLayer(int layer
, OUString
const & url
) {
862 parseFiles(layer
, ".xcu", &parseXcuFile
, url
, false);
865 void Components::parseResLayer(int layer
, OUString
const & url
) {
866 OUString
resUrl(url
+ "/res");
867 parseXcdFiles(layer
, resUrl
);
868 parseFiles(layer
, ".xcu", &parseXcuFile
, resUrl
, false);
871 void Components::parseModificationLayer(int layer
, OUString
const & url
) {
873 parseFileLeniently(&parseXcuFile
, url
, layer
, nullptr, nullptr, nullptr);
874 } catch (css::container::NoSuchElementException
&) {
876 "configmgr", "user registrymodifications.xcu does not (yet) exist");
877 // Migrate old user layer data (can be removed once migration is no
878 // longer relevant, probably OOo 4; also see hack for xsi namespace in
879 // xmlreader::XmlReader::registerNamespaceIri):
881 layer
, ".xcu", &parseXcuFile
,
883 "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("bootstrap")
884 ":UserInstallation}/user/registry/data"),
889 int Components::getExtensionLayer(bool shared
) const {
890 int layer
= shared
? sharedExtensionLayer_
: userExtensionLayer_
;
892 throw css::uno::RuntimeException(
893 "insert extension xcs/xcu file into undefined layer");
900 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */