Line data Source code
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 : #include <climits>
24 : #include <cstddef>
25 :
26 : #include "com/sun/star/container/NoSuchElementException.hpp"
27 : #include "com/sun/star/uno/Reference.hxx"
28 : #include "com/sun/star/uno/RuntimeException.hpp"
29 : #include "com/sun/star/uno/XInterface.hpp"
30 : #include "osl/file.h"
31 : #include "rtl/string.h"
32 : #include "rtl/ustring.h"
33 : #include "rtl/ustring.hxx"
34 : #include "sal/log.hxx"
35 : #include "sal/types.h"
36 : #include "xmlreader/pad.hxx"
37 : #include "xmlreader/span.hxx"
38 : #include "xmlreader/xmlreader.hxx"
39 :
40 : namespace xmlreader {
41 :
42 : namespace {
43 :
44 12443399 : bool isSpace(char c) {
45 12443399 : switch (c) {
46 : case '\x09':
47 : case '\x0A':
48 : case '\x0D':
49 : case ' ':
50 1852177 : return true;
51 : default:
52 10591222 : return false;
53 : }
54 : }
55 :
56 : }
57 :
58 8079 : XmlReader::XmlReader(rtl::OUString const & fileUrl)
59 : SAL_THROW((
60 : css::container::NoSuchElementException, css::uno::RuntimeException)):
61 8079 : fileUrl_(fileUrl)
62 : {
63 : oslFileError e = osl_openFile(
64 8079 : fileUrl_.pData, &fileHandle_, osl_File_OpenFlag_Read);
65 8079 : switch (e)
66 : {
67 : case osl_File_E_None:
68 8079 : break;
69 : case osl_File_E_NOENT:
70 : throw css::container::NoSuchElementException(
71 0 : fileUrl_, css::uno::Reference< css::uno::XInterface >());
72 : default:
73 : throw css::uno::RuntimeException(
74 : (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("cannot open ")) +
75 0 : fileUrl_ + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(": ")) +
76 0 : rtl::OUString::valueOf(static_cast< sal_Int32 >(e))),
77 0 : css::uno::Reference< css::uno::XInterface >());
78 : }
79 8079 : e = osl_getFileSize(fileHandle_, &fileSize_);
80 8079 : if (e == osl_File_E_None) {
81 : e = osl_mapFile(
82 : fileHandle_, &fileAddress_, fileSize_, 0,
83 8079 : osl_File_MapFlag_WillNeed);
84 : }
85 8079 : if (e != osl_File_E_None) {
86 0 : oslFileError e2 = osl_closeFile(fileHandle_);
87 : if (e2 != osl_File_E_None) {
88 : SAL_WARN(
89 : "xmlreader",
90 : "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e2);
91 : }
92 : throw css::uno::RuntimeException(
93 0 : ("cannot mmap " + fileUrl_ + " (" +
94 0 : rtl::OUString::valueOf(static_cast< sal_Int32 >(e)) + ")"),
95 0 : css::uno::Reference< css::uno::XInterface >());
96 : }
97 : namespaceIris_.push_back(
98 : Span(
99 : RTL_CONSTASCII_STRINGPARAM(
100 8079 : "http://www.w3.org/XML/1998/namespace")));
101 : namespaces_.push_back(
102 8079 : NamespaceData(Span(RTL_CONSTASCII_STRINGPARAM("xml")), NAMESPACE_XML));
103 8079 : pos_ = static_cast< char * >(fileAddress_);
104 8079 : end_ = pos_ + fileSize_;
105 8079 : state_ = STATE_CONTENT;
106 8079 : firstAttribute_ = true;
107 8079 : }
108 :
109 16158 : XmlReader::~XmlReader() {
110 8079 : oslFileError e = osl_unmapMappedFile(fileHandle_, fileAddress_, fileSize_);
111 : if (e != osl_File_E_None) {
112 : SAL_WARN(
113 : "xmlreader",
114 : "osl_unmapMappedFile of \"" << fileUrl_ << "\" failed with " << +e);
115 : }
116 8079 : e = osl_closeFile(fileHandle_);
117 : if (e != osl_File_E_None) {
118 : SAL_WARN(
119 : "xmlreader",
120 : "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e);
121 : }
122 8079 : }
123 :
124 22277 : int XmlReader::registerNamespaceIri(Span const & iri) {
125 22277 : int id = toNamespaceId(namespaceIris_.size());
126 22277 : namespaceIris_.push_back(iri);
127 44554 : if (iri.equals(
128 : Span(
129 : RTL_CONSTASCII_STRINGPARAM(
130 44554 : "http://www.w3.org/2001/XMLSchema-instance"))))
131 : {
132 : // Old user layer .xcu files used the xsi namespace prefix without
133 : // declaring a corresponding namespace binding, see issue 77174; reading
134 : // those files during migration would fail without this hack that can be
135 : // removed once migration is no longer relevant (see
136 : // configmgr::Components::parseModificationLayer):
137 : namespaces_.push_back(
138 7099 : NamespaceData(Span(RTL_CONSTASCII_STRINGPARAM("xsi")), id));
139 : }
140 22277 : return id;
141 : }
142 :
143 4508051 : XmlReader::Result XmlReader::nextItem(Text reportText, Span * data, int * nsId)
144 : {
145 4508051 : switch (state_) {
146 : case STATE_CONTENT:
147 3836523 : switch (reportText) {
148 : case TEXT_NONE:
149 3257807 : return handleSkippedText(data, nsId);
150 : case TEXT_RAW:
151 406222 : return handleRawText(data);
152 : case TEXT_NORMALIZED:
153 172494 : return handleNormalizedText(data);
154 : }
155 : case STATE_START_TAG:
156 930 : return handleStartTag(nsId, data);
157 : case STATE_END_TAG:
158 577786 : return handleEndTag();
159 : case STATE_EMPTY_ELEMENT_TAG:
160 84733 : handleElementEnd();
161 84733 : return RESULT_END;
162 : default: // STATE_DONE
163 8079 : return RESULT_DONE;
164 : }
165 : }
166 :
167 3399876 : bool XmlReader::nextAttribute(int * nsId, Span * localName) {
168 : assert(nsId != 0 && localName != 0);
169 3399876 : if (firstAttribute_) {
170 1604069 : currentAttribute_ = attributes_.begin();
171 1604069 : firstAttribute_ = false;
172 : } else {
173 1795807 : ++currentAttribute_;
174 : }
175 3399876 : if (currentAttribute_ == attributes_.end()) {
176 1604069 : return false;
177 : }
178 1795807 : if (currentAttribute_->nameColon == 0) {
179 59752 : *nsId = NAMESPACE_NONE;
180 : *localName = Span(
181 59752 : currentAttribute_->nameBegin,
182 119504 : currentAttribute_->nameEnd - currentAttribute_->nameBegin);
183 : } else {
184 : *nsId = getNamespaceId(
185 : Span(
186 1736055 : currentAttribute_->nameBegin,
187 3472110 : currentAttribute_->nameColon - currentAttribute_->nameBegin));
188 : *localName = Span(
189 1736055 : currentAttribute_->nameColon + 1,
190 3472110 : currentAttribute_->nameEnd - (currentAttribute_->nameColon + 1));
191 : }
192 1795807 : return true;
193 : }
194 :
195 1791560 : Span XmlReader::getAttributeValue(bool fullyNormalize) {
196 : return handleAttributeValue(
197 3583120 : currentAttribute_->valueBegin, currentAttribute_->valueEnd,
198 5374680 : fullyNormalize);
199 : }
200 :
201 1998760 : int XmlReader::getNamespaceId(Span const & prefix) const {
202 21009699 : for (NamespaceList::const_reverse_iterator i(namespaces_.rbegin());
203 14006466 : i != namespaces_.rend(); ++i)
204 : {
205 7003233 : if (prefix.equals(i->prefix)) {
206 1998760 : return i->nsId;
207 : }
208 : }
209 0 : return NAMESPACE_UNKNOWN;
210 : }
211 :
212 26106 : rtl::OUString XmlReader::getUrl() const {
213 26106 : return fileUrl_;
214 : }
215 :
216 0 : void XmlReader::normalizeLineEnds(Span const & text) {
217 0 : char const * p = text.begin;
218 0 : sal_Int32 n = text.length;
219 0 : for (;;) {
220 0 : sal_Int32 i = rtl_str_indexOfChar_WithLength(p, n, '\x0D');
221 0 : if (i < 0) {
222 0 : break;
223 : }
224 0 : pad_.add(p, i);
225 0 : p += i + 1;
226 0 : n -= i + 1;
227 0 : if (n == 0 || *p != '\x0A') {
228 0 : pad_.add(RTL_CONSTASCII_STRINGPARAM("\x0A"));
229 : }
230 : }
231 0 : pad_.add(p, n);
232 0 : }
233 :
234 9382758 : void XmlReader::skipSpace() {
235 20617693 : while (isSpace(peek())) {
236 1852177 : ++pos_;
237 : }
238 9382758 : }
239 :
240 5987 : bool XmlReader::skipComment() {
241 5987 : if (rtl_str_shortenedCompare_WithLength(
242 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"),
243 5987 : RTL_CONSTASCII_LENGTH("--")) !=
244 : 0)
245 : {
246 0 : return false;
247 : }
248 5987 : pos_ += RTL_CONSTASCII_LENGTH("--");
249 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
250 5987 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"));
251 5987 : if (i < 0) {
252 : throw css::uno::RuntimeException(
253 : (rtl::OUString(
254 : RTL_CONSTASCII_USTRINGPARAM(
255 : "premature end (within comment) of ")) +
256 0 : fileUrl_),
257 0 : css::uno::Reference< css::uno::XInterface >());
258 : }
259 5987 : pos_ += i + RTL_CONSTASCII_LENGTH("--");
260 5987 : if (read() != '>') {
261 : throw css::uno::RuntimeException(
262 : (rtl::OUString(
263 : RTL_CONSTASCII_USTRINGPARAM(
264 : "illegal \"--\" within comment in ")) +
265 0 : fileUrl_),
266 0 : css::uno::Reference< css::uno::XInterface >());
267 : }
268 5987 : return true;
269 : }
270 :
271 8079 : void XmlReader::skipProcessingInstruction() {
272 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
273 8079 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("?>"));
274 8079 : if (i < 0) {
275 : throw css::uno::RuntimeException(
276 : (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad '<?' in ")) +
277 0 : fileUrl_),
278 0 : css::uno::Reference< css::uno::XInterface >());
279 : }
280 8079 : pos_ += i + RTL_CONSTASCII_LENGTH("?>");
281 8079 : }
282 :
283 0 : void XmlReader::skipDocumentTypeDeclaration() {
284 : // Neither is it checked that the doctypedecl is at the correct position in
285 : // the document, nor that it is well-formed:
286 0 : for (;;) {
287 0 : char c = read();
288 0 : switch (c) {
289 : case '\0': // i.e., EOF
290 : throw css::uno::RuntimeException(
291 : (rtl::OUString(
292 : RTL_CONSTASCII_USTRINGPARAM(
293 : "premature end (within DTD) of ")) +
294 0 : fileUrl_),
295 0 : css::uno::Reference< css::uno::XInterface >());
296 : case '"':
297 : case '\'':
298 : {
299 : sal_Int32 i = rtl_str_indexOfChar_WithLength(
300 0 : pos_, end_ - pos_, c);
301 0 : if (i < 0) {
302 : throw css::uno::RuntimeException(
303 : (rtl::OUString(
304 : RTL_CONSTASCII_USTRINGPARAM(
305 : "premature end (within DTD) of ")) +
306 0 : fileUrl_),
307 0 : css::uno::Reference< css::uno::XInterface >());
308 : }
309 0 : pos_ += i + 1;
310 : }
311 0 : break;
312 : case '>':
313 0 : return;
314 : case '[':
315 0 : for (;;) {
316 0 : c = read();
317 0 : switch (c) {
318 : case '\0': // i.e., EOF
319 : throw css::uno::RuntimeException(
320 : (rtl::OUString(
321 : RTL_CONSTASCII_USTRINGPARAM(
322 : "premature end (within DTD) of ")) +
323 0 : fileUrl_),
324 0 : css::uno::Reference< css::uno::XInterface >());
325 : case '"':
326 : case '\'':
327 : {
328 : sal_Int32 i = rtl_str_indexOfChar_WithLength(
329 0 : pos_, end_ - pos_, c);
330 0 : if (i < 0) {
331 : throw css::uno::RuntimeException(
332 : (rtl::OUString(
333 : RTL_CONSTASCII_USTRINGPARAM(
334 : "premature end (within DTD) of ")) +
335 0 : fileUrl_),
336 0 : css::uno::Reference< css::uno::XInterface >());
337 : }
338 0 : pos_ += i + 1;
339 : }
340 0 : break;
341 : case '<':
342 0 : switch (read()) {
343 : case '\0': // i.e., EOF
344 : throw css::uno::RuntimeException(
345 : (rtl::OUString(
346 : RTL_CONSTASCII_USTRINGPARAM(
347 : "premature end (within DTD) of ")) +
348 0 : fileUrl_),
349 0 : css::uno::Reference< css::uno::XInterface >());
350 : case '!':
351 0 : skipComment();
352 0 : break;
353 : case '?':
354 0 : skipProcessingInstruction();
355 0 : break;
356 : default:
357 0 : break;
358 : }
359 0 : break;
360 : case ']':
361 0 : skipSpace();
362 0 : if (read() != '>') {
363 : throw css::uno::RuntimeException(
364 : (rtl::OUString(
365 : RTL_CONSTASCII_USTRINGPARAM(
366 : "missing \">\" of DTD in ")) +
367 0 : fileUrl_),
368 0 : css::uno::Reference< css::uno::XInterface >());
369 : }
370 0 : return;
371 : default:
372 0 : break;
373 : }
374 : }
375 : default:
376 0 : break;
377 : }
378 : }
379 : }
380 :
381 0 : Span XmlReader::scanCdataSection() {
382 0 : if (rtl_str_shortenedCompare_WithLength(
383 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("[CDATA["),
384 0 : RTL_CONSTASCII_LENGTH("[CDATA[")) !=
385 : 0)
386 : {
387 0 : return Span();
388 : }
389 0 : pos_ += RTL_CONSTASCII_LENGTH("[CDATA[");
390 0 : char const * begin = pos_;
391 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
392 0 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("]]>"));
393 0 : if (i < 0) {
394 : throw css::uno::RuntimeException(
395 : (rtl::OUString(
396 : RTL_CONSTASCII_USTRINGPARAM(
397 : "premature end (within CDATA section) of ")) +
398 0 : fileUrl_),
399 0 : css::uno::Reference< css::uno::XInterface >());
400 : }
401 0 : pos_ += i + RTL_CONSTASCII_LENGTH("]]>");
402 0 : return Span(begin, i);
403 : }
404 :
405 5685268 : bool XmlReader::scanName(char const ** nameColon) {
406 : assert(nameColon != 0 && *nameColon == 0);
407 38332341 : for (char const * begin = pos_;; ++pos_) {
408 38332341 : switch (peek()) {
409 : case '\0': // i.e., EOF
410 : case '\x09':
411 : case '\x0A':
412 : case '\x0D':
413 : case ' ':
414 : case '/':
415 : case '=':
416 : case '>':
417 11370536 : return pos_ != begin;
418 : case ':':
419 1801978 : *nameColon = pos_;
420 1801978 : break;
421 : default:
422 30845095 : break;
423 : }
424 : }
425 : }
426 :
427 24573 : int XmlReader::scanNamespaceIri(char const * begin, char const * end) {
428 : assert(begin != 0 && begin <= end);
429 24573 : Span iri(handleAttributeValue(begin, end, false));
430 78274 : for (NamespaceIris::size_type i = 0; i < namespaceIris_.size(); ++i) {
431 75088 : if (namespaceIris_[i].equals(iri)) {
432 21387 : return toNamespaceId(i);
433 : }
434 : }
435 3186 : return XmlReader::NAMESPACE_UNKNOWN;
436 : }
437 :
438 5827 : char const * XmlReader::handleReference(char const * position, char const * end)
439 : {
440 : assert(position != 0 && *position == '&' && position < end);
441 5827 : ++position;
442 5827 : if (*position == '#') {
443 0 : ++position;
444 0 : sal_Int32 val = 0;
445 : char const * p;
446 0 : if (*position == 'x') {
447 0 : ++position;
448 0 : p = position;
449 0 : for (;; ++position) {
450 0 : char c = *position;
451 0 : if (c >= '0' && c <= '9') {
452 0 : val = 16 * val + (c - '0');
453 0 : } else if (c >= 'A' && c <= 'F') {
454 0 : val = 16 * val + (c - 'A') + 10;
455 0 : } else if (c >= 'a' && c <= 'f') {
456 0 : val = 16 * val + (c - 'a') + 10;
457 : } else {
458 0 : break;
459 : }
460 0 : if (val > 0x10FFFF) { // avoid overflow
461 : throw css::uno::RuntimeException(
462 : (rtl::OUString(
463 : RTL_CONSTASCII_USTRINGPARAM(
464 : "'&#x...' too large in ")) +
465 0 : fileUrl_),
466 0 : css::uno::Reference< css::uno::XInterface >());
467 : }
468 : }
469 : } else {
470 0 : p = position;
471 0 : for (;; ++position) {
472 0 : char c = *position;
473 0 : if (c >= '0' && c <= '9') {
474 0 : val = 10 * val + (c - '0');
475 : } else {
476 0 : break;
477 : }
478 0 : if (val > 0x10FFFF) { // avoid overflow
479 : throw css::uno::RuntimeException(
480 : (rtl::OUString(
481 : RTL_CONSTASCII_USTRINGPARAM(
482 : "'&#...' too large in ")) +
483 0 : fileUrl_),
484 0 : css::uno::Reference< css::uno::XInterface >());
485 : }
486 : }
487 : }
488 0 : if (position == p || *position++ != ';') {
489 : throw css::uno::RuntimeException(
490 : (rtl::OUString(
491 : RTL_CONSTASCII_USTRINGPARAM("'&#...' missing ';' in ")) +
492 0 : fileUrl_),
493 0 : css::uno::Reference< css::uno::XInterface >());
494 : }
495 : assert(val >= 0 && val <= 0x10FFFF);
496 0 : if ((val < 0x20 && val != 0x9 && val != 0xA && val != 0xD) ||
497 : (val >= 0xD800 && val <= 0xDFFF) || val == 0xFFFE || val == 0xFFFF)
498 : {
499 : throw css::uno::RuntimeException(
500 : (rtl::OUString(
501 : RTL_CONSTASCII_USTRINGPARAM(
502 : "character reference denoting invalid character in ")) +
503 0 : fileUrl_),
504 0 : css::uno::Reference< css::uno::XInterface >());
505 : }
506 : char buf[4];
507 : sal_Int32 len;
508 0 : if (val < 0x80) {
509 0 : buf[0] = static_cast< char >(val);
510 0 : len = 1;
511 0 : } else if (val < 0x800) {
512 0 : buf[0] = static_cast< char >((val >> 6) | 0xC0);
513 0 : buf[1] = static_cast< char >((val & 0x3F) | 0x80);
514 0 : len = 2;
515 0 : } else if (val < 0x10000) {
516 0 : buf[0] = static_cast< char >((val >> 12) | 0xE0);
517 0 : buf[1] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
518 0 : buf[2] = static_cast< char >((val & 0x3F) | 0x80);
519 0 : len = 3;
520 : } else {
521 0 : buf[0] = static_cast< char >((val >> 18) | 0xF0);
522 0 : buf[1] = static_cast< char >(((val >> 12) & 0x3F) | 0x80);
523 0 : buf[2] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
524 0 : buf[3] = static_cast< char >((val & 0x3F) | 0x80);
525 0 : len = 4;
526 : }
527 0 : pad_.addEphemeral(buf, len);
528 0 : return position;
529 : } else {
530 : struct EntityRef {
531 : char const * inBegin;
532 : sal_Int32 inLength;
533 : char const * outBegin;
534 : sal_Int32 outLength;
535 : };
536 : static EntityRef const refs[] = {
537 : { RTL_CONSTASCII_STRINGPARAM("amp;"),
538 : RTL_CONSTASCII_STRINGPARAM("&") },
539 : { RTL_CONSTASCII_STRINGPARAM("lt;"),
540 : RTL_CONSTASCII_STRINGPARAM("<") },
541 : { RTL_CONSTASCII_STRINGPARAM("gt;"),
542 : RTL_CONSTASCII_STRINGPARAM(">") },
543 : { RTL_CONSTASCII_STRINGPARAM("apos;"),
544 : RTL_CONSTASCII_STRINGPARAM("'") },
545 : { RTL_CONSTASCII_STRINGPARAM("quot;"),
546 : RTL_CONSTASCII_STRINGPARAM("\"") } };
547 11655 : for (std::size_t i = 0; i < sizeof refs / sizeof refs[0]; ++i) {
548 11655 : if (rtl_str_shortenedCompare_WithLength(
549 : position, end - position, refs[i].inBegin, refs[i].inLength,
550 11655 : refs[i].inLength) ==
551 : 0)
552 : {
553 5827 : position += refs[i].inLength;
554 5827 : pad_.add(refs[i].outBegin, refs[i].outLength);
555 5827 : return position;
556 : }
557 : }
558 : throw css::uno::RuntimeException(
559 : (rtl::OUString(
560 : RTL_CONSTASCII_USTRINGPARAM("unknown entity reference in ")) +
561 0 : fileUrl_),
562 0 : css::uno::Reference< css::uno::XInterface >());
563 : }
564 : }
565 :
566 1816133 : Span XmlReader::handleAttributeValue(
567 : char const * begin, char const * end, bool fullyNormalize)
568 : {
569 1816133 : pad_.clear();
570 1816133 : if (fullyNormalize) {
571 1208464 : while (begin != end && isSpace(*begin)) {
572 0 : ++begin;
573 : }
574 1208464 : while (end != begin && isSpace(end[-1])) {
575 0 : --end;
576 : }
577 604232 : char const * p = begin;
578 : enum Space { SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
579 : // a single true space character can go into the current span,
580 : // everything else breaks the span
581 604232 : Space space = SPACE_NONE;
582 5634076 : while (p != end) {
583 4425612 : switch (*p) {
584 : case '\x09':
585 : case '\x0A':
586 : case '\x0D':
587 0 : switch (space) {
588 : case SPACE_NONE:
589 0 : pad_.add(begin, p - begin);
590 0 : pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
591 0 : space = SPACE_BREAK;
592 0 : break;
593 : case SPACE_SPAN:
594 0 : pad_.add(begin, p - begin);
595 0 : space = SPACE_BREAK;
596 0 : break;
597 : case SPACE_BREAK:
598 0 : break;
599 : }
600 0 : begin = ++p;
601 0 : break;
602 : case ' ':
603 834 : switch (space) {
604 : case SPACE_NONE:
605 834 : ++p;
606 834 : space = SPACE_SPAN;
607 834 : break;
608 : case SPACE_SPAN:
609 0 : pad_.add(begin, p - begin);
610 0 : begin = ++p;
611 0 : space = SPACE_BREAK;
612 0 : break;
613 : case SPACE_BREAK:
614 0 : begin = ++p;
615 0 : break;
616 : }
617 834 : break;
618 : case '&':
619 0 : pad_.add(begin, p - begin);
620 0 : p = handleReference(p, end);
621 0 : begin = p;
622 0 : space = SPACE_NONE;
623 0 : break;
624 : default:
625 4424778 : ++p;
626 4424778 : space = SPACE_NONE;
627 4424778 : break;
628 : }
629 : }
630 604232 : pad_.add(begin, p - begin);
631 : } else {
632 1211901 : char const * p = begin;
633 15816261 : while (p != end) {
634 13392459 : switch (*p) {
635 : case '\x09':
636 : case '\x0A':
637 0 : pad_.add(begin, p - begin);
638 0 : begin = ++p;
639 0 : pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
640 0 : break;
641 : case '\x0D':
642 0 : pad_.add(begin, p - begin);
643 0 : ++p;
644 0 : if (peek() == '\x0A') {
645 0 : ++p;
646 : }
647 0 : begin = p;
648 0 : pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
649 0 : break;
650 : case '&':
651 0 : pad_.add(begin, p - begin);
652 0 : p = handleReference(p, end);
653 0 : begin = p;
654 0 : break;
655 : default:
656 13392459 : ++p;
657 13392459 : break;
658 : }
659 : }
660 1211901 : pad_.add(begin, p - begin);
661 : }
662 1816133 : return pad_.get();
663 : }
664 :
665 1960628 : XmlReader::Result XmlReader::handleStartTag(int * nsId, Span * localName) {
666 : assert(nsId != 0 && localName);
667 1960628 : char const * nameBegin = pos_;
668 1960628 : char const * nameColon = 0;
669 1960628 : if (!scanName(&nameColon)) {
670 : throw css::uno::RuntimeException(
671 : (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad tag name in ")) +
672 0 : fileUrl_),
673 0 : css::uno::Reference< css::uno::XInterface >());
674 : }
675 1960628 : char const * nameEnd = pos_;
676 1960628 : NamespaceList::size_type inheritedNamespaces = namespaces_.size();
677 1960628 : bool hasDefaultNs = false;
678 1960628 : int defaultNsId = NAMESPACE_NONE;
679 1960628 : attributes_.clear();
680 1848745 : for (;;) {
681 3809373 : char const * p = pos_;
682 3809373 : skipSpace();
683 3809373 : if (peek() == '/' || peek() == '>') {
684 : break;
685 : }
686 1848745 : if (pos_ == p) {
687 : throw css::uno::RuntimeException(
688 : (rtl::OUString(
689 : RTL_CONSTASCII_USTRINGPARAM(
690 : "missing whitespace before attribute in ")) +
691 0 : fileUrl_),
692 0 : css::uno::Reference< css::uno::XInterface >());
693 : }
694 1848745 : char const * attrNameBegin = pos_;
695 1848745 : char const * attrNameColon = 0;
696 1848745 : if (!scanName(&attrNameColon)) {
697 : throw css::uno::RuntimeException(
698 : (rtl::OUString(
699 : RTL_CONSTASCII_USTRINGPARAM("bad attribute name in ")) +
700 0 : fileUrl_),
701 0 : css::uno::Reference< css::uno::XInterface >());
702 : }
703 1848745 : char const * attrNameEnd = pos_;
704 1848745 : skipSpace();
705 1848745 : if (read() != '=') {
706 : throw css::uno::RuntimeException(
707 : (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("missing '=' in ")) +
708 0 : fileUrl_),
709 0 : css::uno::Reference< css::uno::XInterface >());
710 : }
711 1848745 : skipSpace();
712 1848745 : char del = read();
713 1848745 : if (del != '\'' && del != '"') {
714 : throw css::uno::RuntimeException(
715 : (rtl::OUString(
716 : RTL_CONSTASCII_USTRINGPARAM("bad attribute value in ")) +
717 0 : fileUrl_),
718 0 : css::uno::Reference< css::uno::XInterface >());
719 : }
720 1848745 : char const * valueBegin = pos_;
721 1848745 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, del);
722 1848745 : if (i < 0) {
723 : throw css::uno::RuntimeException(
724 : (rtl::OUString(
725 : RTL_CONSTASCII_USTRINGPARAM(
726 : "unterminated attribute value in ")) +
727 0 : fileUrl_),
728 0 : css::uno::Reference< css::uno::XInterface >());
729 : }
730 1848745 : char const * valueEnd = pos_ + i;
731 1848745 : pos_ += i + 1;
732 3818954 : if (attrNameColon == 0 &&
733 : Span(attrNameBegin, attrNameEnd - attrNameBegin).equals(
734 1970209 : RTL_CONSTASCII_STRINGPARAM("xmlns")))
735 : {
736 980 : hasDefaultNs = true;
737 980 : defaultNsId = scanNamespaceIri(valueBegin, valueEnd);
738 7271556 : } else if (attrNameColon != 0 &&
739 : Span(attrNameBegin, attrNameColon - attrNameBegin).equals(
740 5423791 : RTL_CONSTASCII_STRINGPARAM("xmlns")))
741 : {
742 : namespaces_.push_back(
743 : NamespaceData(
744 23593 : Span(attrNameColon + 1, attrNameEnd - (attrNameColon + 1)),
745 47186 : scanNamespaceIri(valueBegin, valueEnd)));
746 : } else {
747 : attributes_.push_back(
748 : AttributeData(
749 : attrNameBegin, attrNameEnd, attrNameColon, valueBegin,
750 1824172 : valueEnd));
751 : }
752 : }
753 1960628 : if (!hasDefaultNs && !elements_.empty()) {
754 1952549 : defaultNsId = elements_.top().defaultNamespaceId;
755 : }
756 1960628 : firstAttribute_ = true;
757 1960628 : if (peek() == '/') {
758 84733 : state_ = STATE_EMPTY_ELEMENT_TAG;
759 84733 : ++pos_;
760 : } else {
761 1875895 : state_ = STATE_CONTENT;
762 : }
763 1960628 : if (peek() != '>') {
764 : throw css::uno::RuntimeException(
765 : (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("missing '>' in ")) +
766 0 : fileUrl_),
767 0 : css::uno::Reference< css::uno::XInterface >());
768 : }
769 1960628 : ++pos_;
770 : elements_.push(
771 : ElementData(
772 : Span(nameBegin, nameEnd - nameBegin), inheritedNamespaces,
773 1960628 : defaultNsId));
774 1960628 : if (nameColon == 0) {
775 1953511 : *nsId = defaultNsId;
776 1953511 : *localName = Span(nameBegin, nameEnd - nameBegin);
777 : } else {
778 7117 : *nsId = getNamespaceId(Span(nameBegin, nameColon - nameBegin));
779 7117 : *localName = Span(nameColon + 1, nameEnd - (nameColon + 1));
780 : }
781 1960628 : return RESULT_BEGIN;
782 : }
783 :
784 1875895 : XmlReader::Result XmlReader::handleEndTag() {
785 1875895 : if (elements_.empty()) {
786 : throw css::uno::RuntimeException(
787 : (rtl::OUString(
788 : RTL_CONSTASCII_USTRINGPARAM("spurious end tag in ")) +
789 0 : fileUrl_),
790 0 : css::uno::Reference< css::uno::XInterface >());
791 : }
792 1875895 : char const * nameBegin = pos_;
793 1875895 : char const * nameColon = 0;
794 3751790 : if (!scanName(&nameColon) ||
795 1875895 : !elements_.top().name.equals(nameBegin, pos_ - nameBegin))
796 : {
797 : throw css::uno::RuntimeException(
798 : (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("tag mismatch in ")) +
799 0 : fileUrl_),
800 0 : css::uno::Reference< css::uno::XInterface >());
801 : }
802 1875895 : handleElementEnd();
803 1875895 : skipSpace();
804 1875895 : if (peek() != '>') {
805 : throw css::uno::RuntimeException(
806 : (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("missing '>' in ")) +
807 0 : fileUrl_),
808 0 : css::uno::Reference< css::uno::XInterface >());
809 : }
810 1875895 : ++pos_;
811 1875895 : return RESULT_END;
812 : }
813 :
814 1960628 : void XmlReader::handleElementEnd() {
815 : assert(!elements_.empty());
816 1960628 : namespaces_.resize(elements_.top().inheritedNamespaces);
817 1960628 : elements_.pop();
818 1960628 : state_ = elements_.empty() ? STATE_DONE : STATE_CONTENT;
819 1960628 : }
820 :
821 3271687 : XmlReader::Result XmlReader::handleSkippedText(Span * data, int * nsId) {
822 13880 : for (;;) {
823 3271687 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, '<');
824 3271687 : if (i < 0) {
825 : throw css::uno::RuntimeException(
826 : (rtl::OUString(
827 : RTL_CONSTASCII_USTRINGPARAM("premature end of ")) +
828 0 : fileUrl_),
829 0 : css::uno::Reference< css::uno::XInterface >());
830 : }
831 3271687 : pos_ += i + 1;
832 3271687 : switch (peek()) {
833 : case '!':
834 5801 : ++pos_;
835 5801 : if (!skipComment() && !scanCdataSection().is()) {
836 0 : skipDocumentTypeDeclaration();
837 : }
838 5801 : break;
839 : case '/':
840 1298109 : ++pos_;
841 1298109 : return handleEndTag();
842 : case '?':
843 8079 : ++pos_;
844 8079 : skipProcessingInstruction();
845 8079 : break;
846 : default:
847 1959698 : return handleStartTag(nsId, data);
848 : }
849 : }
850 : }
851 :
852 406222 : XmlReader::Result XmlReader::handleRawText(Span * text) {
853 406222 : pad_.clear();
854 11931642 : for (char const * begin = pos_;;) {
855 11931642 : switch (peek()) {
856 : case '\0': // i.e., EOF
857 : throw css::uno::RuntimeException(
858 : (rtl::OUString(
859 : RTL_CONSTASCII_USTRINGPARAM("premature end of ")) +
860 0 : fileUrl_),
861 0 : css::uno::Reference< css::uno::XInterface >());
862 : case '\x0D':
863 0 : pad_.add(begin, pos_ - begin);
864 0 : ++pos_;
865 0 : if (peek() != '\x0A') {
866 0 : pad_.add(RTL_CONSTASCII_STRINGPARAM("\x0A"));
867 : }
868 0 : begin = pos_;
869 0 : break;
870 : case '&':
871 5827 : pad_.add(begin, pos_ - begin);
872 5827 : pos_ = handleReference(pos_, end_);
873 5827 : begin = pos_;
874 5827 : break;
875 : case '<':
876 406222 : pad_.add(begin, pos_ - begin);
877 406222 : ++pos_;
878 406222 : switch (peek()) {
879 : case '!':
880 0 : ++pos_;
881 0 : if (!skipComment()) {
882 0 : Span cdata(scanCdataSection());
883 0 : if (cdata.is()) {
884 0 : normalizeLineEnds(cdata);
885 : } else {
886 0 : skipDocumentTypeDeclaration();
887 : }
888 : }
889 0 : begin = pos_;
890 0 : break;
891 : case '/':
892 405292 : *text = pad_.get();
893 405292 : ++pos_;
894 405292 : state_ = STATE_END_TAG;
895 405292 : return RESULT_TEXT;
896 : case '?':
897 0 : ++pos_;
898 0 : skipProcessingInstruction();
899 0 : begin = pos_;
900 0 : break;
901 : default:
902 930 : *text = pad_.get();
903 930 : state_ = STATE_START_TAG;
904 930 : return RESULT_TEXT;
905 : }
906 0 : break;
907 : default:
908 11519593 : ++pos_;
909 11519593 : break;
910 : }
911 : }
912 : }
913 :
914 172494 : XmlReader::Result XmlReader::handleNormalizedText(Span * text) {
915 172494 : pad_.clear();
916 172494 : char const * flowBegin = pos_;
917 172494 : char const * flowEnd = pos_;
918 : enum Space { SPACE_START, SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
919 : // a single true space character can go into the current flow,
920 : // everything else breaks the flow
921 172494 : Space space = SPACE_START;
922 469412 : for (;;) {
923 641906 : switch (peek()) {
924 : case '\0': // i.e., EOF
925 : throw css::uno::RuntimeException(
926 : (rtl::OUString(
927 : RTL_CONSTASCII_USTRINGPARAM("premature end of ")) +
928 0 : fileUrl_),
929 0 : css::uno::Reference< css::uno::XInterface >());
930 : case '\x09':
931 : case '\x0A':
932 : case '\x0D':
933 403 : switch (space) {
934 : case SPACE_START:
935 : case SPACE_BREAK:
936 217 : break;
937 : case SPACE_NONE:
938 : case SPACE_SPAN:
939 186 : space = SPACE_BREAK;
940 186 : break;
941 : }
942 403 : ++pos_;
943 403 : break;
944 : case ' ':
945 3348 : switch (space) {
946 : case SPACE_START:
947 : case SPACE_BREAK:
948 1581 : break;
949 : case SPACE_NONE:
950 1767 : space = SPACE_SPAN;
951 1767 : break;
952 : case SPACE_SPAN:
953 0 : space = SPACE_BREAK;
954 0 : break;
955 : }
956 3348 : ++pos_;
957 3348 : break;
958 : case '&':
959 0 : switch (space) {
960 : case SPACE_START:
961 0 : break;
962 : case SPACE_NONE:
963 : case SPACE_SPAN:
964 0 : pad_.add(flowBegin, pos_ - flowBegin);
965 0 : break;
966 : case SPACE_BREAK:
967 0 : pad_.add(flowBegin, flowEnd - flowBegin);
968 0 : pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
969 0 : break;
970 : }
971 0 : pos_ = handleReference(pos_, end_);
972 0 : flowBegin = pos_;
973 0 : flowEnd = pos_;
974 0 : space = SPACE_NONE;
975 0 : break;
976 : case '<':
977 172680 : ++pos_;
978 172680 : switch (peek()) {
979 : case '!':
980 186 : ++pos_;
981 186 : if (skipComment()) {
982 186 : space = SPACE_BREAK;
983 : } else {
984 0 : Span cdata(scanCdataSection());
985 0 : if (cdata.is()) {
986 : // CDATA is not normalized (similar to character
987 : // references; it keeps the code simple), but it might
988 : // arguably be better to normalize it:
989 0 : switch (space) {
990 : case SPACE_START:
991 0 : break;
992 : case SPACE_NONE:
993 : case SPACE_SPAN:
994 0 : pad_.add(flowBegin, pos_ - flowBegin);
995 0 : break;
996 : case SPACE_BREAK:
997 0 : pad_.add(flowBegin, flowEnd - flowBegin);
998 0 : pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
999 0 : break;
1000 : }
1001 0 : normalizeLineEnds(cdata);
1002 0 : flowBegin = pos_;
1003 0 : flowEnd = pos_;
1004 0 : space = SPACE_NONE;
1005 : } else {
1006 0 : skipDocumentTypeDeclaration();
1007 : }
1008 : }
1009 186 : break;
1010 : case '/':
1011 172494 : ++pos_;
1012 172494 : pad_.add(flowBegin, flowEnd - flowBegin);
1013 172494 : *text = pad_.get();
1014 172494 : state_ = STATE_END_TAG;
1015 172494 : return RESULT_TEXT;
1016 : case '?':
1017 0 : ++pos_;
1018 0 : skipProcessingInstruction();
1019 0 : space = SPACE_BREAK;
1020 0 : break;
1021 : default:
1022 0 : pad_.add(flowBegin, flowEnd - flowBegin);
1023 0 : *text = pad_.get();
1024 0 : state_ = STATE_START_TAG;
1025 0 : return RESULT_TEXT;
1026 : }
1027 186 : break;
1028 : default:
1029 465475 : switch (space) {
1030 : case SPACE_START:
1031 172463 : flowBegin = pos_;
1032 172463 : break;
1033 : case SPACE_NONE:
1034 : case SPACE_SPAN:
1035 292826 : break;
1036 : case SPACE_BREAK:
1037 186 : pad_.add(flowBegin, flowEnd - flowBegin);
1038 186 : pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
1039 186 : flowBegin = pos_;
1040 186 : break;
1041 : }
1042 465475 : flowEnd = ++pos_;
1043 465475 : space = SPACE_NONE;
1044 465475 : break;
1045 : }
1046 : }
1047 : }
1048 :
1049 43664 : int XmlReader::toNamespaceId(NamespaceIris::size_type pos) {
1050 : assert(pos <= INT_MAX);
1051 43664 : return static_cast< int >(pos);
1052 : }
1053 :
1054 : }
1055 :
1056 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|