bump product version to 6.3.0.0.beta1
[LibreOffice.git] / configmgr / source / components.cxx
blob9587fde1e7a5083d30b74c8f01feacaa17c33f2a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <cassert>
24 #include <chrono>
25 #include <cstddef>
26 #include <vector>
27 #include <set>
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/lang/WrappedTargetRuntimeException.hpp>
35 #include <com/sun/star/uno/Any.hxx>
36 #include <com/sun/star/uno/Exception.hpp>
37 #include <com/sun/star/uno/Reference.hxx>
38 #include <com/sun/star/uno/RuntimeException.hpp>
39 #include <com/sun/star/uno/XComponentContext.hpp>
40 #include <com/sun/star/uno/XInterface.hpp>
41 #include <cppuhelper/exc_hlp.hxx>
42 #include <config_dconf.h>
43 #include <config_folders.h>
44 #include <osl/conditn.hxx>
45 #include <osl/file.hxx>
46 #include <osl/mutex.hxx>
47 #include <rtl/bootstrap.hxx>
48 #include <rtl/ref.hxx>
49 #include <rtl/ustrbuf.hxx>
50 #include <rtl/ustring.hxx>
51 #include <sal/log.hxx>
52 #include <sal/types.h>
53 #include <salhelper/thread.hxx>
54 #include <comphelper/backupfilehelper.hxx>
56 #include "additions.hxx"
57 #include "components.hxx"
58 #include "data.hxx"
59 #include "lock.hxx"
60 #include "modifications.hxx"
61 #include "node.hxx"
62 #include "nodemap.hxx"
63 #include "parsemanager.hxx"
64 #include "partial.hxx"
65 #include "rootaccess.hxx"
66 #include "writemodfile.hxx"
67 #include "xcdparser.hxx"
68 #include "xcuparser.hxx"
69 #include "xcsparser.hxx"
71 #if ENABLE_DCONF
72 #include "dconf.hxx"
73 #endif
75 #if defined(_WIN32)
76 #include "winreg.hxx"
77 #endif
79 namespace configmgr {
81 namespace {
83 struct UnresolvedVectorItem {
84 OUString name;
85 rtl::Reference< ParseManager > manager;
87 UnresolvedVectorItem(
88 OUString const & theName,
89 rtl::Reference< ParseManager > const & theManager):
90 name(theName), manager(theManager) {}
93 typedef std::vector< UnresolvedVectorItem > UnresolvedVector;
95 void parseXcsFile(
96 OUString const & url, int layer, Data & data, Partial const * partial,
97 Modifications * modifications, Additions * additions)
99 assert(partial == nullptr && modifications == nullptr && additions == nullptr);
100 (void) partial; (void) modifications; (void) additions;
101 bool ok = rtl::Reference< ParseManager >(
102 new ParseManager(url, new XcsParser(layer, data)))->parse(nullptr);
103 assert(ok);
104 (void) ok; // avoid warnings
107 void parseXcuFile(
108 OUString const & url, int layer, Data & data, Partial const * partial,
109 Modifications * modifications, Additions * additions)
111 bool ok = rtl::Reference< ParseManager >(
112 new ParseManager(
113 url,
114 new XcuParser(layer, data, partial, modifications, additions)))->
115 parse(nullptr);
116 assert(ok);
117 (void) ok; // avoid warnings
120 OUString expand(OUString const & str) {
121 OUString s(str);
122 rtl::Bootstrap::expandMacros(s); //TODO: detect failure
123 return s;
126 bool canRemoveFromLayer(int layer, rtl::Reference< Node > const & node) {
127 assert(node.is());
128 if (node->getLayer() > layer && node->getLayer() < Data::NO_LAYER) {
129 return false;
131 switch (node->kind()) {
132 case Node::KIND_LOCALIZED_PROPERTY:
133 case Node::KIND_GROUP:
134 for (auto const& member : node->getMembers())
136 if (!canRemoveFromLayer(layer, member.second)) {
137 return false;
140 return true;
141 case Node::KIND_SET:
142 return node->getMembers().empty();
143 default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE
144 return true;
150 class Components::WriteThread: public salhelper::Thread {
151 public:
152 WriteThread(
153 rtl::Reference< WriteThread > * reference, Components & components,
154 OUString const & url, Data const & data);
156 void flush() { delay_.set(); }
158 private:
159 virtual ~WriteThread() override {}
161 virtual void execute() override;
163 rtl::Reference< WriteThread > * reference_;
164 Components & components_;
165 OUString url_;
166 Data const & data_;
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),
176 lock_( lock() )
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
184 try {
185 try {
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);
191 } catch (...) {
192 reference_->clear();
193 throw;
195 reference_->clear();
198 Components & Components::getSingleton(
199 css::uno::Reference< css::uno::XComponentContext > const & context)
201 assert(context.is());
202 static Components singleton(context);
203 return singleton;
206 bool Components::allLocales(OUString const & locale) {
207 return locale == "*";
210 rtl::Reference< Node > Components::resolvePathRepresentation(
211 OUString const & pathRepresentation,
212 OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
213 const
215 return data_.resolvePathRepresentation(
216 pathRepresentation, canonicRepresentation, path, finalizedLayer);
219 rtl::Reference< Node > Components::getTemplate(OUString const & fullName) const
221 return data_.getTemplate(Data::NO_LAYER, fullName);
224 void Components::addRootAccess(rtl::Reference< RootAccess > const & access) {
225 roots_.insert(access.get());
228 void Components::removeRootAccess(RootAccess * access) {
229 roots_.erase(access);
232 void Components::initGlobalBroadcaster(
233 Modifications const & modifications,
234 rtl::Reference< RootAccess > const & exclude, Broadcaster * broadcaster)
236 //TODO: Iterate only over roots w/ listeners:
237 for (auto const& elemRoot : roots_)
239 rtl::Reference< RootAccess > root;
240 if (elemRoot->acquireCounting() > 1) {
241 root.set(elemRoot); // must not throw
243 elemRoot->releaseNondeleting();
244 if (root.is()) {
245 if (root != exclude) {
246 std::vector<OUString> path(root->getAbsolutePath());
247 Modifications::Node const * mods = &modifications.getRoot();
248 for (auto const& pathElem : path)
250 Modifications::Node::Children::const_iterator k(
251 mods->children.find(pathElem));
252 if (k == mods->children.end()) {
253 mods = nullptr;
254 break;
256 mods = &k->second;
258 //TODO: If the complete tree of which root is a part is deleted,
259 // or replaced, mods will be null, but some of the listeners
260 // from within root should probably fire nonetheless:
261 if (mods != nullptr) {
262 root->initBroadcaster(*mods, broadcaster);
269 void Components::addModification(std::vector<OUString> const & path) {
270 data_.modifications.add(path);
273 void Components::writeModifications() {
275 if (!data_.modifications.empty()) {
276 switch (modificationTarget_) {
277 case ModificationTarget::None:
278 break;
279 case ModificationTarget::File:
280 if (!writeThread_.is()) {
281 writeThread_ = new WriteThread(
282 &writeThread_, *this, modificationFileUrl_, data_);
283 writeThread_->launch();
285 break;
286 case ModificationTarget::Dconf:
287 #if ENABLE_DCONF
288 dconf::writeModifications(*this, data_);
289 #endif
290 break;
295 void Components::flushModifications() {
296 rtl::Reference< WriteThread > thread;
298 osl::MutexGuard g(*lock_);
299 thread = writeThread_;
301 if (thread.is()) {
302 thread->flush();
303 thread->join();
307 void Components::insertExtensionXcsFile(
308 bool shared, OUString const & fileUri)
310 int layer = getExtensionLayer(shared);
311 try {
312 parseXcsFile(fileUri, layer, data_, nullptr, nullptr, nullptr);
313 } catch (css::container::NoSuchElementException & e) {
314 throw css::uno::RuntimeException(
315 "insertExtensionXcsFile does not exist: " + e.Message);
319 void Components::insertExtensionXcuFile(
320 bool shared, OUString const & fileUri, Modifications * modifications)
322 assert(modifications != nullptr);
323 int layer = getExtensionLayer(shared) + 1;
324 Additions * adds = data_.addExtensionXcuAdditions(fileUri, layer);
325 try {
326 parseXcuFile(fileUri, layer, data_, nullptr, modifications, adds);
327 } catch (css::container::NoSuchElementException & e) {
328 data_.removeExtensionXcuAdditions(fileUri);
329 throw css::uno::RuntimeException(
330 "insertExtensionXcuFile does not exist: " + e.Message);
334 void Components::removeExtensionXcuFile(
335 OUString const & fileUri, Modifications * modifications)
337 //TODO: Ideally, exactly the data coming from the specified xcu file would
338 // be removed. However, not enough information is recorded in the in-memory
339 // data structures to do so. So, as a workaround, all those set elements
340 // that were freshly added by the xcu and have afterwards been left
341 // unchanged or have only had their properties changed in the user layer are
342 // removed (and nothing else). The heuristic to determine
343 // whether a node has been left unchanged is to check the layer ID (as
344 // usual) and additionally to check that the node does not recursively
345 // contain any non-empty sets (multiple extension xcu files are merged into
346 // one layer, so checking layer ID alone is not enough). Since
347 // item->additions records all additions of set members in textual order,
348 // the latter check works well when iterating through item->additions in
349 // reverse order.
350 assert(modifications != nullptr);
351 rtl::Reference< Data::ExtensionXcu > item(
352 data_.removeExtensionXcuAdditions(fileUri));
353 if (item.is()) {
354 for (Additions::reverse_iterator i(item->additions.rbegin());
355 i != item->additions.rend(); ++i)
357 rtl::Reference< Node > parent;
358 NodeMap const * map = &data_.getComponents();
359 rtl::Reference< Node > node;
360 for (auto const& j : *i)
362 parent = node;
363 node = map->findNode(Data::NO_LAYER, j);
364 if (!node.is()) {
365 break;
367 map = &node->getMembers();
369 if (node.is()) {
370 assert(parent.is());
371 if (parent->kind() == Node::KIND_SET) {
372 assert(
373 node->kind() == Node::KIND_GROUP ||
374 node->kind() == Node::KIND_SET);
375 if (canRemoveFromLayer(item->layer, node)) {
376 parent->getMembers().erase(i->back());
377 data_.modifications.remove(*i);
378 modifications->add(*i);
383 writeModifications();
387 void Components::insertModificationXcuFile(
388 OUString const & fileUri,
389 std::set< OUString > const & includedPaths,
390 std::set< OUString > const & excludedPaths,
391 Modifications * modifications)
393 assert(modifications != nullptr);
394 Partial part(includedPaths, excludedPaths);
395 try {
396 parseFileLeniently(
397 &parseXcuFile, fileUri, Data::NO_LAYER, &part, modifications, nullptr);
398 } catch (css::container::NoSuchElementException & e) {
399 SAL_WARN(
400 "configmgr",
401 "error inserting non-existing \"" << fileUri << "\": " << e);
405 css::beans::Optional< css::uno::Any > Components::getExternalValue(
406 OUString const & descriptor)
408 sal_Int32 i = descriptor.indexOf(' ');
409 if (i <= 0) {
410 throw css::uno::RuntimeException(
411 "bad external value descriptor " + descriptor);
413 //TODO: Do not make calls with mutex locked:
414 OUString name(descriptor.copy(0, i));
415 ExternalServices::iterator j(externalServices_.find(name));
416 if (j == externalServices_.end()) {
417 css::uno::Reference< css::uno::XInterface > service;
418 try {
419 service = context_->getServiceManager()->createInstanceWithContext(
420 name, context_);
421 } catch (css::uno::RuntimeException &) {
422 // Assuming these exceptions are real errors:
423 throw;
424 } catch (css::uno::Exception & e) {
425 // Assuming these exceptions indicate that the service is not
426 // installed:
427 SAL_WARN(
428 "configmgr",
429 "createInstance(" << name << ") failed with " << e);
431 css::uno::Reference< css::beans::XPropertySet > propset;
432 if (service.is()) {
433 propset.set( service, css::uno::UNO_QUERY_THROW);
435 j = externalServices_.emplace(name, propset).first;
437 css::beans::Optional< css::uno::Any > value;
438 if (j->second.is()) {
439 try {
440 if (!(j->second->getPropertyValue(descriptor.copy(i + 1)) >>=
441 value))
443 throw css::uno::RuntimeException(
444 "cannot obtain external value through " + descriptor);
446 } catch (css::beans::UnknownPropertyException & e) {
447 throw css::uno::RuntimeException(
448 "unknown external value descriptor ID: " + e.Message);
449 } catch (css::lang::WrappedTargetException & e) {
450 css::uno::Any anyEx = cppu::getCaughtException();
451 throw css::lang::WrappedTargetRuntimeException(
452 "cannot obtain external value: " + e.Message,
453 nullptr, anyEx );
456 return value;
459 Components::Components(
460 css::uno::Reference< css::uno::XComponentContext > const & context):
461 context_(context), sharedExtensionLayer_(-1), userExtensionLayer_(-1),
462 modificationTarget_(ModificationTarget::None)
464 assert(context.is());
465 lock_ = lock();
466 OUString conf(expand("${CONFIGURATION_LAYERS}"));
467 int layer = 0;
468 for (sal_Int32 i = 0;;) {
469 while (i != conf.getLength() && conf[i] == ' ') {
470 ++i;
472 if (i == conf.getLength()) {
473 break;
475 if (modificationTarget_ != ModificationTarget::None) {
476 throw css::uno::RuntimeException(
477 "CONFIGURATION_LAYERS: modification target layer followed by"
478 " further layers");
480 sal_Int32 c = i;
481 for (;; ++c) {
482 if (c == conf.getLength() || conf[c] == ' ') {
483 throw css::uno::RuntimeException(
484 "CONFIGURATION_LAYERS: missing ':' in \"" + conf + "\"");
486 if (conf[c] == ':') {
487 break;
490 sal_Int32 n = conf.indexOf(' ', c + 1);
491 if (n == -1) {
492 n = conf.getLength();
494 OUString type(conf.copy(i, c - i));
495 OUString url(conf.copy(c + 1, n - c - 1));
496 if (type == "xcsxcu") {
497 sal_uInt32 nStartTime = osl_getGlobalTimer();
498 parseXcsXcuLayer(layer, url);
499 SAL_INFO("configmgr", "parseXcsXcuLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
500 layer += 2; //TODO: overflow
501 } else if (type == "bundledext") {
502 parseXcsXcuIniLayer(layer, url, false);
503 layer += 2; //TODO: overflow
504 } else if (type == "sharedext") {
505 if (sharedExtensionLayer_ != -1) {
506 throw css::uno::RuntimeException(
507 "CONFIGURATION_LAYERS: multiple \"sharedext\" layers");
509 sharedExtensionLayer_ = layer;
510 parseXcsXcuIniLayer(layer, url, true);
511 layer += 2; //TODO: overflow
512 } else if (type == "userext") {
513 if (userExtensionLayer_ != -1) {
514 throw css::uno::RuntimeException(
515 "CONFIGURATION_LAYERS: multiple \"userext\" layers");
517 userExtensionLayer_ = layer;
518 parseXcsXcuIniLayer(layer, url, true);
519 layer += 2; //TODO: overflow
520 } else if (type == "module") {
521 parseModuleLayer(layer, url);
522 ++layer; //TODO: overflow
523 } else if (type == "res") {
524 sal_uInt32 nStartTime = osl_getGlobalTimer();
525 parseResLayer(layer, url);
526 SAL_INFO("configmgr", "parseResLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
527 ++layer; //TODO: overflow
528 #if ENABLE_DCONF
529 } else if (type == "dconf") {
530 if (url == "!") {
531 modificationTarget_ = ModificationTarget::Dconf;
532 dconf::readLayer(data_, Data::NO_LAYER);
533 } else if (url == "*") {
534 dconf::readLayer(data_, layer);
535 } else {
536 throw css::uno::RuntimeException(
537 "CONFIGURATION_LAYERS: unknown \"dconf\" kind \"" + url
538 + "\"");
540 ++layer; //TODO: overflow
541 #endif
542 #if defined(_WIN32)
543 } else if (type == "winreg") {
544 WinRegType eType;
545 if (url == "LOCAL_MACHINE" || url.isEmpty()/*backwards comp.*/) {
546 eType = WinRegType::LOCAL_MACHINE;
547 } else if (url == "CURRENT_USER") {
548 eType = WinRegType::CURRENT_USER;
549 } else {
550 throw css::uno::RuntimeException(
551 "CONFIGURATION_LAYERS: unknown \"winreg\" kind \"" + url
552 + "\"");
554 OUString aTempFileURL;
555 if (dumpWindowsRegistry(&aTempFileURL, eType)) {
556 parseFileLeniently(&parseXcuFile, aTempFileURL, layer, nullptr, nullptr, nullptr);
557 if (!getenv("SAL_CONFIG_WINREG_RETAIN_TMP"))
558 osl::File::remove(aTempFileURL);
560 ++layer; //TODO: overflow
561 #endif
562 } else if (type == "user") {
563 bool write;
564 if (url.startsWith("!", &url)) {
565 write = true;
566 } else if (url.startsWith("*", &url)) {
567 write = false;
568 } else {
569 write = true; // for backwards compatibility
571 if (url.isEmpty()) {
572 throw css::uno::RuntimeException(
573 "CONFIGURATION_LAYERS: empty \"user\" URL");
575 bool ignore = false;
576 #if ENABLE_DCONF
577 if (write) {
578 OUString token(
579 expand("${SYSUSERCONFIG}/libreoffice/dconfwrite"));
580 osl::DirectoryItem it;
581 osl::FileBase::RC e = osl::DirectoryItem::get(token, it);
582 ignore = e == osl::FileBase::E_None;
583 SAL_INFO(
584 "configmgr",
585 "dconf write (<" << token << "> " << +e << "): "
586 << int(ignore));
587 if (ignore) {
588 modificationTarget_ = ModificationTarget::Dconf;
591 #endif
592 if (!ignore) {
593 if (write) {
594 modificationTarget_ = ModificationTarget::File;
595 modificationFileUrl_ = url;
597 parseModificationLayer(write ? Data::NO_LAYER : layer, url);
599 ++layer; //TODO: overflow
600 } else {
601 throw css::uno::RuntimeException(
602 "CONFIGURATION_LAYERS: unknown layer type \"" + type + "\"");
604 i = n;
608 Components::~Components()
610 // get flag if _exit was already called which is a sign to not secure user config.
611 // this is used for win only currently where calling _exit() unfortunately still
612 // calls destructors (what is not wanted). May be needed for other systems, too
613 // (unknown yet) but can do no harm
614 const bool bExitWasCalled(comphelper::BackupFileHelper::getExitWasCalled());
616 #ifndef WNT
617 // we can add a SAL_WARN here for other systems where the destructor gets called after
618 // an _exit() call. Still safe - the getExitWasCalled() is used, but a hint that _exit
619 // behaves different on a system
620 SAL_WARN_IF(bExitWasCalled, "configmgr", "Components::~Components() called after _exit() call");
621 #endif
623 if (bExitWasCalled)
625 // do not write, re-join threads
626 osl::MutexGuard g(*lock_);
628 if (writeThread_.is())
630 writeThread_->join();
633 else
635 // write changes
636 flushModifications();
639 for (auto const& rootElem : roots_)
641 rootElem->setAlive(false);
645 void Components::parseFileLeniently(
646 FileParser * parseFile, OUString const & url, int layer,
647 Partial const * partial, Modifications * modifications,
648 Additions * additions)
650 assert(parseFile != nullptr);
651 try {
652 (*parseFile)(url, layer, data_, partial, modifications, additions);
653 } catch (css::container::NoSuchElementException &) {
654 throw;
655 } catch (css::uno::Exception & e) { //TODO: more specific exception catching
656 // Ignore invalid XML files, instead of completely preventing OOo from
657 // starting:
658 SAL_WARN(
659 "configmgr",
660 "error reading \"" << url << "\": " << e);
664 void Components::parseFiles(
665 int layer, OUString const & extension, FileParser * parseFile,
666 OUString const & url, bool recursive)
668 osl::Directory dir(url);
669 switch (dir.open()) {
670 case osl::FileBase::E_None:
671 break;
672 case osl::FileBase::E_NOENT:
673 if (!recursive) {
674 return;
676 [[fallthrough]];
677 default:
678 throw css::uno::RuntimeException(
679 "cannot open directory " + url);
681 for (;;) {
682 osl::DirectoryItem i;
683 osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
684 if (rc == osl::FileBase::E_NOENT) {
685 break;
687 if (rc != osl::FileBase::E_None) {
688 throw css::uno::RuntimeException(
689 "cannot iterate directory " + url);
691 osl::FileStatus stat(
692 osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
693 osl_FileStatus_Mask_FileURL);
694 if (i.getFileStatus(stat) != osl::FileBase::E_None) {
695 throw css::uno::RuntimeException(
696 "cannot stat in directory " + url);
698 if (stat.getFileType() == osl::FileStatus::Directory) { //TODO: symlinks
699 parseFiles(layer, extension, parseFile, stat.getFileURL(), true);
700 } else {
701 OUString file(stat.getFileName());
702 if (file.endsWith(extension)) {
703 try {
704 parseFileLeniently(
705 parseFile, stat.getFileURL(), layer, nullptr, nullptr, nullptr);
706 } catch (css::container::NoSuchElementException & e) {
707 if (stat.getFileType() == osl::FileStatus::Link) {
708 SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
709 continue;
711 throw css::uno::RuntimeException(
712 "stat'ed file does not exist: " + e.Message);
719 void Components::parseFileList(
720 int layer, FileParser * parseFile, OUString const & urls,
721 bool recordAdditions)
723 for (sal_Int32 i = 0;;) {
724 OUString url(urls.getToken(0, ' ', i));
725 if (!url.isEmpty()) {
726 Additions * adds = nullptr;
727 if (recordAdditions) {
728 adds = data_.addExtensionXcuAdditions(url, layer);
730 try {
731 parseFileLeniently(parseFile, url, layer, nullptr, nullptr, adds);
732 } catch (css::container::NoSuchElementException & e) {
733 SAL_WARN("configmgr", "file does not exist: " << e);
734 if (adds != nullptr) {
735 data_.removeExtensionXcuAdditions(url);
739 if (i == -1) {
740 break;
745 void Components::parseXcdFiles(int layer, OUString const & url) {
746 osl::Directory dir(url);
747 switch (dir.open()) {
748 case osl::FileBase::E_None:
749 break;
750 case osl::FileBase::E_NOENT:
751 return;
752 default:
753 throw css::uno::RuntimeException(
754 "cannot open directory " + url);
756 UnresolvedVector unres;
757 std::set< OUString > existingDeps;
758 std::set< OUString > processedDeps;
759 for (;;) {
760 osl::DirectoryItem i;
761 osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
762 if (rc == osl::FileBase::E_NOENT) {
763 break;
765 if (rc != osl::FileBase::E_None) {
766 throw css::uno::RuntimeException(
767 "cannot iterate directory " + url);
769 osl::FileStatus stat(
770 osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
771 osl_FileStatus_Mask_FileURL);
772 if (i.getFileStatus(stat) != osl::FileBase::E_None) {
773 throw css::uno::RuntimeException(
774 "cannot stat in directory " + url);
776 if (stat.getFileType() != osl::FileStatus::Directory) { //TODO: symlinks
777 OUString file(stat.getFileName());
778 OUString name;
779 if (file.endsWith(".xcd", &name)) {
780 existingDeps.insert(name);
781 rtl::Reference< ParseManager > manager;
782 try {
783 manager = new ParseManager(
784 stat.getFileURL(),
785 new XcdParser(layer, processedDeps, data_));
786 } catch (css::container::NoSuchElementException & e) {
787 if (stat.getFileType() == osl::FileStatus::Link) {
788 SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
789 continue;
791 throw css::uno::RuntimeException(
792 "stat'ed file does not exist: " + e.Message);
794 if (manager->parse(nullptr)) {
795 processedDeps.insert(name);
796 } else {
797 unres.emplace_back(name, manager);
802 while (!unres.empty()) {
803 bool isResolved = false;
804 for (UnresolvedVector::iterator i(unres.begin()); i != unres.end();) {
805 if (i->manager->parse(&existingDeps)) {
806 processedDeps.insert(i->name);
807 i = unres.erase(i);
808 isResolved = true;
809 } else {
810 ++i;
813 if (!isResolved) {
814 throw css::uno::RuntimeException(
815 "xcd: unresolved dependencies in " + url);
820 void Components::parseXcsXcuLayer(int layer, OUString const & url) {
821 parseXcdFiles(layer, url);
822 parseFiles(layer, ".xcs", &parseXcsFile, url + "/schema", false);
823 parseFiles(layer + 1, ".xcu", &parseXcuFile, url + "/data", false);
826 void Components::parseXcsXcuIniLayer(
827 int layer, OUString const & url, bool recordAdditions)
829 // Check if ini file exists (otherwise .override would still read global
830 // SCHEMA/DATA variables, which could interfere with unrelated environment
831 // variables):
832 if (rtl::Bootstrap(url).getHandle() != nullptr) {
833 OUStringBuffer prefix("${.override:");
834 for (sal_Int32 i = 0; i != url.getLength(); ++i) {
835 sal_Unicode c = url[i];
836 switch (c) {
837 case '$':
838 case ':':
839 case '\\':
840 prefix.append('\\');
841 [[fallthrough]];
842 default:
843 prefix.append(c);
846 prefix.append(':');
847 OUString urls(prefix.toString() + "SCHEMA}");
848 rtl::Bootstrap::expandMacros(urls);
849 if (!urls.isEmpty()) {
850 parseFileList(layer, &parseXcsFile, urls, false);
852 urls = prefix.makeStringAndClear() + "DATA}";
853 rtl::Bootstrap::expandMacros(urls);
854 if (!urls.isEmpty()) {
855 parseFileList(layer + 1, &parseXcuFile, urls, recordAdditions);
860 void Components::parseModuleLayer(int layer, OUString const & url) {
861 parseFiles(layer, ".xcu", &parseXcuFile, url, false);
864 void Components::parseResLayer(int layer, OUString const & url) {
865 OUString resUrl(url + "/res");
866 parseXcdFiles(layer, resUrl);
867 parseFiles(layer, ".xcu", &parseXcuFile, resUrl, false);
870 void Components::parseModificationLayer(int layer, OUString const & url) {
871 try {
872 parseFileLeniently(&parseXcuFile, url, layer, nullptr, nullptr, nullptr);
873 } catch (css::container::NoSuchElementException &) {
874 SAL_INFO(
875 "configmgr", "user registrymodifications.xcu does not (yet) exist");
876 // Migrate old user layer data (can be removed once migration is no
877 // longer relevant, probably OOo 4; also see hack for xsi namespace in
878 // xmlreader::XmlReader::registerNamespaceIri):
879 parseFiles(
880 layer, ".xcu", &parseXcuFile,
881 expand(
882 "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
883 ":UserInstallation}/user/registry/data"),
884 false);
888 int Components::getExtensionLayer(bool shared) const {
889 int layer = shared ? sharedExtensionLayer_ : userExtensionLayer_;
890 if (layer == -1) {
891 throw css::uno::RuntimeException(
892 "insert extension xcs/xcu file into undefined layer");
894 return layer;
899 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */