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 39810513 : bool isSpace(char c) {
45 39810513 : switch (c) {
46 : case '\x09':
47 : case '\x0A':
48 : case '\x0D':
49 : case ' ':
50 5939383 : return true;
51 : default:
52 33871130 : return false;
53 : }
54 : }
55 :
56 : }
57 :
58 24798 : XmlReader::XmlReader(rtl::OUString const & fileUrl)
59 : SAL_THROW((
60 : css::container::NoSuchElementException, css::uno::RuntimeException)):
61 24798 : fileUrl_(fileUrl)
62 : {
63 : oslFileError e = osl_openFile(
64 24798 : fileUrl_.pData, &fileHandle_, osl_File_OpenFlag_Read);
65 24798 : switch (e)
66 : {
67 : case osl_File_E_None:
68 24798 : 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 24798 : e = osl_getFileSize(fileHandle_, &fileSize_);
80 24798 : if (e == osl_File_E_None) {
81 : e = osl_mapFile(
82 : fileHandle_, &fileAddress_, fileSize_, 0,
83 24798 : osl_File_MapFlag_WillNeed);
84 : }
85 24798 : 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 24798 : "http://www.w3.org/XML/1998/namespace")));
101 : namespaces_.push_back(
102 24798 : NamespaceData(Span(RTL_CONSTASCII_STRINGPARAM("xml")), NAMESPACE_XML));
103 24798 : pos_ = static_cast< char * >(fileAddress_);
104 24798 : end_ = pos_ + fileSize_;
105 24798 : state_ = STATE_CONTENT;
106 24798 : firstAttribute_ = true;
107 24798 : }
108 :
109 49596 : XmlReader::~XmlReader() {
110 24798 : 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 24798 : 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 24798 : }
123 :
124 70338 : int XmlReader::registerNamespaceIri(Span const & iri) {
125 70338 : int id = toNamespaceId(namespaceIris_.size());
126 70338 : namespaceIris_.push_back(iri);
127 140676 : if (iri.equals(
128 : Span(
129 : RTL_CONSTASCII_STRINGPARAM(
130 140676 : "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 22770 : NamespaceData(Span(RTL_CONSTASCII_STRINGPARAM("xsi")), id));
139 : }
140 70338 : return id;
141 : }
142 :
143 14427949 : XmlReader::Result XmlReader::nextItem(Text reportText, Span * data, int * nsId)
144 : {
145 14427949 : switch (state_) {
146 : case STATE_CONTENT:
147 12222339 : switch (reportText) {
148 : case TEXT_NONE:
149 10322320 : return handleSkippedText(data, nsId);
150 : case TEXT_RAW:
151 1342619 : return handleRawText(data);
152 : case TEXT_NORMALIZED:
153 557400 : return handleNormalizedText(data);
154 : }
155 : case STATE_START_TAG:
156 3424 : return handleStartTag(nsId, data);
157 : case STATE_END_TAG:
158 1896595 : return handleEndTag();
159 : case STATE_EMPTY_ELEMENT_TAG:
160 280809 : handleElementEnd();
161 280809 : return RESULT_END;
162 : default: // STATE_DONE
163 24782 : return RESULT_DONE;
164 : }
165 : }
166 :
167 10977441 : bool XmlReader::nextAttribute(int * nsId, Span * localName) {
168 : assert(nsId != 0 && localName != 0);
169 10977441 : if (firstAttribute_) {
170 5213318 : currentAttribute_ = attributes_.begin();
171 5213318 : firstAttribute_ = false;
172 : } else {
173 5764123 : ++currentAttribute_;
174 : }
175 10977441 : if (currentAttribute_ == attributes_.end()) {
176 5213318 : return false;
177 : }
178 5764123 : if (currentAttribute_->nameColon == 0) {
179 118872 : *nsId = NAMESPACE_NONE;
180 : *localName = Span(
181 118872 : currentAttribute_->nameBegin,
182 237744 : currentAttribute_->nameEnd - currentAttribute_->nameBegin);
183 : } else {
184 : *nsId = getNamespaceId(
185 : Span(
186 5645251 : currentAttribute_->nameBegin,
187 11290502 : currentAttribute_->nameColon - currentAttribute_->nameBegin));
188 : *localName = Span(
189 5645251 : currentAttribute_->nameColon + 1,
190 11290502 : currentAttribute_->nameEnd - (currentAttribute_->nameColon + 1));
191 : }
192 5764123 : return true;
193 : }
194 :
195 5750493 : Span XmlReader::getAttributeValue(bool fullyNormalize) {
196 : return handleAttributeValue(
197 11500986 : currentAttribute_->valueBegin, currentAttribute_->valueEnd,
198 17251479 : fullyNormalize);
199 : }
200 :
201 6485404 : int XmlReader::getNamespaceId(Span const & prefix) const {
202 65918793 : for (NamespaceList::const_reverse_iterator i(namespaces_.rbegin());
203 43945862 : i != namespaces_.rend(); ++i)
204 : {
205 21972931 : if (prefix.equals(i->prefix)) {
206 6485404 : return i->nsId;
207 : }
208 : }
209 0 : return NAMESPACE_UNKNOWN;
210 : }
211 :
212 51008 : rtl::OUString XmlReader::getUrl() const {
213 51008 : 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 29996016 : void XmlReader::skipSpace() {
235 65931415 : while (isSpace(peek())) {
236 5939383 : ++pos_;
237 : }
238 29996016 : }
239 :
240 24802 : bool XmlReader::skipComment() {
241 24802 : if (rtl_str_shortenedCompare_WithLength(
242 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"),
243 24802 : RTL_CONSTASCII_LENGTH("--")) !=
244 : 0)
245 : {
246 8 : return false;
247 : }
248 24794 : pos_ += RTL_CONSTASCII_LENGTH("--");
249 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
250 24794 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"));
251 24794 : 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 24794 : pos_ += i + RTL_CONSTASCII_LENGTH("--");
260 24794 : 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 24794 : return true;
269 : }
270 :
271 24782 : void XmlReader::skipProcessingInstruction() {
272 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
273 24782 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("?>"));
274 24782 : 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 24782 : pos_ += i + RTL_CONSTASCII_LENGTH("?>");
281 24782 : }
282 :
283 288 : 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 280 : for (;;) {
287 288 : char c = read();
288 288 : 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 8 : pos_, end_ - pos_, c);
301 8 : 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 8 : pos_ += i + 1;
310 : }
311 8 : break;
312 : case '>':
313 8 : 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 272 : break;
377 : }
378 : }
379 : }
380 :
381 8 : Span XmlReader::scanCdataSection() {
382 8 : if (rtl_str_shortenedCompare_WithLength(
383 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("[CDATA["),
384 8 : RTL_CONSTASCII_LENGTH("[CDATA[")) !=
385 : 0)
386 : {
387 8 : 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 18146898 : bool XmlReader::scanName(char const ** nameColon) {
406 : assert(nameColon != 0 && *nameColon == 0);
407 122094122 : for (char const * begin = pos_;; ++pos_) {
408 122094122 : 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 36293796 : return pos_ != begin;
418 : case ':':
419 5852721 : *nameColon = pos_;
420 5852721 : break;
421 : default:
422 98094503 : break;
423 : }
424 : }
425 : }
426 :
427 78292 : int XmlReader::scanNamespaceIri(char const * begin, char const * end) {
428 : assert(begin != 0 && begin <= end);
429 78292 : Span iri(handleAttributeValue(begin, end, false));
430 252420 : for (NamespaceIris::size_type i = 0; i < namespaceIris_.size(); ++i) {
431 241024 : if (namespaceIris_[i].equals(iri)) {
432 66896 : return toNamespaceId(i);
433 : }
434 : }
435 11396 : return XmlReader::NAMESPACE_UNKNOWN;
436 : }
437 :
438 18946 : char const * XmlReader::handleReference(char const * position, char const * end)
439 : {
440 : assert(position != 0 && *position == '&' && position < end);
441 18946 : ++position;
442 18946 : if (*position == '#') {
443 96 : ++position;
444 96 : sal_Int32 val = 0;
445 : char const * p;
446 96 : if (*position == 'x') {
447 96 : ++position;
448 96 : p = position;
449 384 : for (;; ++position) {
450 480 : char c = *position;
451 480 : if (c >= '0' && c <= '9') {
452 176 : val = 16 * val + (c - '0');
453 304 : } else if (c >= 'A' && c <= 'F') {
454 208 : val = 16 * val + (c - 'A') + 10;
455 96 : } else if (c >= 'a' && c <= 'f') {
456 0 : val = 16 * val + (c - 'a') + 10;
457 : } else {
458 96 : break;
459 : }
460 384 : 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 96 : 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 96 : 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 96 : if (val < 0x80) {
509 0 : buf[0] = static_cast< char >(val);
510 0 : len = 1;
511 96 : } 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 96 : } else if (val < 0x10000) {
516 96 : buf[0] = static_cast< char >((val >> 12) | 0xE0);
517 96 : buf[1] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
518 96 : buf[2] = static_cast< char >((val & 0x3F) | 0x80);
519 96 : 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 96 : pad_.addEphemeral(buf, len);
528 96 : 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 37322 : for (std::size_t i = 0; i < sizeof refs / sizeof refs[0]; ++i) {
548 37322 : if (rtl_str_shortenedCompare_WithLength(
549 : position, end - position, refs[i].inBegin, refs[i].inLength,
550 37322 : refs[i].inLength) ==
551 : 0)
552 : {
553 18850 : position += refs[i].inLength;
554 18850 : pad_.add(refs[i].outBegin, refs[i].outLength);
555 18850 : 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 5828785 : Span XmlReader::handleAttributeValue(
567 : char const * begin, char const * end, bool fullyNormalize)
568 : {
569 5828785 : pad_.clear();
570 5828785 : if (fullyNormalize) {
571 3875114 : while (begin != end && isSpace(*begin)) {
572 0 : ++begin;
573 : }
574 3875114 : while (end != begin && isSpace(end[-1])) {
575 0 : --end;
576 : }
577 1937557 : 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 1937557 : Space space = SPACE_NONE;
582 18120428 : while (p != end) {
583 14245314 : 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 3492 : switch (space) {
604 : case SPACE_NONE:
605 3492 : ++p;
606 3492 : space = SPACE_SPAN;
607 3492 : 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 3492 : 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 14241822 : ++p;
626 14241822 : space = SPACE_NONE;
627 14241822 : break;
628 : }
629 : }
630 1937557 : pad_.add(begin, p - begin);
631 : } else {
632 3891228 : char const * p = begin;
633 49083617 : while (p != end) {
634 41301161 : 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 96 : pad_.add(begin, p - begin);
652 96 : p = handleReference(p, end);
653 96 : begin = p;
654 96 : break;
655 : default:
656 41301065 : ++p;
657 41301065 : break;
658 : }
659 : }
660 3891228 : pad_.add(begin, p - begin);
661 : }
662 5828785 : return pad_.get();
663 : }
664 :
665 6251582 : XmlReader::Result XmlReader::handleStartTag(int * nsId, Span * localName) {
666 : assert(nsId != 0 && localName);
667 6251582 : char const * nameBegin = pos_;
668 6251582 : char const * nameColon = 0;
669 6251582 : 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 6251582 : char const * nameEnd = pos_;
676 6251582 : NamespaceList::size_type inheritedNamespaces = namespaces_.size();
677 6251582 : bool hasDefaultNs = false;
678 6251582 : int defaultNsId = NAMESPACE_NONE;
679 6251582 : attributes_.clear();
680 5924559 : for (;;) {
681 12176141 : char const * p = pos_;
682 12176141 : skipSpace();
683 12176141 : if (peek() == '/' || peek() == '>') {
684 : break;
685 : }
686 5924575 : if (pos_ == p) {
687 : throw css::uno::RuntimeException(
688 : (rtl::OUString(
689 : RTL_CONSTASCII_USTRINGPARAM(
690 : "missing whitespace before attribute in ")) +
691 32 : fileUrl_),
692 48 : css::uno::Reference< css::uno::XInterface >());
693 : }
694 5924559 : char const * attrNameBegin = pos_;
695 5924559 : char const * attrNameColon = 0;
696 5924559 : 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 5924559 : char const * attrNameEnd = pos_;
704 5924559 : skipSpace();
705 5924559 : 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 5924559 : skipSpace();
712 5924559 : char del = read();
713 5924559 : 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 5924559 : char const * valueBegin = pos_;
721 5924559 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, del);
722 5924559 : 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 5924559 : char const * valueEnd = pos_ + i;
731 5924559 : pos_ += i + 1;
732 12090886 : if (attrNameColon == 0 &&
733 : Span(attrNameBegin, attrNameEnd - attrNameBegin).equals(
734 6166327 : RTL_CONSTASCII_STRINGPARAM("xmlns")))
735 : {
736 2012 : hasDefaultNs = true;
737 2012 : defaultNsId = scanNamespaceIri(valueBegin, valueEnd);
738 23452444 : } else if (attrNameColon != 0 &&
739 : Span(attrNameBegin, attrNameColon - attrNameBegin).equals(
740 17529897 : RTL_CONSTASCII_STRINGPARAM("xmlns")))
741 : {
742 : namespaces_.push_back(
743 : NamespaceData(
744 76280 : Span(attrNameColon + 1, attrNameEnd - (attrNameColon + 1)),
745 152560 : scanNamespaceIri(valueBegin, valueEnd)));
746 : } else {
747 : attributes_.push_back(
748 : AttributeData(
749 : attrNameBegin, attrNameEnd, attrNameColon, valueBegin,
750 5846267 : valueEnd));
751 : }
752 : }
753 6251566 : if (!hasDefaultNs && !elements_.empty()) {
754 6226784 : defaultNsId = elements_.top().defaultNamespaceId;
755 : }
756 6251566 : firstAttribute_ = true;
757 6251566 : if (peek() == '/') {
758 280809 : state_ = STATE_EMPTY_ELEMENT_TAG;
759 280809 : ++pos_;
760 : } else {
761 5970757 : state_ = STATE_CONTENT;
762 : }
763 6251566 : 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 6251566 : ++pos_;
770 : elements_.push(
771 : ElementData(
772 : Span(nameBegin, nameEnd - nameBegin), inheritedNamespaces,
773 6251566 : defaultNsId));
774 6251566 : if (nameColon == 0) {
775 6226600 : *nsId = defaultNsId;
776 6226600 : *localName = Span(nameBegin, nameEnd - nameBegin);
777 : } else {
778 24966 : *nsId = getNamespaceId(Span(nameBegin, nameColon - nameBegin));
779 24966 : *localName = Span(nameColon + 1, nameEnd - (nameColon + 1));
780 : }
781 6251566 : return RESULT_BEGIN;
782 : }
783 :
784 5970757 : XmlReader::Result XmlReader::handleEndTag() {
785 5970757 : 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 5970757 : char const * nameBegin = pos_;
793 5970757 : char const * nameColon = 0;
794 11941514 : if (!scanName(&nameColon) ||
795 5970757 : !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 5970757 : handleElementEnd();
803 5970757 : skipSpace();
804 5970757 : 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 5970757 : ++pos_;
811 5970757 : return RESULT_END;
812 : }
813 :
814 6251566 : void XmlReader::handleElementEnd() {
815 : assert(!elements_.empty());
816 6251566 : namespaces_.resize(elements_.top().inheritedNamespaces);
817 6251566 : elements_.pop();
818 6251566 : state_ = elements_.empty() ? STATE_DONE : STATE_CONTENT;
819 6251566 : }
820 :
821 10371364 : XmlReader::Result XmlReader::handleSkippedText(Span * data, int * nsId) {
822 49044 : for (;;) {
823 10371364 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, '<');
824 10371364 : 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 10371364 : pos_ += i + 1;
832 10371364 : switch (peek()) {
833 : case '!':
834 24262 : ++pos_;
835 24262 : if (!skipComment() && !scanCdataSection().is()) {
836 8 : skipDocumentTypeDeclaration();
837 : }
838 24262 : break;
839 : case '/':
840 4074162 : ++pos_;
841 4074162 : return handleEndTag();
842 : case '?':
843 24782 : ++pos_;
844 24782 : skipProcessingInstruction();
845 24782 : break;
846 : default:
847 6248158 : return handleStartTag(nsId, data);
848 : }
849 : }
850 : }
851 :
852 1342619 : XmlReader::Result XmlReader::handleRawText(Span * text) {
853 1342619 : pad_.clear();
854 39327507 : for (char const * begin = pos_;;) {
855 39327507 : 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 18850 : pad_.add(begin, pos_ - begin);
872 18850 : pos_ = handleReference(pos_, end_);
873 18850 : begin = pos_;
874 18850 : break;
875 : case '<':
876 1342619 : pad_.add(begin, pos_ - begin);
877 1342619 : ++pos_;
878 1342619 : 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 1339195 : *text = pad_.get();
893 1339195 : ++pos_;
894 1339195 : state_ = STATE_END_TAG;
895 1339195 : return RESULT_TEXT;
896 : case '?':
897 0 : ++pos_;
898 0 : skipProcessingInstruction();
899 0 : begin = pos_;
900 0 : break;
901 : default:
902 3424 : *text = pad_.get();
903 3424 : state_ = STATE_START_TAG;
904 3424 : return RESULT_TEXT;
905 : }
906 0 : break;
907 : default:
908 37966038 : ++pos_;
909 37966038 : break;
910 : }
911 : }
912 : }
913 :
914 557400 : XmlReader::Result XmlReader::handleNormalizedText(Span * text) {
915 557400 : pad_.clear();
916 557400 : char const * flowBegin = pos_;
917 557400 : 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 557400 : Space space = SPACE_START;
922 1525869 : for (;;) {
923 2083269 : 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 1274 : switch (space) {
934 : case SPACE_START:
935 : case SPACE_BREAK:
936 686 : break;
937 : case SPACE_NONE:
938 : case SPACE_SPAN:
939 588 : space = SPACE_BREAK;
940 588 : break;
941 : }
942 1274 : ++pos_;
943 1274 : break;
944 : case ' ':
945 10584 : switch (space) {
946 : case SPACE_START:
947 : case SPACE_BREAK:
948 4998 : break;
949 : case SPACE_NONE:
950 5586 : space = SPACE_SPAN;
951 5586 : break;
952 : case SPACE_SPAN:
953 0 : space = SPACE_BREAK;
954 0 : break;
955 : }
956 10584 : ++pos_;
957 10584 : 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 557940 : ++pos_;
978 557940 : switch (peek()) {
979 : case '!':
980 540 : ++pos_;
981 540 : if (skipComment()) {
982 540 : 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 540 : break;
1010 : case '/':
1011 557400 : ++pos_;
1012 557400 : pad_.add(flowBegin, flowEnd - flowBegin);
1013 557400 : *text = pad_.get();
1014 557400 : state_ = STATE_END_TAG;
1015 557400 : 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 540 : break;
1028 : default:
1029 1513471 : switch (space) {
1030 : case SPACE_START:
1031 557310 : flowBegin = pos_;
1032 557310 : break;
1033 : case SPACE_NONE:
1034 : case SPACE_SPAN:
1035 955581 : break;
1036 : case SPACE_BREAK:
1037 580 : pad_.add(flowBegin, flowEnd - flowBegin);
1038 580 : pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
1039 580 : flowBegin = pos_;
1040 580 : break;
1041 : }
1042 1513471 : flowEnd = ++pos_;
1043 1513471 : space = SPACE_NONE;
1044 1513471 : break;
1045 : }
1046 : }
1047 : }
1048 :
1049 137234 : int XmlReader::toNamespaceId(NamespaceIris::size_type pos) {
1050 : assert(pos <= INT_MAX);
1051 137234 : return static_cast< int >(pos);
1052 : }
1053 :
1054 : }
1055 :
1056 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|