writemodfile.cxx 21.8 KB
Newer Older
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
Michael Meeks's avatar
Michael Meeks committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * 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 .
 */
19

20
#include <sal/config.h>
21

22
#include <cassert>
23 24
#include <cstddef>
#include <limits>
25
#include <string_view>
26

27 28 29 30 31 32 33 34 35 36 37 38 39 40
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/uno/XInterface.hpp>
#include <osl/file.h>
#include <osl/file.hxx>
#include <rtl/string.h>
#include <rtl/string.hxx>
#include <rtl/textcvt.h>
#include <rtl/textenc.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.h>
#include <rtl/ustring.hxx>
41
#include <rtl/strbuf.hxx>
42 43 44
#include <sal/log.hxx>
#include <sal/types.h>
#include <xmlreader/span.hxx>
45 46 47 48 49

#include "data.hxx"
#include "groupnode.hxx"
#include "localizedpropertynode.hxx"
#include "localizedvaluenode.hxx"
50
#include "modifications.hxx"
51 52 53 54 55 56 57 58
#include "node.hxx"
#include "nodemap.hxx"
#include "propertynode.hxx"
#include "type.hxx"
#include "writemodfile.hxx"

namespace configmgr {

59 60
class Components;

61 62
namespace {

63
OString convertToUtf8(std::u16string_view text) {
64
    OString s;
65
    assert(text.size() <= sal_uInt32(std::numeric_limits<sal_Int32>::max()));
66
    if (!rtl_convertUStringToString(
67
            &s.pData, text.data(), text.size(),
68 69 70 71 72
            RTL_TEXTENCODING_UTF8,
            (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
             RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
    {
        throw css::uno::RuntimeException(
73
            "cannot convert to UTF-8");
74 75 76 77
    }
    return s;
}

78
} // anonymous namespace
79 80

TempFile::~TempFile() {
81
    if (handle != nullptr) {
82 83 84
        if (!closed) {
            oslFileError e = osl_closeFile(handle);
            if (e != osl_File_E_None) {
85
                SAL_WARN("configmgr", "osl_closeFile failed with " << +e);
86 87 88 89
            }
        }
        osl::FileBase::RC e = osl::File::remove(url);
        if (e != osl::FileBase::E_None) {
90 91 92
            SAL_WARN(
                "configmgr",
                "osl::File::remove(" << url << ") failed with " << +e);
93 94 95 96
        }
    }
}

97
#ifdef _WIN32
98 99 100
oslFileError TempFile::closeWithoutUnlink() {
    flush();
    oslFileError e = osl_closeFile(handle);
101
    handle = nullptr;
102 103 104
    closed = true;
    return e;
}
Noel Grandin's avatar
Noel Grandin committed
105
#endif
106 107 108 109

void TempFile::closeAndRename(const OUString &_url) {
    oslFileError e = flush();
    if (e != osl_File_E_None) {
110
        throw css::uno::RuntimeException(
111
            "cannot write to " + url);
112
    }
113 114 115 116 117 118 119 120 121 122
    e = osl_closeFile(handle);
    closed = true;
    if (e != osl_File_E_None) {
        throw css::uno::RuntimeException(
            "cannot close " + url);
    }
    if (osl::File::move(url, _url) != osl::FileBase::E_None) {
        throw css::uno::RuntimeException(
            "cannot move " + url);
    }
123
    handle = nullptr;
124 125
}

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
oslFileError TempFile::flush() {
    oslFileError e = osl_File_E_None;
    if (!buffer.isEmpty()) {
        sal_uInt64 nBytesWritten = 0;
        e = osl_writeFile(handle, buffer.getStr(),
                          static_cast< sal_uInt32 >(buffer.getLength()),
                          &nBytesWritten);
        if (nBytesWritten != static_cast< sal_uInt32 >(buffer.getLength())) {
            // queue up any error / exception until close.
            buffer.remove(0, static_cast< sal_Int32 >( nBytesWritten ) );
        } else {
            buffer.setLength(0);
        }
    }
    return e;
}

143
void TempFile::writeString(std::string_view text) {
144
    buffer.append(text.data(), text.size());
145 146 147 148 149 150
    if (buffer.getLength() > 0x10000)
        flush();
}

namespace {

151
void writeValueContent_(TempFile &, bool) = delete;
Noel Grandin's avatar
Noel Grandin committed
152
    // silence loplugin:salbool
153
void writeValueContent_(TempFile &handle, sal_Bool value) {
154
    if (value) {
155
        handle.writeString("true");
156
    } else {
157
        handle.writeString("false");
158 159 160
    }
}

161
void writeValueContent_(TempFile &handle, sal_Int16 value) {
162
    handle.writeString(OString::number(value));
163 164
}

165
void writeValueContent_(TempFile &handle, sal_Int32 value) {
166
    handle.writeString(OString::number(value));
167 168
}

169
void writeValueContent_(TempFile &handle, sal_Int64 value) {
170
    handle.writeString(OString::number(value));
171 172
}

173
void writeValueContent_(TempFile &handle, double value) {
174
    handle.writeString(OString::number(value));
175 176
}

177
void writeValueContent_(TempFile &handle, const OUString& value) {
Stephan Bergmann's avatar
Stephan Bergmann committed
178
    writeValueContent(handle, value);
179 180
}

Stephan Bergmann's avatar
Stephan Bergmann committed
181
void writeValueContent_(
182
    TempFile &handle, css::uno::Sequence< sal_Int8 > const & value)
183 184 185 186 187
{
    for (sal_Int32 i = 0; i < value.getLength(); ++i) {
        static char const hexDigit[16] = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
            'D', 'E', 'F' };
188
        handle.writeString(
189 190
            std::string_view(hexDigit + ((value[i] >> 4) & 0xF), 1));
        handle.writeString(std::string_view(hexDigit + (value[i] & 0xF), 1));
191 192 193 194
    }
}

template< typename T > void writeSingleValue(
195
    TempFile &handle, css::uno::Any const & value)
196
{
197
    handle.writeString(">");
198 199
    T val = T();
    value >>= val;
Stephan Bergmann's avatar
Stephan Bergmann committed
200
    writeValueContent_(handle, val);
201
    handle.writeString("</value>");
202 203 204
}

template< typename T > void writeListValue(
205
    TempFile &handle, css::uno::Any const & value)
206
{
207
    handle.writeString(">");
208 209 210 211
    css::uno::Sequence< T > val;
    value >>= val;
    for (sal_Int32 i = 0; i < val.getLength(); ++i) {
        if (i != 0) {
212
            handle.writeString(" ");
213
        }
Stephan Bergmann's avatar
Stephan Bergmann committed
214
        writeValueContent_(handle, val[i]);
215
    }
216
    handle.writeString("</value>");
217 218 219
}

template< typename T > void writeItemListValue(
220
    TempFile &handle, css::uno::Any const & value)
221
{
222
    handle.writeString(">");
223 224 225
    css::uno::Sequence< T > val;
    value >>= val;
    for (sal_Int32 i = 0; i < val.getLength(); ++i) {
226
        handle.writeString("<it>");
Stephan Bergmann's avatar
Stephan Bergmann committed
227
        writeValueContent_(handle, val[i]);
228
        handle.writeString("</it>");
229
    }
230
    handle.writeString("</value>");
231 232
}

233
void writeValue(TempFile &handle, Type type, css::uno::Any const & value) {
234 235
    switch (type) {
    case TYPE_BOOLEAN:
236
        writeSingleValue< sal_Bool >(handle, value);
237 238 239 240 241 242 243 244 245 246 247 248 249 250
        break;
    case TYPE_SHORT:
        writeSingleValue< sal_Int16 >(handle, value);
        break;
    case TYPE_INT:
        writeSingleValue< sal_Int32 >(handle, value);
        break;
    case TYPE_LONG:
        writeSingleValue< sal_Int64 >(handle, value);
        break;
    case TYPE_DOUBLE:
        writeSingleValue< double >(handle, value);
        break;
    case TYPE_STRING:
251
        writeSingleValue< OUString >(handle, value);
252 253 254 255 256
        break;
    case TYPE_HEXBINARY:
        writeSingleValue< css::uno::Sequence< sal_Int8 > >(handle, value);
        break;
    case TYPE_BOOLEAN_LIST:
257
        writeListValue< sal_Bool >(handle, value);
258 259 260 261 262 263 264 265 266 267 268 269 270 271
        break;
    case TYPE_SHORT_LIST:
        writeListValue< sal_Int16 >(handle, value);
        break;
    case TYPE_INT_LIST:
        writeListValue< sal_Int32 >(handle, value);
        break;
    case TYPE_LONG_LIST:
        writeListValue< sal_Int64 >(handle, value);
        break;
    case TYPE_DOUBLE_LIST:
        writeListValue< double >(handle, value);
        break;
    case TYPE_STRING_LIST:
272
        writeItemListValue< OUString >(handle, value);
273 274 275 276
        break;
    case TYPE_HEXBINARY_LIST:
        writeItemListValue< css::uno::Sequence< sal_Int8 > >(handle, value);
        break;
277
    default: // TYPE_ERROR, TYPE_NIL, TYPE_ANY
278
        assert(false); // this cannot happen
279 280 281 282
    }
}

void writeNode(
283
    Components & components, TempFile &handle,
284
    rtl::Reference< Node > const & parent, std::u16string_view name,
285
    rtl::Reference< Node > const & node)
286
{
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
    static xmlreader::Span const typeNames[] = {
        xmlreader::Span(), xmlreader::Span(), xmlreader::Span(),
            // TYPE_ERROR, TYPE_NIL, TYPE_ANY
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:boolean")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:short")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:int")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:long")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:double")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:string")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:hexBinary")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:boolean-list")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:short-list")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:int-list")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:long-list")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:double-list")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:string-list")),
        xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:hexBinary-list")) };
304 305 306
    switch (node->kind()) {
    case Node::KIND_PROPERTY:
        {
307
            PropertyNode * prop = static_cast< PropertyNode * >(node.get());
308
            handle.writeString("<prop oor:name=\"");
309
            writeAttributeValue(handle, name);
310
            handle.writeString("\" oor:op=\"fuse\"");
311 312
            Type type = prop->getStaticType();
            Type dynType = getDynamicType(prop->getValue(components));
313
            assert(dynType != TYPE_ERROR);
314
            if (type == TYPE_ANY) {
315 316
                type = dynType;
                if (type != TYPE_NIL) {
317 318
                    handle.writeString(" oor:type=\"");
                    handle.writeString(
319
                        std::string_view(
320 321
                            typeNames[type].begin, typeNames[type].length));
                    handle.writeString("\"");
322 323
                }
            }
324
            handle.writeString("><value");
325
            if (dynType == TYPE_NIL) {
326
                handle.writeString(" xsi:nil=\"true\"/>");
327 328 329
            } else {
                writeValue(handle, type, prop->getValue(components));
            }
330
            handle.writeString("</prop>");
331 332 333
        }
        break;
    case Node::KIND_LOCALIZED_PROPERTY:
334
        handle.writeString("<prop oor:name=\"");
sb's avatar
sb committed
335
        writeAttributeValue(handle, name);
336
        handle.writeString("\" oor:op=\"fuse\">");
337
        for (auto const& member : node->getMembers())
338
        {
339
            writeNode(components, handle, node, member.first, member.second);
340
        }
341
        handle.writeString("</prop>");
342 343 344
        break;
    case Node::KIND_LOCALIZED_VALUE:
        {
345 346 347
            handle.writeString("<value");
            if (!name.empty()) {
                handle.writeString(" xml:lang=\"");
sb's avatar
sb committed
348
                writeAttributeValue(handle, name);
349
                handle.writeString("\"");
sb's avatar
sb committed
350
            }
351
            Type type = static_cast< LocalizedPropertyNode * >(parent.get())->
352
                getStaticType();
sb's avatar
sb committed
353
            css::uno::Any value(
354
                static_cast< LocalizedValueNode * >(node.get())->getValue());
355
            Type dynType = getDynamicType(value);
356
            assert(dynType != TYPE_ERROR);
sb's avatar
sb committed
357
            if (type == TYPE_ANY) {
358 359
                type = dynType;
                if (type != TYPE_NIL) {
360 361
                    handle.writeString(" oor:type=\"");
                    handle.writeString(
362
                        std::string_view(
363 364
                            typeNames[type].begin, typeNames[type].length));
                    handle.writeString("\"");
365 366
                }
            }
367
            if (dynType == TYPE_NIL) {
368
                handle.writeString(" xsi:nil=\"true\"/>");
369 370 371
            } else {
                writeValue(handle, type, value);
            }
372 373 374 375
        }
        break;
    case Node::KIND_GROUP:
    case Node::KIND_SET:
376
        handle.writeString("<node oor:name=\"");
sb's avatar
sb committed
377
        writeAttributeValue(handle, name);
378
        if (!node->getTemplateName().isEmpty()) { // set member
379
            handle.writeString("\" oor:op=\"replace");
sb's avatar
sb committed
380
        }
381
        handle.writeString("\">");
382
        for (auto const& member : node->getMembers())
383
        {
384
            writeNode(components, handle, node, member.first, member.second);
385
        }
386
        handle.writeString("</node>");
387
        break;
388 389 390
    case Node::KIND_ROOT:
        assert(false); // this cannot happen
        break;
391 392 393
    }
}

394
// helpers to allow sorting of configmgr::Modifications::Node
395
typedef std::pair< const OUString, configmgr::Modifications::Node > ModNodePairEntry;
396 397 398 399 400 401 402 403
struct PairEntrySorter
{
    bool operator() (const ModNodePairEntry* pValue1, const ModNodePairEntry* pValue2) const
    {
        return pValue1->first.compareTo(pValue2->first) < 0;
    }
};

404
void writeModifications(
405
    Components & components, TempFile &handle,
406 407
    OUString const & parentPathRepresentation,
    rtl::Reference< Node > const & parent, OUString const & nodeName,
408
    rtl::Reference< Node > const & node,
sb's avatar
sb committed
409
    Modifications::Node const & modifications)
410
{
411 412
    // It is never necessary to write oor:finalized or oor:mandatory attributes,
    // as they cannot be set via the UNO API.
413
    if (modifications.children.empty()) {
414
        assert(parent.is());
415
            // components themselves have no parent but must have children
416
        handle.writeString("<item oor:path=\"");
417
        writeAttributeValue(handle, parentPathRepresentation);
418
        handle.writeString("\">");
419
        if (node.is()) {
420
            writeNode(components, handle, parent, nodeName, node);
421 422 423
        } else {
            switch (parent->kind()) {
            case Node::KIND_LOCALIZED_PROPERTY:
424
                handle.writeString("<value");
425
                if (!nodeName.isEmpty()) {
426
                    handle.writeString(" xml:lang=\"");
427
                    writeAttributeValue(handle, nodeName);
428
                    handle.writeString("\"");
429
                }
430
                handle.writeString(" oor:op=\"remove\"/>");
431 432
                break;
            case Node::KIND_GROUP:
433
                assert(
434
                    static_cast< GroupNode * >(parent.get())->isExtensible());
435
                handle.writeString("<prop oor:name=\"");
436
                writeAttributeValue(handle, nodeName);
437
                handle.writeString("\" oor:op=\"remove\"/>");
438 439
                break;
            case Node::KIND_SET:
440
                handle.writeString("<node oor:name=\"");
441
                writeAttributeValue(handle, nodeName);
442
                handle.writeString("\" oor:op=\"remove\"/>");
443 444
                break;
            default:
445
                assert(false); // this cannot happen
446 447 448
                break;
            }
        }
449
        handle.writeString("</item>\n");
450
    } else {
451
        assert(node.is());
452
        OUString pathRep(
453
            parentPathRepresentation + "/" +
454
            Data::createSegment(node->getTemplateName(), nodeName));
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471

        // copy configmgr::Modifications::Node's to a sortable list. Use pointers
        // to just reference the data instead of copying it
        std::vector< const ModNodePairEntry* > ModNodePairEntryVector;
        ModNodePairEntryVector.reserve(modifications.children.size());

        for (const auto& rCand : modifications.children)
        {
            ModNodePairEntryVector.push_back(&rCand);
        }

        // sort the list
        std::sort(ModNodePairEntryVector.begin(), ModNodePairEntryVector.end(), PairEntrySorter());

        // now use the list to write entries in sorted order
        // instead of random as from the unordered map
        for (const auto & i : ModNodePairEntryVector)
472 473
        {
            writeModifications(
474 475
                components, handle, pathRep, node, i->first,
                node->getMember(i->first), i->second);
476 477 478 479
        }
    }
}

Stephan Bergmann's avatar
Stephan Bergmann committed
480 481
}

482
void writeAttributeValue(TempFile &handle, std::u16string_view value) {
483 484 485
    std::size_t i = 0;
    std::size_t j = i;
    for (; j != value.size(); ++j) {
Stephan Bergmann's avatar
Stephan Bergmann committed
486 487 488 489 490
        assert(
            value[j] == 0x0009 || value[j] == 0x000A || value[j] == 0x000D ||
            (value[j] >= 0x0020 && value[j] != 0xFFFE && value[j] != 0xFFFF));
        switch(value[j]) {
        case '\x09':
491 492
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&#9;");
Stephan Bergmann's avatar
Stephan Bergmann committed
493 494 495
            i = j + 1;
            break;
        case '\x0A':
496 497
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&#xA;");
Stephan Bergmann's avatar
Stephan Bergmann committed
498 499 500
            i = j + 1;
            break;
        case '\x0D':
501 502
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&#xD;");
Stephan Bergmann's avatar
Stephan Bergmann committed
503 504 505
            i = j + 1;
            break;
        case '"':
506 507
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&quot;");
Stephan Bergmann's avatar
Stephan Bergmann committed
508 509 510
            i = j + 1;
            break;
        case '&':
511 512
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&amp;");
Stephan Bergmann's avatar
Stephan Bergmann committed
513 514 515
            i = j + 1;
            break;
        case '<':
516 517
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&lt;");
Stephan Bergmann's avatar
Stephan Bergmann committed
518 519 520 521 522 523
            i = j + 1;
            break;
        default:
            break;
        }
    }
524
    handle.writeString(convertToUtf8(value.substr(i, j - i)));
Stephan Bergmann's avatar
Stephan Bergmann committed
525 526
}

527
void writeValueContent(TempFile &handle, std::u16string_view value) {
528 529 530 531
    std::size_t i = 0;
    std::size_t j = i;
    for (; j != value.size(); ++j) {
        char16_t c = value[j];
Stephan Bergmann's avatar
Stephan Bergmann committed
532 533 534
        if ((c < 0x0020 && c != 0x0009 && c != 0x000A && c != 0x000D) ||
            c == 0xFFFE || c == 0xFFFF)
        {
535 536 537 538
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("<unicode oor:scalar=\"");
            handle.writeString(OString::number(c));
            handle.writeString("\"/>");
Stephan Bergmann's avatar
Stephan Bergmann committed
539 540
            i = j + 1;
        } else if (c == '\x0D') {
541 542
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&#xD;");
Stephan Bergmann's avatar
Stephan Bergmann committed
543 544
            i = j + 1;
        } else if (c == '&') {
545 546
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&amp;");
Stephan Bergmann's avatar
Stephan Bergmann committed
547 548
            i = j + 1;
        } else if (c == '<') {
549 550
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&lt;");
Stephan Bergmann's avatar
Stephan Bergmann committed
551 552 553 554
            i = j + 1;
        } else if (c == '>') {
            // "MUST, for compatibility, be escaped [...] when it appears in the
            // string ']]>'":
555 556
            handle.writeString(convertToUtf8(value.substr(i, j - i)));
            handle.writeString("&gt;");
Stephan Bergmann's avatar
Stephan Bergmann committed
557 558 559
            i = j + 1;
        }
    }
560
    handle.writeString(convertToUtf8(value.substr(i, j - i)));
Stephan Bergmann's avatar
Stephan Bergmann committed
561 562
}

563
void writeModFile(
564
    Components & components, OUString const & url, Data const & data)
565
{
566
    sal_Int32 i = url.lastIndexOf('/');
567
    assert(i != -1);
568
    OUString dir(url.copy(0, i));
569 570 571 572
    switch (osl::Directory::createPath(dir)) {
    case osl::FileBase::E_None:
    case osl::FileBase::E_EXIST:
        break;
573
    case osl::FileBase::E_ACCES:
574 575 576 577
        SAL_INFO(
            "configmgr",
            ("cannot create registrymodifications.xcu path (E_ACCES); changes"
             " will be lost"));
578
        return;
579 580
    default:
        throw css::uno::RuntimeException(
581
            "cannot create directory " + dir);
582 583
    }
    TempFile tmp;
584 585 586 587
    switch (osl::FileBase::createTempFile(&dir, &tmp.handle, &tmp.url)) {
    case osl::FileBase::E_None:
        break;
    case osl::FileBase::E_ACCES:
588 589 590 591
        SAL_INFO(
            "configmgr",
            ("cannot create temp registrymodifications.xcu (E_ACCES); changes"
             " will be lost"));
592 593
        return;
    default:
594
        throw css::uno::RuntimeException(
595
            "cannot create temporary file in " + dir);
596
    }
597 598 599 600 601
    tmp.writeString(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items"
        " xmlns:oor=\"http://openoffice.org/2001/registry\""
        " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
        " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n");
602 603 604
    //TODO: Do not write back information about those removed items that did not
    // come from the .xcs/.xcu files, anyway (but had been added dynamically
    // instead):
605 606 607 608 609

    // For profilesafemode it is necessary to detect changes in the
    // registrymodifications file, this is done based on file size in bytes and crc32.
    // Unfortunately this write is based on writing unordered map entries, which creates
    // valid and semantically equal XML-Files, bubt with different crc32 checksums. For
Andrea Gelmini's avatar
Andrea Gelmini committed
610
    // the future usage it will be preferable to have easily comparable config files
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
    // which is guaranteed by writing the entries in sorted order. Indeed with this change
    // (and in the recursive writeModifications call) the same config files get written

    // copy configmgr::Modifications::Node's to a sortable list. Use pointers
    // to just reference the data instead of copying it
    std::vector< const ModNodePairEntry* > ModNodePairEntryVector;
    ModNodePairEntryVector.reserve(data.modifications.getRoot().children.size());

    for (const auto& rCand : data.modifications.getRoot().children)
    {
        ModNodePairEntryVector.push_back(&rCand);
    }

    // sort the list
    std::sort(ModNodePairEntryVector.begin(), ModNodePairEntryVector.end(), PairEntrySorter());

    // now use the list to write entries in sorted order
    // instead of random as from the unordered map
    for (const auto& j : ModNodePairEntryVector)
630
    {
631
        writeModifications(
632
            components, tmp, "", rtl::Reference< Node >(), j->first,
633
            data.getComponents().findNode(Data::NO_LAYER, j->first),
634
            j->second);
635
    }
636
    tmp.writeString("</oor:items>\n");
637
    tmp.closeAndRename(url);
638 639 640
}

}
641 642

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */