Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/configmgr/source/valueparser.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
#include <sal/config.h>
21
22
#include <cassert>
23
24
#include <com/sun/star/uno/Any.hxx>
25
#include <com/sun/star/uno/RuntimeException.hpp>
26
#include <com/sun/star/uno/Sequence.hxx>
27
#include <comphelper/sequence.hxx>
28
#include <o3tl/string_view.hxx>
29
#include <o3tl/numeric.hxx>
30
#include <rtl/math.h>
31
#include <rtl/string.h>
32
#include <rtl/string.hxx>
33
#include <rtl/ustring.hxx>
34
#include <sal/types.h>
35
#include <xmlreader/span.hxx>
36
#include <xmlreader/xmlreader.hxx>
37
38
#include "data.hxx"
39
#include "localizedvaluenode.hxx"
40
#include "node.hxx"
41
#include "nodemap.hxx"
42
#include "parsemanager.hxx"
43
#include "propertynode.hxx"
44
#include "type.hxx"
45
#include "valueparser.hxx"
46
47
namespace configmgr {
48
49
namespace {
50
51
bool parseHexDigit(char c, int * value)
52
0
{
53
0
    assert(value != nullptr);
54
0
    int converted = o3tl::convertToHex<int>(c);
55
0
    if (converted < 0)
56
0
        return false;
57
0
    *value = converted;
58
0
    return true;
59
0
}
60
61
0
bool parseValue(xmlreader::Span const & text, sal_Bool * value) {
62
0
    assert(text.is() && value != nullptr);
63
0
    if (text == "true" || text == "1") {
64
0
        *value = true;
65
0
        return true;
66
0
    }
67
0
    if (text == "false" || text == "0") {
68
0
        *value = false;
69
0
        return true;
70
0
    }
71
0
    return false;
72
0
}
73
74
0
bool parseValue(xmlreader::Span const & text, sal_Int16 * value) {
75
0
    assert(text.is() && value != nullptr);
76
    // For backwards compatibility, support hexadecimal values:
77
0
    bool bStartWithHexPrefix =
78
0
        rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
79
0
            text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
80
0
            RTL_CONSTASCII_LENGTH("0X")) == 0;
81
0
    sal_Int32 n;
82
0
    if (bStartWithHexPrefix)
83
0
    {
84
0
        std::string_view sView(
85
0
                text.begin + RTL_CONSTASCII_LENGTH("0X"),
86
0
                text.length - RTL_CONSTASCII_LENGTH("0X"));
87
0
        n = o3tl::toUInt32(sView, 16);
88
0
    }
89
0
    else
90
0
        n = o3tl::toInt32(std::string_view(text.begin, text.length));
91
    //TODO: check valid lexical representation
92
0
    if (n >= SAL_MIN_INT16 && n <= SAL_MAX_INT16) {
93
0
        *value = static_cast< sal_Int16 >(n);
94
0
        return true;
95
0
    }
96
0
    return false;
97
0
}
98
99
0
bool parseValue(xmlreader::Span const & text, sal_Int32 * value) {
100
0
    assert(text.is() && value != nullptr);
101
    // For backwards compatibility, support hexadecimal values:
102
0
    bool bStartWithHexPrefix = rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
103
0
            text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
104
0
            RTL_CONSTASCII_LENGTH("0X")) == 0;
105
106
0
    if (bStartWithHexPrefix)
107
0
    {
108
0
        std::string_view sView(text.begin + RTL_CONSTASCII_LENGTH("0X"),
109
0
                text.length - RTL_CONSTASCII_LENGTH("0X"));
110
0
        *value = static_cast< sal_Int32 >(o3tl::toUInt32(sView, 16));
111
0
    }
112
0
    else
113
0
    {
114
0
        std::string_view sView(text.begin, text.length);
115
0
        *value = o3tl::toInt32(sView);
116
0
    }
117
    //TODO: check valid lexical representation
118
0
    return true;
119
0
}
120
121
0
bool parseValue(xmlreader::Span const & text, sal_Int64 * value) {
122
0
    assert(text.is() && value != nullptr);
123
    // For backwards compatibility, support hexadecimal values:
124
0
    bool bStartWithHexPrefix =
125
0
        rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
126
0
            text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
127
0
            RTL_CONSTASCII_LENGTH("0X")) == 0;
128
0
    if (bStartWithHexPrefix)
129
0
    {
130
0
        OString sSuffix(
131
0
                text.begin + RTL_CONSTASCII_LENGTH("0X"),
132
0
                text.length - RTL_CONSTASCII_LENGTH("0X"));
133
0
        *value = static_cast< sal_Int64 >(sSuffix.toUInt64(16));
134
0
    }
135
0
    else *value = o3tl::toInt64(std::string_view(text.begin, text.length));
136
    //TODO: check valid lexical representation
137
0
    return true;
138
0
}
139
140
0
bool parseValue(xmlreader::Span const & text, double * value) {
141
0
    assert(text.is() && value != nullptr);
142
0
    *value = rtl_math_stringToDouble(
143
0
        text.begin, text.begin + text.length, '.', 0, nullptr, nullptr);
144
        //TODO: check valid lexical representation
145
0
    return true;
146
0
}
147
148
0
bool parseValue(xmlreader::Span const & text, OUString * value) {
149
0
    assert(text.is() && value != nullptr);
150
0
    *value = text.convertFromUtf8();
151
0
    return true;
152
0
}
153
154
bool parseValue(
155
    xmlreader::Span const & text, css::uno::Sequence< sal_Int8 > * value)
156
0
{
157
0
    assert(text.is() && value != nullptr);
158
0
    if ((text.length & 1) != 0) {
159
0
        return false;
160
0
    }
161
0
    std::vector< sal_Int8 > seq;
162
0
    for (sal_Int32 i = 0; i != text.length;) {
163
0
        int n1;
164
0
        int n2;
165
0
        if (!parseHexDigit(text.begin[i++], &n1) ||
166
0
            !parseHexDigit(text.begin[i++], &n2))
167
0
        {
168
0
            return false;
169
0
        }
170
0
        seq.push_back(static_cast< sal_Int8 >((n1 << 4) | n2));
171
0
    }
172
0
    *value = comphelper::containerToSequence(seq);
173
0
    return true;
174
0
}
175
176
template< typename T > css::uno::Any parseSingleValue(
177
    xmlreader::Span const & text)
178
0
{
179
0
    T val;
180
0
    if (!parseValue(text, &val)) {
181
0
        throw css::uno::RuntimeException(u"invalid value"_ustr);
182
0
    }
183
0
    return css::uno::Any(val);
184
0
}
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseSingleValue<unsigned char>(xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseSingleValue<short>(xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseSingleValue<int>(xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseSingleValue<long>(xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseSingleValue<double>(xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseSingleValue<rtl::OUString>(xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseSingleValue<com::sun::star::uno::Sequence<signed char> >(xmlreader::Span const&)
185
186
template< typename T > css::uno::Any parseListValue(
187
    OString const & separator, xmlreader::Span const & text)
188
0
{
189
0
    std::vector< T > seq;
190
0
    xmlreader::Span sep;
191
0
    if (separator.isEmpty()) {
192
0
        sep = xmlreader::Span(RTL_CONSTASCII_STRINGPARAM(" "));
193
0
    } else {
194
0
        sep = xmlreader::Span(separator.getStr(), separator.getLength());
195
0
    }
196
0
    if (text.length != 0) {
197
0
        for (xmlreader::Span t(text);;) {
198
0
            sal_Int32 i = rtl_str_indexOfStr_WithLength(
199
0
                t.begin, t.length, sep.begin, sep.length);
200
0
            T val;
201
0
            if (!parseValue(
202
0
                    xmlreader::Span(t.begin, i == -1 ? t.length : i), &val))
203
0
            {
204
0
                throw css::uno::RuntimeException(u"invalid value"_ustr);
205
0
            }
206
0
            seq.push_back(val);
207
0
            if (i < 0) {
208
0
                break;
209
0
            }
210
0
            t.begin += i + sep.length;
211
0
            t.length -= i + sep.length;
212
0
        }
213
0
    }
214
0
    return css::uno::Any(comphelper::containerToSequence(seq));
215
0
}
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseListValue<unsigned char>(rtl::OString const&, xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseListValue<short>(rtl::OString const&, xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseListValue<int>(rtl::OString const&, xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseListValue<long>(rtl::OString const&, xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseListValue<double>(rtl::OString const&, xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseListValue<rtl::OUString>(rtl::OString const&, xmlreader::Span const&)
Unexecuted instantiation: valueparser.cxx:com::sun::star::uno::Any configmgr::(anonymous namespace)::parseListValue<com::sun::star::uno::Sequence<signed char> >(rtl::OString const&, xmlreader::Span const&)
216
217
css::uno::Any parseValue(
218
    OString const & separator, xmlreader::Span const & text, Type type)
219
0
{
220
0
    switch (type) {
221
0
    case TYPE_ANY:
222
0
        throw css::uno::RuntimeException(u"invalid value of type any"_ustr);
223
0
    case TYPE_BOOLEAN:
224
0
        return parseSingleValue< sal_Bool >(text);
225
0
    case TYPE_SHORT:
226
0
        return parseSingleValue< sal_Int16 >(text);
227
0
    case TYPE_INT:
228
0
        return parseSingleValue< sal_Int32 >(text);
229
0
    case TYPE_LONG:
230
0
        return parseSingleValue< sal_Int64 >(text);
231
0
    case TYPE_DOUBLE:
232
0
        return parseSingleValue< double >(text);
233
0
    case TYPE_STRING:
234
0
        return parseSingleValue< OUString >(text);
235
0
    case TYPE_HEXBINARY:
236
0
        return parseSingleValue< css::uno::Sequence< sal_Int8 > >(text);
237
0
    case TYPE_BOOLEAN_LIST:
238
0
        return parseListValue< sal_Bool >(separator, text);
239
0
    case TYPE_SHORT_LIST:
240
0
        return parseListValue< sal_Int16 >(separator, text);
241
0
    case TYPE_INT_LIST:
242
0
        return parseListValue< sal_Int32 >(separator, text);
243
0
    case TYPE_LONG_LIST:
244
0
        return parseListValue< sal_Int64 >(separator, text);
245
0
    case TYPE_DOUBLE_LIST:
246
0
        return parseListValue< double >(separator, text);
247
0
    case TYPE_STRING_LIST:
248
0
        return parseListValue< OUString >(separator, text);
249
0
    case TYPE_HEXBINARY_LIST:
250
0
        return parseListValue< css::uno::Sequence< sal_Int8 > >(
251
0
            separator, text);
252
0
    default:
253
0
        assert(false);
254
0
        throw css::uno::RuntimeException(u"this cannot happen"_ustr);
255
0
    }
256
0
}
257
258
}
259
260
0
ValueParser::ValueParser(int layer): type_(TYPE_ERROR), layer_(layer), state_() {}
261
262
0
ValueParser::~ValueParser() {}
263
264
0
xmlreader::XmlReader::Text ValueParser::getTextMode() const {
265
0
    if (!node_)
266
0
        return xmlreader::XmlReader::Text::NONE;
267
268
0
    switch (state_) {
269
0
    case State::Text:
270
0
        if (!items_.empty()) {
271
0
            break;
272
0
        }
273
0
        [[fallthrough]];
274
0
    case State::IT:
275
0
        return
276
0
            (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST ||
277
0
             !separator_.isEmpty())
278
0
            ? xmlreader::XmlReader::Text::Raw
279
0
            : xmlreader::XmlReader::Text::Normalized;
280
0
    default:
281
0
        break;
282
0
    }
283
0
    return xmlreader::XmlReader::Text::NONE;
284
0
}
285
286
bool ValueParser::startElement(
287
    xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name)
288
0
{
289
0
    if (!node_.is()) {
290
0
        return false;
291
0
    }
292
0
    switch (state_) {
293
0
    case State::Text:
294
0
        if (nsId == xmlreader::XmlReader::NAMESPACE_NONE && name == "it" &&
295
0
            isListType(type_) && separator_.isEmpty())
296
0
        {
297
0
            pad_.clear();
298
                // before first <it>, characters are not ignored; assume they
299
                // are only whitespace
300
0
            state_ = State::IT;
301
0
            return true;
302
0
        }
303
0
        [[fallthrough]];
304
0
    case State::IT:
305
0
        if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
306
0
            name == "unicode" &&
307
0
            (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST))
308
0
        {
309
0
            sal_Int32 scalar = -1;
310
0
            for (;;) {
311
0
                int attrNsId;
312
0
                xmlreader::Span attrLn;
313
0
                if (!reader.nextAttribute(&attrNsId, &attrLn)) {
314
0
                    break;
315
0
                }
316
0
                if (attrNsId == ParseManager::NAMESPACE_OOR &&
317
0
                    attrLn == "scalar")
318
0
                {
319
0
                    if (!parseValue(reader.getAttributeValue(true), &scalar)) {
320
0
                        scalar = -1;
321
0
                    }
322
0
                    break;
323
0
                }
324
0
            }
325
0
            if (scalar >= 0 && scalar < 0x20 && scalar != 0x09 &&
326
0
                scalar != 0x0A && scalar != 0x0D)
327
0
            {
328
0
                char c = static_cast< char >(scalar);
329
0
                pad_.add(&c, 1);
330
0
            } else if (scalar == 0xFFFE) {
331
0
                pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBE"));
332
0
            } else if (scalar == 0xFFFF) {
333
0
                pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBF"));
334
0
            } else {
335
0
                throw css::uno::RuntimeException(
336
0
                    "bad unicode scalar attribute in " + reader.getUrl());
337
0
            }
338
0
            state_ = state_ == State::Text ? State::TextUnicode : State::ITUnicode;
339
0
            return true;
340
0
        }
341
0
        break;
342
0
    default:
343
0
        break;
344
0
    }
345
0
    throw css::uno::RuntimeException(
346
0
        "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
347
0
}
348
349
0
bool ValueParser::endElement() {
350
0
    if (!node_.is()) {
351
0
        return false;
352
0
    }
353
0
    switch (state_) {
354
0
    case State::Text:
355
0
        {
356
0
            css::uno::Any *pValue = nullptr;
357
358
0
            switch (node_->kind()) {
359
0
            case Node::KIND_PROPERTY:
360
0
                pValue = static_cast< PropertyNode * >(node_.get())->getValuePtr(layer_, layer_ == Data::NO_LAYER);
361
0
                break;
362
0
            case Node::KIND_LOCALIZED_PROPERTY:
363
0
                {
364
0
                    NodeMap & members = node_->getMembers();
365
0
                    auto [i, bInserted] = members.insert(NodeMap::value_type(localizedName_, nullptr));
366
0
                    LocalizedValueNode *pLVNode;
367
0
                    if (bInserted) {
368
0
                        pLVNode = new LocalizedValueNode(layer_);
369
0
                        i->second = pLVNode;
370
0
                    } else {
371
0
                        pLVNode = static_cast< LocalizedValueNode * >(i->second.get());
372
0
                    }
373
0
                    pValue = pLVNode->getValuePtr(layer_, layer_ == Data::NO_LAYER);
374
0
                }
375
0
                break;
376
0
            default:
377
0
                assert(false); // this cannot happen
378
0
                return false;
379
0
            }
380
381
0
            if (items_.empty()) {
382
0
                *pValue = parseValue(separator_, pad_.get(), type_);
383
0
                pad_.clear();
384
0
            } else {
385
0
                switch (type_) {
386
0
                case TYPE_BOOLEAN_LIST:
387
0
                    *pValue = convertItems< sal_Bool >();
388
0
                    break;
389
0
                case TYPE_SHORT_LIST:
390
0
                    *pValue = convertItems< sal_Int16 >();
391
0
                    break;
392
0
                case TYPE_INT_LIST:
393
0
                    *pValue = convertItems< sal_Int32 >();
394
0
                    break;
395
0
                case TYPE_LONG_LIST:
396
0
                    *pValue = convertItems< sal_Int64 >();
397
0
                    break;
398
0
                case TYPE_DOUBLE_LIST:
399
0
                    *pValue = convertItems< double >();
400
0
                    break;
401
0
                case TYPE_STRING_LIST:
402
0
                    *pValue = convertItems< OUString >();
403
0
                    break;
404
0
                case TYPE_HEXBINARY_LIST:
405
0
                    *pValue = convertItems< css::uno::Sequence< sal_Int8 > >();
406
0
                    break;
407
0
                default:
408
0
                    assert(false); // this cannot happen
409
0
                    break;
410
0
                }
411
0
                items_.clear();
412
0
            }
413
0
            separator_.clear();
414
0
            node_.clear();
415
0
        }
416
0
        break;
417
0
    case State::TextUnicode:
418
0
        state_ = State::Text;
419
0
        break;
420
0
    case State::ITUnicode:
421
0
        state_ = State::IT;
422
0
        break;
423
0
    case State::IT:
424
0
        items_.push_back(
425
0
            parseValue(OString(), pad_.get(), elementType(type_)));
426
0
        pad_.clear();
427
0
        state_ = State::Text;
428
0
        break;
429
0
    }
430
0
    return true;
431
0
}
432
433
0
void ValueParser::characters(xmlreader::Span const & text) {
434
0
    if (node_.is()) {
435
0
        assert(state_ == State::Text || state_ == State::IT);
436
0
        pad_.add(text.begin, text.length);
437
0
    }
438
0
}
439
440
void ValueParser::start(
441
    rtl::Reference< Node > const & node, OUString const & localizedName)
442
0
{
443
0
    assert(node.is() && !node_.is());
444
0
    node_ = node;
445
0
    localizedName_ = localizedName;
446
0
    state_ = State::Text;
447
0
}
448
449
450
0
template< typename T > css::uno::Any ValueParser::convertItems() {
451
0
    css::uno::Sequence< T > seq(items_.size());
452
0
    auto seqRange = asNonConstRange(seq);
453
0
    for (sal_Int32 i = 0; i < seq.getLength(); ++i) {
454
0
        bool ok = (items_[i] >>= seqRange[i]);
455
        assert(ok);
456
0
        (void) ok; // avoid warnings
457
0
    }
458
0
    return css::uno::Any(seq);
459
0
}
Unexecuted instantiation: com::sun::star::uno::Any configmgr::ValueParser::convertItems<unsigned char>()
Unexecuted instantiation: com::sun::star::uno::Any configmgr::ValueParser::convertItems<short>()
Unexecuted instantiation: com::sun::star::uno::Any configmgr::ValueParser::convertItems<int>()
Unexecuted instantiation: com::sun::star::uno::Any configmgr::ValueParser::convertItems<long>()
Unexecuted instantiation: com::sun::star::uno::Any configmgr::ValueParser::convertItems<double>()
Unexecuted instantiation: com::sun::star::uno::Any configmgr::ValueParser::convertItems<rtl::OUString>()
Unexecuted instantiation: com::sun::star::uno::Any configmgr::ValueParser::convertItems<com::sun::star::uno::Sequence<signed char> >()
460
461
}
462
463
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */