lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / configmgr / source / components.cxx
blobaf666ba2b71983efa682724526b0ec2c92870005
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 <rtl/instance.hxx>
52 #include <sal/log.hxx>
53 #include <sal/types.h>
54 #include <salhelper/thread.hxx>
55 #include <comphelper/backupfilehelper.hxx>
57 #include "additions.hxx"
58 #include "components.hxx"
59 #include "data.hxx"
60 #include "lock.hxx"
61 #include "modifications.hxx"
62 #include "node.hxx"
63 #include "nodemap.hxx"
64 #include "parsemanager.hxx"
65 #include "partial.hxx"
66 #include "rootaccess.hxx"
67 #include "writemodfile.hxx"
68 #include "xcdparser.hxx"
69 #include "xcuparser.hxx"
70 #include "xcsparser.hxx"
72 #if ENABLE_DCONF
73 #include "dconf.hxx"
74 #endif
76 #if defined(_WIN32)
77 #include "winreg.hxx"
78 #endif
80 namespace configmgr {
82 namespace {
84 struct UnresolvedVectorItem {
85 OUString name;
86 rtl::Reference< ParseManager > manager;
88 UnresolvedVectorItem(
89 OUString const & theName,
90 rtl::Reference< ParseManager > const & theManager):
91 name(theName), manager(theManager) {}
94 typedef std::vector< UnresolvedVectorItem > UnresolvedVector;
96 void parseXcsFile(
97 OUString const & url, int layer, Data & data, Partial const * partial,
98 Modifications * modifications, Additions * additions)
100 assert(partial == nullptr && modifications == nullptr && additions == nullptr);
101 (void) partial; (void) modifications; (void) additions;
102 bool ok = rtl::Reference< ParseManager >(
103 new ParseManager(url, new XcsParser(layer, data)))->parse(nullptr);
104 assert(ok);
105 (void) ok; // avoid warnings
108 void parseXcuFile(
109 OUString const & url, int layer, Data & data, Partial const * partial,
110 Modifications * modifications, Additions * additions)
112 bool ok = rtl::Reference< ParseManager >(
113 new ParseManager(
114 url,
115 new XcuParser(layer, data, partial, modifications, additions)))->
116 parse(nullptr);
117 assert(ok);
118 (void) ok; // avoid warnings
121 OUString expand(OUString const & str) {
122 OUString s(str);
123 rtl::Bootstrap::expandMacros(s); //TODO: detect failure
124 return s;
127 bool canRemoveFromLayer(int layer, rtl::Reference< Node > const & node) {
128 assert(node.is());
129 if (node->getLayer() > layer && node->getLayer() < Data::NO_LAYER) {
130 return false;
132 switch (node->kind()) {
133 case Node::KIND_LOCALIZED_PROPERTY:
134 case Node::KIND_GROUP:
135 for (auto const& member : node->getMembers())
137 if (!canRemoveFromLayer(layer, member.second)) {
138 return false;
141 return true;
142 case Node::KIND_SET:
143 return node->getMembers().empty();
144 default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE
145 return true;
151 class Components::WriteThread: public salhelper::Thread {
152 public:
153 WriteThread(
154 rtl::Reference< WriteThread > * reference, Components & components,
155 OUString const & url, Data const & data);
157 void flush() { delay_.set(); }
159 private:
160 virtual ~WriteThread() override {}
162 virtual void execute() override;
164 rtl::Reference< WriteThread > * reference_;
165 Components & components_;
166 OUString url_;
167 Data const & data_;
168 osl::Condition delay_;
169 std::shared_ptr<osl::Mutex> lock_;
172 Components::WriteThread::WriteThread(
173 rtl::Reference< WriteThread > * reference, Components & components,
174 OUString const & url, Data const & data):
175 Thread("configmgrWriter"), reference_(reference), components_(components),
176 url_(url), data_(data),
177 lock_( lock() )
179 assert(reference != nullptr);
182 void Components::WriteThread::execute() {
183 delay_.wait(std::chrono::seconds(1)); // must not throw; result_error is harmless and ignored
184 osl::MutexGuard g(*lock_); // must not throw
185 try {
186 try {
187 writeModFile(components_, url_, data_);
188 } catch (css::uno::RuntimeException & e) {
189 // Ignore write errors, instead of aborting:
190 SAL_WARN("configmgr", "error writing modifications: " << e);
192 } catch (...) {
193 reference_->clear();
194 throw;
196 reference_->clear();
199 class theComponentsSingleton :
200 public rtl::StaticWithArg<
201 Components,
202 css::uno::Reference< css::uno::XComponentContext >,
203 theComponentsSingleton>
207 Components & Components::getSingleton(
208 css::uno::Reference< css::uno::XComponentContext > const & context)
210 assert(context.is());
211 return theComponentsSingleton::get(context);
214 bool Components::allLocales(OUString const & locale) {
215 return locale == "*";
218 rtl::Reference< Node > Components::resolvePathRepresentation(
219 OUString const & pathRepresentation,
220 OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
221 const
223 return data_.resolvePathRepresentation(
224 pathRepresentation, canonicRepresentation, path, finalizedLayer);
227 rtl::Reference< Node > Components::getTemplate(OUString const & fullName) const
229 return data_.getTemplate(Data::NO_LAYER, fullName);
232 void Components::addRootAccess(rtl::Reference< RootAccess > const & access) {
233 roots_.insert(access.get());
236 void Components::removeRootAccess(RootAccess * access) {
237 roots_.erase(access);
240 void Components::initGlobalBroadcaster(
241 Modifications const & modifications,
242 rtl::Reference< RootAccess > const & exclude, Broadcaster * broadcaster)
244 //TODO: Iterate only over roots w/ listeners:
245 for (auto const& elemRoot : roots_)
247 rtl::Reference< RootAccess > root;
248 if (elemRoot->acquireCounting() > 1) {
249 root.set(elemRoot); // must not throw
251 elemRoot->releaseNondeleting();
252 if (root.is()) {
253 if (root != exclude) {
254 std::vector<OUString> path(root->getAbsolutePath());
255 Modifications::Node const * mods = &modifications.getRoot();
256 for (auto const& pathElem : path)
258 Modifications::Node::Children::const_iterator k(
259 mods->children.find(pathElem));
260 if (k == mods->children.end()) {
261 mods = nullptr;
262 break;
264 mods = &k->second;
266 //TODO: If the complete tree of which root is a part is deleted,
267 // or replaced, mods will be null, but some of the listeners
268 // from within root should probably fire nonetheless:
269 if (mods != nullptr) {
270 root->initBroadcaster(*mods, broadcaster);
277 void Components::addModification(std::vector<OUString> const & path) {
278 data_.modifications.add(path);
281 void Components::writeModifications() {
283 if (!data_.modifications.empty()) {
284 switch (modificationTarget_) {
285 case ModificationTarget::None:
286 break;
287 case ModificationTarget::File:
288 if (!writeThread_.is()) {
289 writeThread_ = new WriteThread(
290 &writeThread_, *this, modificationFileUrl_, data_);
291 writeThread_->launch();
293 break;
294 case ModificationTarget::Dconf:
295 #if ENABLE_DCONF
296 dconf::writeModifications(*this, data_);
297 #endif
298 break;
303 void Components::flushModifications() {
304 rtl::Reference< WriteThread > thread;
306 osl::MutexGuard g(*lock_);
307 thread = writeThread_;
309 if (thread.is()) {
310 thread->flush();
311 thread->join();
315 void Components::insertExtensionXcsFile(
316 bool shared, OUString const & fileUri)
318 int layer = getExtensionLayer(shared);
319 try {
320 parseXcsFile(fileUri, layer, data_, nullptr, nullptr, nullptr);
321 } catch (css::container::NoSuchElementException & e) {
322 throw css::uno::RuntimeException(
323 "insertExtensionXcsFile does not exist: " + e.Message);
327 void Components::insertExtensionXcuFile(
328 bool shared, OUString const & fileUri, Modifications * modifications)
330 assert(modifications != nullptr);
331 int layer = getExtensionLayer(shared) + 1;
332 Additions * adds = data_.addExtensionXcuAdditions(fileUri, layer);
333 try {
334 parseXcuFile(fileUri, layer, data_, nullptr, modifications, adds);
335 } catch (css::container::NoSuchElementException & e) {
336 data_.removeExtensionXcuAdditions(fileUri);
337 throw css::uno::RuntimeException(
338 "insertExtensionXcuFile does not exist: " + e.Message);
342 void Components::removeExtensionXcuFile(
343 OUString const & fileUri, Modifications * modifications)
345 //TODO: Ideally, exactly the data coming from the specified xcu file would
346 // be removed. However, not enough information is recorded in the in-memory
347 // data structures to do so. So, as a workaround, all those set elements
348 // that were freshly added by the xcu and have afterwards been left
349 // unchanged or have only had their properties changed in the user layer are
350 // removed (and nothing else). The heuristic to determine
351 // whether a node has been left unchanged is to check the layer ID (as
352 // usual) and additionally to check that the node does not recursively
353 // contain any non-empty sets (multiple extension xcu files are merged into
354 // one layer, so checking layer ID alone is not enough). Since
355 // item->additions records all additions of set members in textual order,
356 // the latter check works well when iterating through item->additions in
357 // reverse order.
358 assert(modifications != nullptr);
359 rtl::Reference< Data::ExtensionXcu > item(
360 data_.removeExtensionXcuAdditions(fileUri));
361 if (item.is()) {
362 for (Additions::reverse_iterator i(item->additions.rbegin());
363 i != item->additions.rend(); ++i)
365 rtl::Reference< Node > parent;
366 NodeMap const * map = &data_.getComponents();
367 rtl::Reference< Node > node;
368 for (auto const& j : *i)
370 parent = node;
371 node = map->findNode(Data::NO_LAYER, j);
372 if (!node.is()) {
373 break;
375 map = &node->getMembers();
377 if (node.is()) {
378 assert(parent.is());
379 if (parent->kind() == Node::KIND_SET) {
380 assert(
381 node->kind() == Node::KIND_GROUP ||
382 node->kind() == Node::KIND_SET);
383 if (canRemoveFromLayer(item->layer, node)) {
384 parent->getMembers().erase(i->back());
385 data_.modifications.remove(*i);
386 modifications->add(*i);
391 writeModifications();
395 void Components::insertModificationXcuFile(
396 OUString const & fileUri,
397 std::set< OUString > const & includedPaths,
398 std::set< OUString > const & excludedPaths,
399 Modifications * modifications)
401 assert(modifications != nullptr);
402 Partial part(includedPaths, excludedPaths);
403 try {
404 parseFileLeniently(
405 &parseXcuFile, fileUri, Data::NO_LAYER, &part, modifications, nullptr);
406 } catch (css::container::NoSuchElementException & e) {
407 SAL_WARN(
408 "configmgr",
409 "error inserting non-existing \"" << fileUri << "\": " << e);
413 css::beans::Optional< css::uno::Any > Components::getExternalValue(
414 OUString const & descriptor)
416 sal_Int32 i = descriptor.indexOf(' ');
417 if (i <= 0) {
418 throw css::uno::RuntimeException(
419 "bad external value descriptor " + descriptor);
421 //TODO: Do not make calls with mutex locked:
422 OUString name(descriptor.copy(0, i));
423 ExternalServices::iterator j(externalServices_.find(name));
424 if (j == externalServices_.end()) {
425 css::uno::Reference< css::uno::XInterface > service;
426 try {
427 service = context_->getServiceManager()->createInstanceWithContext(
428 name, context_);
429 } catch (css::uno::RuntimeException &) {
430 // Assuming these exceptions are real errors:
431 throw;
432 } catch (css::uno::Exception & e) {
433 // Assuming these exceptions indicate that the service is not
434 // installed:
435 SAL_WARN(
436 "configmgr",
437 "createInstance(" << name << ") failed with " << e);
439 css::uno::Reference< css::beans::XPropertySet > propset;
440 if (service.is()) {
441 propset.set( service, css::uno::UNO_QUERY_THROW);
443 j = externalServices_.emplace(name, propset).first;
445 css::beans::Optional< css::uno::Any > value;
446 if (j->second.is()) {
447 try {
448 if (!(j->second->getPropertyValue(descriptor.copy(i + 1)) >>=
449 value))
451 throw css::uno::RuntimeException(
452 "cannot obtain external value through " + descriptor);
454 } catch (css::beans::UnknownPropertyException & e) {
455 throw css::uno::RuntimeException(
456 "unknown external value descriptor ID: " + e.Message);
457 } catch (css::lang::WrappedTargetException & e) {
458 css::uno::Any anyEx = cppu::getCaughtException();
459 throw css::lang::WrappedTargetRuntimeException(
460 "cannot obtain external value: " + e.Message,
461 nullptr, anyEx );
464 return value;
467 Components::Components(
468 css::uno::Reference< css::uno::XComponentContext > const & context):
469 context_(context), sharedExtensionLayer_(-1), userExtensionLayer_(-1),
470 modificationTarget_(ModificationTarget::None)
472 assert(context.is());
473 lock_ = lock();
474 OUString conf(expand("${CONFIGURATION_LAYERS}"));
475 int layer = 0;
476 for (sal_Int32 i = 0;;) {
477 while (i != conf.getLength() && conf[i] == ' ') {
478 ++i;
480 if (i == conf.getLength()) {
481 break;
483 if (modificationTarget_ != ModificationTarget::None) {
484 throw css::uno::RuntimeException(
485 "CONFIGURATION_LAYERS: modification target layer followed by"
486 " further layers");
488 sal_Int32 c = i;
489 for (;; ++c) {
490 if (c == conf.getLength() || conf[c] == ' ') {
491 throw css::uno::RuntimeException(
492 "CONFIGURATION_LAYERS: missing ':' in \"" + conf + "\"");
494 if (conf[c] == ':') {
495 break;
498 sal_Int32 n = conf.indexOf(' ', c + 1);
499 if (n == -1) {
500 n = conf.getLength();
502 OUString type(conf.copy(i, c - i));
503 OUString url(conf.copy(c + 1, n - c - 1));
504 if (type == "xcsxcu") {
505 sal_uInt32 nStartTime = osl_getGlobalTimer();
506 parseXcsXcuLayer(layer, url);
507 SAL_INFO("configmgr", "parseXcsXcuLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
508 layer += 2; //TODO: overflow
509 } else if (type == "bundledext") {
510 parseXcsXcuIniLayer(layer, url, false);
511 layer += 2; //TODO: overflow
512 } else if (type == "sharedext") {
513 if (sharedExtensionLayer_ != -1) {
514 throw css::uno::RuntimeException(
515 "CONFIGURATION_LAYERS: multiple \"sharedext\" layers");
517 sharedExtensionLayer_ = layer;
518 parseXcsXcuIniLayer(layer, url, true);
519 layer += 2; //TODO: overflow
520 } else if (type == "userext") {
521 if (userExtensionLayer_ != -1) {
522 throw css::uno::RuntimeException(
523 "CONFIGURATION_LAYERS: multiple \"userext\" layers");
525 userExtensionLayer_ = layer;
526 parseXcsXcuIniLayer(layer, url, true);
527 layer += 2; //TODO: overflow
528 } else if (type == "module") {
529 parseModuleLayer(layer, url);
530 ++layer; //TODO: overflow
531 } else if (type == "res") {
532 sal_uInt32 nStartTime = osl_getGlobalTimer();
533 parseResLayer(layer, url);
534 SAL_INFO("configmgr", "parseResLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
535 ++layer; //TODO: overflow
536 #if ENABLE_DCONF
537 } else if (type == "dconf") {
538 if (url == "!") {
539 modificationTarget_ = ModificationTarget::Dconf;
540 dconf::readLayer(data_, Data::NO_LAYER);
541 } else if (url == "*") {
542 dconf::readLayer(data_, layer);
543 } else {
544 throw css::uno::RuntimeException(
545 "CONFIGURATION_LAYERS: unknown \"dconf\" kind \"" + url
546 + "\"");
548 ++layer; //TODO: overflow
549 #endif
550 #if defined(_WIN32)
551 } else if (type == "winreg") {
552 WinRegType eType;
553 if (url == "LOCAL_MACHINE" || url.isEmpty()/*backwards comp.*/) {
554 eType = WinRegType::LOCAL_MACHINE;
555 } else if (url == "CURRENT_USER") {
556 eType = WinRegType::CURRENT_USER;
557 } else {
558 throw css::uno::RuntimeException(
559 "CONFIGURATION_LAYERS: unknown \"winreg\" kind \"" + url
560 + "\"");
562 OUString aTempFileURL;
563 if (dumpWindowsRegistry(&aTempFileURL, eType)) {
564 parseFileLeniently(&parseXcuFile, aTempFileURL, layer, nullptr, nullptr, nullptr);
565 if (!getenv("SAL_CONFIG_WINREG_RETAIN_TMP"))
566 osl::File::remove(aTempFileURL);
568 ++layer; //TODO: overflow
569 #endif
570 } else if (type == "user") {
571 bool write;
572 if (url.startsWith("!", &url)) {
573 write = true;
574 } else if (url.startsWith("*", &url)) {
575 write = false;
576 } else {
577 write = true; // for backwards compatibility
579 if (url.isEmpty()) {
580 throw css::uno::RuntimeException(
581 "CONFIGURATION_LAYERS: empty \"user\" URL");
583 bool ignore = false;
584 #if ENABLE_DCONF
585 if (write) {
586 OUString token(
587 expand("${SYSUSERCONFIG}/libreoffice/dconfwrite"));
588 osl::DirectoryItem it;
589 osl::FileBase::RC e = osl::DirectoryItem::get(token, it);
590 ignore = e == osl::FileBase::E_None;
591 SAL_INFO(
592 "configmgr",
593 "dconf write (<" << token << "> " << +e << "): "
594 << int(ignore));
595 if (ignore) {
596 modificationTarget_ = ModificationTarget::Dconf;
599 #endif
600 if (!ignore) {
601 if (write) {
602 modificationTarget_ = ModificationTarget::File;
603 modificationFileUrl_ = url;
605 parseModificationLayer(write ? Data::NO_LAYER : layer, url);
607 ++layer; //TODO: overflow
608 } else {
609 throw css::uno::RuntimeException(
610 "CONFIGURATION_LAYERS: unknown layer type \"" + type + "\"");
612 i = n;
616 Components::~Components()
618 // get flag if _exit was already called which is a sign to not secure user config.
619 // this is used for win only currently where calling _exit() unfortunately still
620 // calls destructors (what is not wanted). May be needed for other systems, too
621 // (unknown yet) but can do no harm
622 const bool bExitWasCalled(comphelper::BackupFileHelper::getExitWasCalled());
624 #ifndef WNT
625 // we can add a SAL_WARN here for other systems where the destructor gets called after
626 // an _exit() call. Still safe - the getExitWasCalled() is used, but a hint that _exit
627 // behaves different on a system
628 SAL_WARN_IF(bExitWasCalled, "configmgr", "Components::~Components() called after _exit() call");
629 #endif
631 if (bExitWasCalled)
633 // do not write, re-join threads
634 osl::MutexGuard g(*lock_);
636 if (writeThread_.is())
638 writeThread_->join();
641 else
643 // write changes
644 flushModifications();
647 for (auto const& rootElem : roots_)
649 rootElem->setAlive(false);
653 void Components::parseFileLeniently(
654 FileParser * parseFile, OUString const & url, int layer,
655 Partial const * partial, Modifications * modifications,
656 Additions * additions)
658 assert(parseFile != nullptr);
659 try {
660 (*parseFile)(url, layer, data_, partial, modifications, additions);
661 } catch (css::container::NoSuchElementException &) {
662 throw;
663 } catch (css::uno::Exception & e) { //TODO: more specific exception catching
664 // Ignore invalid XML files, instead of completely preventing OOo from
665 // starting:
666 SAL_WARN(
667 "configmgr",
668 "error reading \"" << url << "\": " << e);
672 void Components::parseFiles(
673 int layer, OUString const & extension, FileParser * parseFile,
674 OUString const & url, bool recursive)
676 osl::Directory dir(url);
677 switch (dir.open()) {
678 case osl::FileBase::E_None:
679 break;
680 case osl::FileBase::E_NOENT:
681 if (!recursive) {
682 return;
684 SAL_FALLTHROUGH;
685 default:
686 throw css::uno::RuntimeException(
687 "cannot open directory " + url);
689 for (;;) {
690 osl::DirectoryItem i;
691 osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
692 if (rc == osl::FileBase::E_NOENT) {
693 break;
695 if (rc != osl::FileBase::E_None) {
696 throw css::uno::RuntimeException(
697 "cannot iterate directory " + url);
699 osl::FileStatus stat(
700 osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
701 osl_FileStatus_Mask_FileURL);
702 if (i.getFileStatus(stat) != osl::FileBase::E_None) {
703 throw css::uno::RuntimeException(
704 "cannot stat in directory " + url);
706 if (stat.getFileType() == osl::FileStatus::Directory) { //TODO: symlinks
707 parseFiles(layer, extension, parseFile, stat.getFileURL(), true);
708 } else {
709 OUString file(stat.getFileName());
710 if (file.endsWith(extension)) {
711 try {
712 parseFileLeniently(
713 parseFile, stat.getFileURL(), layer, nullptr, nullptr, nullptr);
714 } catch (css::container::NoSuchElementException & e) {
715 if (stat.getFileType() == osl::FileStatus::Link) {
716 SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
717 continue;
719 throw css::uno::RuntimeException(
720 "stat'ed file does not exist: " + e.Message);
727 void Components::parseFileList(
728 int layer, FileParser * parseFile, OUString const & urls,
729 bool recordAdditions)
731 for (sal_Int32 i = 0;;) {
732 OUString url(urls.getToken(0, ' ', i));
733 if (!url.isEmpty()) {
734 Additions * adds = nullptr;
735 if (recordAdditions) {
736 adds = data_.addExtensionXcuAdditions(url, layer);
738 try {
739 parseFileLeniently(parseFile, url, layer, nullptr, nullptr, adds);
740 } catch (css::container::NoSuchElementException & e) {
741 SAL_WARN("configmgr", "file does not exist: " << e);
742 if (adds != nullptr) {
743 data_.removeExtensionXcuAdditions(url);
747 if (i == -1) {
748 break;
753 void Components::parseXcdFiles(int layer, OUString const & url) {
754 osl::Directory dir(url);
755 switch (dir.open()) {
756 case osl::FileBase::E_None:
757 break;
758 case osl::FileBase::E_NOENT:
759 return;
760 default:
761 throw css::uno::RuntimeException(
762 "cannot open directory " + url);
764 UnresolvedVector unres;
765 std::set< OUString > existingDeps;
766 std::set< OUString > processedDeps;
767 for (;;) {
768 osl::DirectoryItem i;
769 osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
770 if (rc == osl::FileBase::E_NOENT) {
771 break;
773 if (rc != osl::FileBase::E_None) {
774 throw css::uno::RuntimeException(
775 "cannot iterate directory " + url);
777 osl::FileStatus stat(
778 osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
779 osl_FileStatus_Mask_FileURL);
780 if (i.getFileStatus(stat) != osl::FileBase::E_None) {
781 throw css::uno::RuntimeException(
782 "cannot stat in directory " + url);
784 if (stat.getFileType() != osl::FileStatus::Directory) { //TODO: symlinks
785 OUString file(stat.getFileName());
786 OUString name;
787 if (file.endsWith(".xcd", &name)) {
788 existingDeps.insert(name);
789 rtl::Reference< ParseManager > manager;
790 try {
791 manager = new ParseManager(
792 stat.getFileURL(),
793 new XcdParser(layer, processedDeps, data_));
794 } catch (css::container::NoSuchElementException & e) {
795 if (stat.getFileType() == osl::FileStatus::Link) {
796 SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
797 continue;
799 throw css::uno::RuntimeException(
800 "stat'ed file does not exist: " + e.Message);
802 if (manager->parse(nullptr)) {
803 processedDeps.insert(name);
804 } else {
805 unres.emplace_back(name, manager);
810 while (!unres.empty()) {
811 bool isResolved = false;
812 for (UnresolvedVector::iterator i(unres.begin()); i != unres.end();) {
813 if (i->manager->parse(&existingDeps)) {
814 processedDeps.insert(i->name);
815 i = unres.erase(i);
816 isResolved = true;
817 } else {
818 ++i;
821 if (!isResolved) {
822 throw css::uno::RuntimeException(
823 "xcd: unresolved dependencies in " + url);
828 void Components::parseXcsXcuLayer(int layer, OUString const & url) {
829 parseXcdFiles(layer, url);
830 parseFiles(layer, ".xcs", &parseXcsFile, url + "/schema", false);
831 parseFiles(layer + 1, ".xcu", &parseXcuFile, url + "/data", false);
834 void Components::parseXcsXcuIniLayer(
835 int layer, OUString const & url, bool recordAdditions)
837 // Check if ini file exists (otherwise .override would still read global
838 // SCHEMA/DATA variables, which could interfere with unrelated environment
839 // variables):
840 if (rtl::Bootstrap(url).getHandle() != nullptr) {
841 OUStringBuffer prefix("${.override:");
842 for (sal_Int32 i = 0; i != url.getLength(); ++i) {
843 sal_Unicode c = url[i];
844 switch (c) {
845 case '$':
846 case ':':
847 case '\\':
848 prefix.append('\\');
849 SAL_FALLTHROUGH;
850 default:
851 prefix.append(c);
854 prefix.append(':');
855 OUString urls(prefix.toString() + "SCHEMA}");
856 rtl::Bootstrap::expandMacros(urls);
857 if (!urls.isEmpty()) {
858 parseFileList(layer, &parseXcsFile, urls, false);
860 urls = prefix.makeStringAndClear() + "DATA}";
861 rtl::Bootstrap::expandMacros(urls);
862 if (!urls.isEmpty()) {
863 parseFileList(layer + 1, &parseXcuFile, urls, recordAdditions);
868 void Components::parseModuleLayer(int layer, OUString const & url) {
869 parseFiles(layer, ".xcu", &parseXcuFile, url, false);
872 void Components::parseResLayer(int layer, OUString const & url) {
873 OUString resUrl(url + "/res");
874 parseXcdFiles(layer, resUrl);
875 parseFiles(layer, ".xcu", &parseXcuFile, resUrl, false);
878 void Components::parseModificationLayer(int layer, OUString const & url) {
879 try {
880 parseFileLeniently(&parseXcuFile, url, layer, nullptr, nullptr, nullptr);
881 } catch (css::container::NoSuchElementException &) {
882 SAL_INFO(
883 "configmgr", "user registrymodifications.xcu does not (yet) exist");
884 // Migrate old user layer data (can be removed once migration is no
885 // longer relevant, probably OOo 4; also see hack for xsi namespace in
886 // xmlreader::XmlReader::registerNamespaceIri):
887 parseFiles(
888 layer, ".xcu", &parseXcuFile,
889 expand(
890 "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
891 ":UserInstallation}/user/registry/data"),
892 false);
896 int Components::getExtensionLayer(bool shared) const {
897 int layer = shared ? sharedExtensionLayer_ : userExtensionLayer_;
898 if (layer == -1) {
899 throw css::uno::RuntimeException(
900 "insert extension xcs/xcu file into undefined layer");
902 return layer;
907 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */