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"
24 #include "boost/noncopyable.hpp"
25 #include "com/sun/star/uno/Any.hxx"
26 #include "com/sun/star/uno/Reference.hxx"
27 #include "com/sun/star/uno/RuntimeException.hpp"
28 #include "com/sun/star/uno/Sequence.hxx"
29 #include "com/sun/star/uno/XInterface.hpp"
31 #include "osl/file.hxx"
32 #include "rtl/string.h"
33 #include "rtl/string.hxx"
34 #include "rtl/textcvt.h"
35 #include "rtl/textenc.h"
36 #include "rtl/ustrbuf.hxx"
37 #include "rtl/ustring.h"
38 #include "rtl/ustring.hxx"
39 #include "sal/log.hxx"
40 #include "sal/types.h"
41 #include "xmlreader/span.hxx"
44 #include "groupnode.hxx"
45 #include "localizedpropertynode.hxx"
46 #include "localizedvaluenode.hxx"
47 #include "modifications.hxx"
49 #include "nodemap.hxx"
50 #include "propertynode.hxx"
52 #include "writemodfile.hxx"
60 OString
convertToUtf8(
61 OUString
const & text
, sal_Int32 offset
, sal_Int32 length
)
63 assert(offset
<= text
.getLength() && text
.getLength() - offset
>= length
);
65 if (!rtl_convertUStringToString(
66 &s
.pData
, text
.pData
->buffer
+ offset
, length
,
67 RTL_TEXTENCODING_UTF8
,
68 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
|
69 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
)))
71 throw css::uno::RuntimeException(
72 OUString("cannot convert to UTF-8"),
73 css::uno::Reference
< css::uno::XInterface
>());
78 struct TempFile
: public boost::noncopyable
{
83 TempFile(): handle(0), closed(false) {}
88 TempFile::~TempFile() {
91 oslFileError e
= osl_closeFile(handle
);
92 if (e
!= osl_File_E_None
) {
93 SAL_WARN("configmgr", "osl_closeFile failed with " << +e
);
96 osl::FileBase::RC e
= osl::File::remove(url
);
97 if (e
!= osl::FileBase::E_None
) {
100 "osl::File::remove(" << url
<< ") failed with " << +e
);
105 void writeData(oslFileHandle handle
, char const * begin
, sal_Int32 length
) {
108 if ((osl_writeFile(handle
, begin
, static_cast< sal_uInt32
>(length
), &n
) !=
110 n
!= static_cast< sal_uInt32
>(length
))
112 throw css::uno::RuntimeException(
113 OUString("write failure"),
114 css::uno::Reference
< css::uno::XInterface
>());
118 void writeData(oslFileHandle handle
, OString
const & text
) {
119 writeData(handle
, text
.getStr(), text
.getLength());
122 void writeAttributeValue(oslFileHandle handle
, OUString
const & value
) {
125 for (; j
< value
.getLength(); ++j
) {
127 value
[j
] == 0x0009 || value
[j
] == 0x000A || value
[j
] == 0x000D ||
128 (value
[j
] >= 0x0020 && value
[j
] != 0xFFFE && value
[j
] != 0xFFFF));
131 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
132 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("	"));
136 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
137 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("
"));
141 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
142 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("
"));
146 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
147 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("""));
151 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
152 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("&"));
156 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
157 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<"));
164 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
167 void writeValueContent(oslFileHandle handle
, sal_Bool value
) {
169 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("true"));
171 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("false"));
175 void writeValueContent(oslFileHandle handle
, sal_Int16 value
) {
176 writeData(handle
, OString::number(value
));
179 void writeValueContent(oslFileHandle handle
, sal_Int32 value
) {
180 writeData(handle
, OString::valueOf(value
));
183 void writeValueContent(oslFileHandle handle
, sal_Int64 value
) {
184 writeData(handle
, OString::valueOf(value
));
187 void writeValueContent(oslFileHandle handle
, double value
) {
188 writeData(handle
, OString::valueOf(value
));
191 void writeValueContent(oslFileHandle handle
, OUString
const & value
) {
194 for (; j
< value
.getLength(); ++j
) {
195 sal_Unicode c
= value
[j
];
196 if ((c
< 0x0020 && c
!= 0x0009 && c
!= 0x000A && c
!= 0x000D) ||
197 c
== 0xFFFE || c
== 0xFFFF)
199 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
201 handle
, RTL_CONSTASCII_STRINGPARAM("<unicode oor:scalar=\""));
203 handle
, OString::number(c
));
204 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\"/>"));
206 } else if (c
== '\x0D') {
207 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
208 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("
"));
210 } else if (c
== '&') {
211 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
212 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("&"));
214 } else if (c
== '<') {
215 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
216 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<"));
218 } else if (c
== '>') {
219 // "MUST, for compatibility, be escaped [...] when it appears in the
221 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
222 writeData(handle
, RTL_CONSTASCII_STRINGPARAM(">"));
226 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
229 void writeValueContent(
230 oslFileHandle handle
, css::uno::Sequence
< sal_Int8
> const & value
)
232 for (sal_Int32 i
= 0; i
< value
.getLength(); ++i
) {
233 static char const hexDigit
[16] = {
234 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
236 writeData(handle
, hexDigit
+ ((value
[i
] >> 4) & 0xF), 1);
237 writeData(handle
, hexDigit
+ (value
[i
] & 0xF), 1);
241 template< typename T
> void writeSingleValue(
242 oslFileHandle handle
, css::uno::Any
const & value
)
244 writeData(handle
, RTL_CONSTASCII_STRINGPARAM(">"));
247 writeValueContent(handle
, val
);
248 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("</value>"));
251 template< typename T
> void writeListValue(
252 oslFileHandle handle
, css::uno::Any
const & value
)
254 writeData(handle
, RTL_CONSTASCII_STRINGPARAM(">"));
255 css::uno::Sequence
< T
> val
;
257 for (sal_Int32 i
= 0; i
< val
.getLength(); ++i
) {
259 writeData(handle
, RTL_CONSTASCII_STRINGPARAM(" "));
261 writeValueContent(handle
, val
[i
]);
263 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("</value>"));
266 template< typename T
> void writeItemListValue(
267 oslFileHandle handle
, css::uno::Any
const & value
)
269 writeData(handle
, RTL_CONSTASCII_STRINGPARAM(">"));
270 css::uno::Sequence
< T
> val
;
272 for (sal_Int32 i
= 0; i
< val
.getLength(); ++i
) {
273 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<it>"));
274 writeValueContent(handle
, val
[i
]);
275 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("</it>"));
277 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("</value>"));
280 void writeValue(oslFileHandle handle
, Type type
, css::uno::Any
const & value
) {
283 writeSingleValue
< sal_Bool
>(handle
, value
);
286 writeSingleValue
< sal_Int16
>(handle
, value
);
289 writeSingleValue
< sal_Int32
>(handle
, value
);
292 writeSingleValue
< sal_Int64
>(handle
, value
);
295 writeSingleValue
< double >(handle
, value
);
298 writeSingleValue
< OUString
>(handle
, value
);
301 writeSingleValue
< css::uno::Sequence
< sal_Int8
> >(handle
, value
);
303 case TYPE_BOOLEAN_LIST
:
304 writeListValue
< sal_Bool
>(handle
, value
);
306 case TYPE_SHORT_LIST
:
307 writeListValue
< sal_Int16
>(handle
, value
);
310 writeListValue
< sal_Int32
>(handle
, value
);
313 writeListValue
< sal_Int64
>(handle
, value
);
315 case TYPE_DOUBLE_LIST
:
316 writeListValue
< double >(handle
, value
);
318 case TYPE_STRING_LIST
:
319 writeItemListValue
< OUString
>(handle
, value
);
321 case TYPE_HEXBINARY_LIST
:
322 writeItemListValue
< css::uno::Sequence
< sal_Int8
> >(handle
, value
);
324 default: // TYPE_ERROR, TYPE_NIL, TYPE_ANY
325 assert(false); // this cannot happen
330 Components
& components
, oslFileHandle handle
,
331 rtl::Reference
< Node
> const & parent
, OUString
const & name
,
332 rtl::Reference
< Node
> const & node
)
334 static xmlreader::Span
const typeNames
[] = {
335 xmlreader::Span(), xmlreader::Span(), xmlreader::Span(),
336 // TYPE_ERROR, TYPE_NIL, TYPE_ANY
337 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:boolean")),
338 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:short")),
339 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:int")),
340 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:long")),
341 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:double")),
342 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:string")),
343 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:hexBinary")),
344 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:boolean-list")),
345 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:short-list")),
346 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:int-list")),
347 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:long-list")),
348 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:double-list")),
349 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:string-list")),
350 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:hexBinary-list")) };
351 switch (node
->kind()) {
352 case Node::KIND_PROPERTY
:
354 PropertyNode
* prop
= dynamic_cast< PropertyNode
* >(node
.get());
355 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
356 writeAttributeValue(handle
, name
);
357 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"fuse\""));
358 Type type
= prop
->getStaticType();
359 Type dynType
= getDynamicType(prop
->getValue(components
));
360 assert(dynType
!= TYPE_ERROR
);
361 if (type
== TYPE_ANY
) {
363 if (type
!= TYPE_NIL
) {
365 handle
, RTL_CONSTASCII_STRINGPARAM(" oor:type=\""));
367 handle
, typeNames
[type
].begin
, typeNames
[type
].length
);
368 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\""));
371 writeData(handle
, "><value");
372 if (dynType
== TYPE_NIL
) {
374 handle
, RTL_CONSTASCII_STRINGPARAM(" xsi:nil=\"true\"/>"));
376 writeValue(handle
, type
, prop
->getValue(components
));
378 writeData(handle
, "</prop>");
381 case Node::KIND_LOCALIZED_PROPERTY
:
382 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
383 writeAttributeValue(handle
, name
);
384 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"fuse\">"));
385 for (NodeMap::const_iterator
i(node
->getMembers().begin());
386 i
!= node
->getMembers().end(); ++i
)
388 writeNode(components
, handle
, node
, i
->first
, i
->second
);
390 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("</prop>"));
392 case Node::KIND_LOCALIZED_VALUE
:
394 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<value"));
395 if (!name
.isEmpty()) {
396 writeData(handle
, RTL_CONSTASCII_STRINGPARAM(" xml:lang=\""));
397 writeAttributeValue(handle
, name
);
398 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\""));
400 Type type
= dynamic_cast< LocalizedPropertyNode
* >(parent
.get())->
403 dynamic_cast< LocalizedValueNode
* >(node
.get())->getValue());
404 Type dynType
= getDynamicType(value
);
405 assert(dynType
!= TYPE_ERROR
);
406 if (type
== TYPE_ANY
) {
408 if (type
!= TYPE_NIL
) {
410 handle
, RTL_CONSTASCII_STRINGPARAM(" oor:type=\""));
412 handle
, typeNames
[type
].begin
, typeNames
[type
].length
);
413 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\""));
416 if (dynType
== TYPE_NIL
) {
418 handle
, RTL_CONSTASCII_STRINGPARAM(" xsi:nil=\"true\"/>"));
420 writeValue(handle
, type
, value
);
424 case Node::KIND_GROUP
:
426 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<node oor:name=\""));
427 writeAttributeValue(handle
, name
);
428 if (!node
->getTemplateName().isEmpty()) { // set member
430 handle
, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"replace"));
432 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\">"));
433 for (NodeMap::const_iterator
i(node
->getMembers().begin());
434 i
!= node
->getMembers().end(); ++i
)
436 writeNode(components
, handle
, node
, i
->first
, i
->second
);
438 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("</node>"));
440 case Node::KIND_ROOT
:
441 assert(false); // this cannot happen
446 void writeModifications(
447 Components
& components
, oslFileHandle handle
,
448 OUString
const & parentPathRepresentation
,
449 rtl::Reference
< Node
> const & parent
, OUString
const & nodeName
,
450 rtl::Reference
< Node
> const & node
,
451 Modifications::Node
const & modifications
)
453 // It is never necessary to write oor:finalized or oor:mandatory attributes,
454 // as they cannot be set via the UNO API.
455 if (modifications
.children
.empty()) {
457 // components themselves have no parent but must have children
458 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<item oor:path=\""));
459 writeAttributeValue(handle
, parentPathRepresentation
);
460 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\">"));
462 writeNode(components
, handle
, parent
, nodeName
, node
);
464 switch (parent
->kind()) {
465 case Node::KIND_LOCALIZED_PROPERTY
:
466 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("<value"));
467 if (!nodeName
.isEmpty()) {
469 handle
, RTL_CONSTASCII_STRINGPARAM(" xml:lang=\""));
470 writeAttributeValue(handle
, nodeName
);
471 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("\""));
474 handle
, RTL_CONSTASCII_STRINGPARAM(" oor:op=\"remove\"/>"));
476 case Node::KIND_GROUP
:
478 dynamic_cast< GroupNode
* >(parent
.get())->isExtensible());
480 handle
, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
481 writeAttributeValue(handle
, nodeName
);
484 RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"remove\"/>"));
488 handle
, RTL_CONSTASCII_STRINGPARAM("<node oor:name=\""));
489 writeAttributeValue(handle
, nodeName
);
492 RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"remove\"/>"));
495 assert(false); // this cannot happen
499 writeData(handle
, RTL_CONSTASCII_STRINGPARAM("</item>\n"));
503 parentPathRepresentation
+
505 Data::createSegment(node
->getTemplateName(), nodeName
));
506 for (Modifications::Node::Children::const_iterator
i(
507 modifications
.children
.begin());
508 i
!= modifications
.children
.end(); ++i
)
511 components
, handle
, pathRep
, node
, i
->first
,
512 node
->getMember(i
->first
), i
->second
);
520 Components
& components
, OUString
const & url
, Data
const & data
)
522 sal_Int32 i
= url
.lastIndexOf('/');
524 OUString
dir(url
.copy(0, i
));
525 switch (osl::Directory::createPath(dir
)) {
526 case osl::FileBase::E_None
:
527 case osl::FileBase::E_EXIST
:
529 case osl::FileBase::E_ACCES
:
532 ("cannot create registrymodifications.xcu path (E_ACCES); changes"
536 throw css::uno::RuntimeException(
537 (OUString("cannot create directory ") +
539 css::uno::Reference
< css::uno::XInterface
>());
542 switch (osl::FileBase::createTempFile(&dir
, &tmp
.handle
, &tmp
.url
)) {
543 case osl::FileBase::E_None
:
545 case osl::FileBase::E_ACCES
:
548 ("cannot create temp registrymodifications.xcu (E_ACCES); changes"
552 throw css::uno::RuntimeException(
553 (OUString("cannot create temporary file in ") +
555 css::uno::Reference
< css::uno::XInterface
>());
559 RTL_CONSTASCII_STRINGPARAM(
560 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items"
561 " xmlns:oor=\"http://openoffice.org/2001/registry\""
562 " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
563 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"));
564 //TODO: Do not write back information about those removed items that did not
565 // come from the .xcs/.xcu files, anyway (but had been added dynamically
567 for (Modifications::Node::Children::const_iterator
j(
568 data
.modifications
.getRoot().children
.begin());
569 j
!= data
.modifications
.getRoot().children
.end(); ++j
)
572 components
, tmp
.handle
, OUString(), rtl::Reference
< Node
>(),
574 Data::findNode(Data::NO_LAYER
, data
.getComponents(), j
->first
),
577 writeData(tmp
.handle
, RTL_CONSTASCII_STRINGPARAM("</oor:items>\n"));
578 oslFileError e
= osl_closeFile(tmp
.handle
);
580 if (e
!= osl_File_E_None
) {
581 throw css::uno::RuntimeException(
582 (OUString("cannot close ") +
584 css::uno::Reference
< css::uno::XInterface
>());
586 if (osl::File::move(tmp
.url
, url
) != osl::FileBase::E_None
) {
587 throw css::uno::RuntimeException(
588 (OUString("cannot move ") +
590 css::uno::Reference
< css::uno::XInterface
>());
597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */