Avoid potential negative array index access to cached text.
[LibreOffice.git] / configmgr / source / valueparser.cxx
blob6b9faf6edbe0bffec456402a7f732b763409ce0f
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 <cassert>
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 <rtl/math.h>
30 #include <rtl/string.h>
31 #include <rtl/string.hxx>
32 #include <rtl/ustring.hxx>
33 #include <sal/types.h>
34 #include <xmlreader/span.hxx>
35 #include <xmlreader/xmlreader.hxx>
37 #include "data.hxx"
38 #include "localizedvaluenode.hxx"
39 #include "node.hxx"
40 #include "nodemap.hxx"
41 #include "parsemanager.hxx"
42 #include "propertynode.hxx"
43 #include "type.hxx"
44 #include "valueparser.hxx"
46 namespace configmgr {
48 namespace {
50 bool parseHexDigit(char c, int * value) {
51 assert(value != nullptr);
52 if (c >= '0' && c <= '9') {
53 *value = c - '0';
54 return true;
56 if (c >= 'A' && c <= 'F') {
57 *value = c - 'A' + 10;
58 return true;
60 if (c >= 'a' && c <= 'f') {
61 *value = c - 'a' + 10;
62 return true;
64 return false;
67 bool parseValue(xmlreader::Span const & text, sal_Bool * value) {
68 assert(text.is() && value != nullptr);
69 if (text == "true" || text == "1") {
70 *value = true;
71 return true;
73 if (text == "false" || text == "0") {
74 *value = false;
75 return true;
77 return false;
80 bool parseValue(xmlreader::Span const & text, sal_Int16 * value) {
81 assert(text.is() && value != nullptr);
82 // For backwards compatibility, support hexadecimal values:
83 sal_Int32 n =
84 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
85 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
86 RTL_CONSTASCII_LENGTH("0X")) == 0 ?
87 static_cast< sal_Int32 >(
88 OString(
89 text.begin + RTL_CONSTASCII_LENGTH("0X"),
90 text.length - RTL_CONSTASCII_LENGTH("0X")).toUInt32(16)) :
91 OString(text.begin, text.length).toInt32();
92 //TODO: check valid lexical representation
93 if (n >= SAL_MIN_INT16 && n <= SAL_MAX_INT16) {
94 *value = static_cast< sal_Int16 >(n);
95 return true;
97 return false;
100 bool parseValue(xmlreader::Span const & text, sal_Int32 * value) {
101 assert(text.is() && value != nullptr);
102 // For backwards compatibility, support hexadecimal values:
103 bool bStartWithHexPrefix = rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
104 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
105 RTL_CONSTASCII_LENGTH("0X")) == 0;
107 if (bStartWithHexPrefix)
109 std::string_view sView(text.begin + RTL_CONSTASCII_LENGTH("0X"),
110 text.length - RTL_CONSTASCII_LENGTH("0X"));
111 *value = static_cast< sal_Int32 >(o3tl::toUInt32(sView, 16));
113 else
115 std::string_view sView(text.begin, text.length);
116 *value = o3tl::toInt32(sView);
118 //TODO: check valid lexical representation
119 return true;
122 bool parseValue(xmlreader::Span const & text, sal_Int64 * value) {
123 assert(text.is() && value != nullptr);
124 // For backwards compatibility, support hexadecimal values:
125 *value =
126 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
127 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
128 RTL_CONSTASCII_LENGTH("0X")) == 0 ?
129 static_cast< sal_Int64 >(
130 OString(
131 text.begin + RTL_CONSTASCII_LENGTH("0X"),
132 text.length - RTL_CONSTASCII_LENGTH("0X")).toUInt64(16)) :
133 OString(text.begin, text.length).toInt64();
134 //TODO: check valid lexical representation
135 return true;
138 bool parseValue(xmlreader::Span const & text, double * value) {
139 assert(text.is() && value != nullptr);
140 *value = rtl_math_stringToDouble(
141 text.begin, text.begin + text.length, '.', 0, nullptr, nullptr);
142 //TODO: check valid lexical representation
143 return true;
146 bool parseValue(xmlreader::Span const & text, OUString * value) {
147 assert(text.is() && value != nullptr);
148 *value = text.convertFromUtf8();
149 return true;
152 bool parseValue(
153 xmlreader::Span const & text, css::uno::Sequence< sal_Int8 > * value)
155 assert(text.is() && value != nullptr);
156 if ((text.length & 1) != 0) {
157 return false;
159 std::vector< sal_Int8 > seq;
160 for (sal_Int32 i = 0; i != text.length;) {
161 int n1;
162 int n2;
163 if (!parseHexDigit(text.begin[i++], &n1) ||
164 !parseHexDigit(text.begin[i++], &n2))
166 return false;
168 seq.push_back(static_cast< sal_Int8 >((n1 << 4) | n2));
170 *value = comphelper::containerToSequence(seq);
171 return true;
174 template< typename T > css::uno::Any parseSingleValue(
175 xmlreader::Span const & text)
177 T val;
178 if (!parseValue(text, &val)) {
179 throw css::uno::RuntimeException("invalid value");
181 return css::uno::Any(val);
184 template< typename T > css::uno::Any parseListValue(
185 OString const & separator, xmlreader::Span const & text)
187 std::vector< T > seq;
188 xmlreader::Span sep;
189 if (separator.isEmpty()) {
190 sep = xmlreader::Span(RTL_CONSTASCII_STRINGPARAM(" "));
191 } else {
192 sep = xmlreader::Span(separator.getStr(), separator.getLength());
194 if (text.length != 0) {
195 for (xmlreader::Span t(text);;) {
196 sal_Int32 i = rtl_str_indexOfStr_WithLength(
197 t.begin, t.length, sep.begin, sep.length);
198 T val;
199 if (!parseValue(
200 xmlreader::Span(t.begin, i == -1 ? t.length : i), &val))
202 throw css::uno::RuntimeException("invalid value");
204 seq.push_back(val);
205 if (i < 0) {
206 break;
208 t.begin += i + sep.length;
209 t.length -= i + sep.length;
212 return css::uno::Any(comphelper::containerToSequence(seq));
215 css::uno::Any parseValue(
216 OString const & separator, xmlreader::Span const & text, Type type)
218 switch (type) {
219 case TYPE_ANY:
220 throw css::uno::RuntimeException("invalid value of type any");
221 case TYPE_BOOLEAN:
222 return parseSingleValue< sal_Bool >(text);
223 case TYPE_SHORT:
224 return parseSingleValue< sal_Int16 >(text);
225 case TYPE_INT:
226 return parseSingleValue< sal_Int32 >(text);
227 case TYPE_LONG:
228 return parseSingleValue< sal_Int64 >(text);
229 case TYPE_DOUBLE:
230 return parseSingleValue< double >(text);
231 case TYPE_STRING:
232 return parseSingleValue< OUString >(text);
233 case TYPE_HEXBINARY:
234 return parseSingleValue< css::uno::Sequence< sal_Int8 > >(text);
235 case TYPE_BOOLEAN_LIST:
236 return parseListValue< sal_Bool >(separator, text);
237 case TYPE_SHORT_LIST:
238 return parseListValue< sal_Int16 >(separator, text);
239 case TYPE_INT_LIST:
240 return parseListValue< sal_Int32 >(separator, text);
241 case TYPE_LONG_LIST:
242 return parseListValue< sal_Int64 >(separator, text);
243 case TYPE_DOUBLE_LIST:
244 return parseListValue< double >(separator, text);
245 case TYPE_STRING_LIST:
246 return parseListValue< OUString >(separator, text);
247 case TYPE_HEXBINARY_LIST:
248 return parseListValue< css::uno::Sequence< sal_Int8 > >(
249 separator, text);
250 default:
251 assert(false);
252 throw css::uno::RuntimeException("this cannot happen");
258 ValueParser::ValueParser(int layer): type_(TYPE_ERROR), layer_(layer), state_() {}
260 ValueParser::~ValueParser() {}
262 xmlreader::XmlReader::Text ValueParser::getTextMode() const {
263 if (!node_)
264 return xmlreader::XmlReader::Text::NONE;
266 switch (state_) {
267 case State::Text:
268 if (!items_.empty()) {
269 break;
271 [[fallthrough]];
272 case State::IT:
273 return
274 (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST ||
275 !separator_.isEmpty())
276 ? xmlreader::XmlReader::Text::Raw
277 : xmlreader::XmlReader::Text::Normalized;
278 default:
279 break;
281 return xmlreader::XmlReader::Text::NONE;
284 bool ValueParser::startElement(
285 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name)
287 if (!node_.is()) {
288 return false;
290 switch (state_) {
291 case State::Text:
292 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE && name == "it" &&
293 isListType(type_) && separator_.isEmpty())
295 pad_.clear();
296 // before first <it>, characters are not ignored; assume they
297 // are only whitespace
298 state_ = State::IT;
299 return true;
301 [[fallthrough]];
302 case State::IT:
303 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
304 name == "unicode" &&
305 (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST))
307 sal_Int32 scalar = -1;
308 for (;;) {
309 int attrNsId;
310 xmlreader::Span attrLn;
311 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
312 break;
314 if (attrNsId == ParseManager::NAMESPACE_OOR &&
315 attrLn == "scalar")
317 if (!parseValue(reader.getAttributeValue(true), &scalar)) {
318 scalar = -1;
320 break;
323 if (scalar >= 0 && scalar < 0x20 && scalar != 0x09 &&
324 scalar != 0x0A && scalar != 0x0D)
326 char c = static_cast< char >(scalar);
327 pad_.add(&c, 1);
328 } else if (scalar == 0xFFFE) {
329 pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBE"));
330 } else if (scalar == 0xFFFF) {
331 pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBF"));
332 } else {
333 throw css::uno::RuntimeException(
334 "bad unicode scalar attribute in " + reader.getUrl());
336 state_ = state_ == State::Text ? State::TextUnicode : State::ITUnicode;
337 return true;
339 break;
340 default:
341 break;
343 throw css::uno::RuntimeException(
344 "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
347 bool ValueParser::endElement() {
348 if (!node_.is()) {
349 return false;
351 switch (state_) {
352 case State::Text:
354 css::uno::Any *pValue = nullptr;
356 switch (node_->kind()) {
357 case Node::KIND_PROPERTY:
358 pValue = static_cast< PropertyNode * >(node_.get())->getValuePtr(layer_, layer_ == Data::NO_LAYER);
359 break;
360 case Node::KIND_LOCALIZED_PROPERTY:
362 NodeMap & members = node_->getMembers();
363 auto [i, bInserted] = members.insert(NodeMap::value_type(localizedName_, nullptr));
364 LocalizedValueNode *pLVNode;
365 if (bInserted) {
366 pLVNode = new LocalizedValueNode(layer_);
367 i->second = pLVNode;
368 } else {
369 pLVNode = static_cast< LocalizedValueNode * >(i->second.get());
371 pValue = pLVNode->getValuePtr(layer_, layer_ == Data::NO_LAYER);
373 break;
374 default:
375 assert(false); // this cannot happen
376 return false;
379 if (items_.empty()) {
380 *pValue = parseValue(separator_, pad_.get(), type_);
381 pad_.clear();
382 } else {
383 switch (type_) {
384 case TYPE_BOOLEAN_LIST:
385 *pValue = convertItems< sal_Bool >();
386 break;
387 case TYPE_SHORT_LIST:
388 *pValue = convertItems< sal_Int16 >();
389 break;
390 case TYPE_INT_LIST:
391 *pValue = convertItems< sal_Int32 >();
392 break;
393 case TYPE_LONG_LIST:
394 *pValue = convertItems< sal_Int64 >();
395 break;
396 case TYPE_DOUBLE_LIST:
397 *pValue = convertItems< double >();
398 break;
399 case TYPE_STRING_LIST:
400 *pValue = convertItems< OUString >();
401 break;
402 case TYPE_HEXBINARY_LIST:
403 *pValue = convertItems< css::uno::Sequence< sal_Int8 > >();
404 break;
405 default:
406 assert(false); // this cannot happen
407 break;
409 items_.clear();
411 separator_.clear();
412 node_.clear();
414 break;
415 case State::TextUnicode:
416 state_ = State::Text;
417 break;
418 case State::ITUnicode:
419 state_ = State::IT;
420 break;
421 case State::IT:
422 items_.push_back(
423 parseValue(OString(), pad_.get(), elementType(type_)));
424 pad_.clear();
425 state_ = State::Text;
426 break;
428 return true;
431 void ValueParser::characters(xmlreader::Span const & text) {
432 if (node_.is()) {
433 assert(state_ == State::Text || state_ == State::IT);
434 pad_.add(text.begin, text.length);
438 void ValueParser::start(
439 rtl::Reference< Node > const & node, OUString const & localizedName)
441 assert(node.is() && !node_.is());
442 node_ = node;
443 localizedName_ = localizedName;
444 state_ = State::Text;
448 template< typename T > css::uno::Any ValueParser::convertItems() {
449 css::uno::Sequence< T > seq(items_.size());
450 auto seqRange = asNonConstRange(seq);
451 for (sal_Int32 i = 0; i < seq.getLength(); ++i) {
452 bool ok = (items_[i] >>= seqRange[i]);
453 assert(ok);
454 (void) ok; // avoid warnings
456 return css::uno::Any(seq);
461 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */